JPA - do not expand reference to other class - java

Within a Spring Boot application, I have a class A that has a reference to another class B. The reference in class A is annotated with #ManyToOne.
When I get the records from class A, each reference to class B is expanded to show the values in B.
If I add an #JsonIdentityInfo annotation, then listing class A only expands the class B reference the first time it is retrived. Subsequent class A records with the same class B ref simply have the id.
What annotation do I need not to expand the class B ref so that each class A record simply shows the class B id.
Updated with code
Climate.java
#Entity
#SequenceGenerator(name = "climate_gen", sequenceName = "climate_gen", initialValue = 100)
public class Climate {
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "climate_gen")
private long id;
private float temperature;
private float humidity;
#JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime date;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "location_id", nullable = false)
private Location location;
Location.java
#Entity
#SequenceGenerator(name = "location_gen", sequenceName = "location_gen", initialValue = 100)
public class Location {
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "location_gen")
private long id;
private String name;
private String gps;
private String timezone;
So setting fetchType to EAGER gives an expanded location record for each climate.
Setting fetchtype to LAZY gives an error
$ curl -k -w "\n" -H "Authorization: Bearer $TOKEN"
https://mint191:8453/api/v1/climates
{"timestamp":"2019-12-23T16:33:36.047+0000","status":500,"error":"Internal
Server Error","message":"Type definition error: [simple type, class
org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor]; nested
exception is
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No
serializer found for class
org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor and no
properties discovered to create BeanSerializer (to avoid exception,
disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference
chain:
java.util.ArrayList[0]->com.norricorp.bikes.model.Climate[\"location\"]->com.norricorp.bikes.model.Location$HibernateProxy$atNV2KHn[\"hibernateLazyInitializer\"])","path":"/api/v1/climates"}
Regards,

Here are some ideas:
use a custom Jackson serializer - example
other ways to handle bidirectional relationships in Jackson
use a custom query with JPQL Constructor Expression - more on SO and in this article: The best way to map a projection query to a DTO (Data Transfer Object) with JPA and Hibernate
instead of A referencing B directly, a use BId entity mapped to the same table as B: How to map multiple JPA entities to one database table with Hibernate. Here is a very simple project where I'm using this approach
use Spring Data JPA Projections or DTO Projections
UPDATE (25.12.2019)
When FetchType.LAZY is used Hibernate returns a proxy.
Here are some ways to work around the error you're getting:
Jackson – JsonMappingException (No serializer found for class)
No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor
Avoid Jackson serialization on non fetched lazy objects

If I got you correctly then I guess you are searching for the LazyLoading annotation inside the OneToMany Annotation
#OneToMany(fetch = FetchType.LAZY)
eg.: https://www.baeldung.com/hibernate-lazy-eager-loading
Maybe you can share some example code?

Related

Have ModelMapper ignore mapping fields that are JPA Lazy and haven't been initialized

If you've used JPA you've probably run into the classic
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: class, could not initialize proxy - no Session
I'm using ModelMapper to map my hibernate objects into DTOs and, of course, I'm running into this issue. I originally, up to this point, had everything set to EAGER but I have quickly learned that that is NOT the solution as everything is now EXTREMELY slow to load. So, now I'm back to setting things to LAZY.
So my question is: is there a way to customize the ModelMapper so that it can check if the field is lazy before mapping / and if it's lazy, don't map it?
I've been doing some research into custom mappings here http://modelmapper.org/user-manual/property-mapping/
but i can't seem to find a way to throw in some logic.
I found a solution here but this is for Dozer
EDIT:
I am using Spring Boot JPA. Here is some of my code:
#Data
#Entity
#Table(name = "vm_request")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class VirtualMachineRequest {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// removed other properties
#OneToMany(mappedBy = "request", cascade = CascadeType.ALL, orphanRemoval = true)
#ToString.Exclude
#EqualsAndHashCode.Exclude
private Set<VirtualMachine> virtualMachines;
}
I am mapping this object to a DTO:
#Data
public class VirtualMachineRequestDTO {
private Long id;
// removed other properties
private Set<VirtualMachineDTO> virtualMachines;
}
using ModelMapper as such:
public VirtualMachineRequestDTO convertToDTO(VirtualMachineRequest request) {
VirtualMachineRequestDTO requestDTO = mapper.map(request, VirtualMachineRequestDTO.class);
return requestDTO;
}
My problem is that since the virtualMachines set is Lazy by default (and this is intended for certain situations), ModelMapper encounters the exception LazyInitializationException
I'm trying to find a way to have ModelMapper ignore the field if it's lazy and hasn't been initialized.

Spring relationship get child id without load sub class

How to get DEPARTMENT_ID of the base class without loading sub-class in Spring boot JPA
For example we have a base model:
#Entity
#Table(name = "TM_POSITIONS")
public class PositionEntity {
#Id
#SequenceGenerator(name = "PositionsSequence", sequenceName = "TM_POSITIONS_SEQ", allocationSize = 1, initialValue = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "PositionsSequence")
private long id;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.REFRESH)
#JoinColumn(name = "DEPARTMENT_ID")
private DepartmentModel department;
...
So, how to just get DEPARTMENT_ID without bundle it and load another object. In some cases, I need to get related Model and in some cases, I need to get just DEPARTMENT_ID.
You can create a responseDTO class and a mapper class for that.
#ApiModel
public class positionEntityResponseDTO{
private long id;
private long departmentId;
//getter & setter
}
And the mapper class look like
#Service
public class ResponseMapper{
public positionEntityResponseDTO map (PositionEntity entity){
positionEntityResponseDTO response = new positionEntityResponseDTO();
response.setId(entity.getId);
response.setDepartmentId(entity.getDepartment.getId();
return response;
}
}
now suppose somewhere you write
#Autowired
private ResponseMapper mapper;
positionEntityResponseDTO response= mapper.map(repository.save(entity));
now you can pass only DEPARTMENT_ID by response object. hope this works for you.
First of all you need to set the fetch = FetchType.LAZY. In the EAGER way the sub-class will always be loaded.
If you set the fetch type lazy, you can reach the sub-class's id (primary-key) field without extra db queries.
So if you write position.getDepartment().getId() you will get the id and it won't cost anything.
Keep in mind, that other method calls on the sub-class will be load it from the database, e.g.: toString, equals, or getName() /if there is such a method/.
In the case you need the sub-class for some other functionalities, you should write another query in your reposiroty which will fetch the sub-class too.
#Query("SELECT pos FROM Position LEFT JOIN FETCH pos.department")
With JOIN FETCH Spring Data JPA will generate a single query which will join the two table, so you can avoid the N+1 problem.

Hibernate/JPA persist OneToMany objects with generated UUID's

I have been using Hibernate a lot but using Hibernate/JPA with UUID got me stumped a bit. I am using hibernate 5.2.12.Final.
I have an object called TimePeriod with this mapping:
#Entity(name = "time_period")
public class TimePeriod extends AbstractDomainObject {
#OneToMany(mappedBy = "timePeriod", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<TimePeriodBlock> timePeriodBlocks = new ArrayList<>();
...
public void addTimePeriodBlock(TimePeriodBlock timePeriodBlock) {
timePeriodBlock.setTimePeriod(this);
this.timePeriodBlocks.add(timePeriodBlock);
}
...
With the following child relationship:
#Entity(name = "time_period_block")
public class TimePeriodBlock extends AbstractDomainObject {
...
#ManyToOne
#JoinColumn(name = "time_period_id", nullable = false)
private TimePeriod timePeriod;
...
They share this super class:
#MappedSuperclass
public abstract class AbstractDomainObject {
...
#Id
#GeneratedValue
#Column(name = "id", columnDefinition = "uuid", updatable = false)
private UUID id;
...
When I execute the following:
// pseudo code
TimePeriod t = new TimePeriod();
t.setName("test");
TimePeriodBlock b = new TimePeriodBlock();
t.addTimePeriodBlock(b);
em.persist(t);
I get the exception:
...
Caused by: org.hibernate.PropertyValueException: not-null property references a null or transient value : test.TimePeriodBlock.timePeriod
...
Some notes:
I strongly believe that this could be because Hibernate generates the UUID (and not the database) but, since I am not sure, I hope some fellow Developer might know how this could work.
I am using PostgreSQL 9.6 and the database can also generates UUIDv4 but requires compiling an extra extension so I opted for Hibernate to generate it.
When I enter some data in the database and retrieve the data it is fetched without any error.
Storing other objects without #ManyToOne relationships do store without any error and have a UUID that is generated by Hibernate.
Well after some debugging and using Luay Abdulreheem suggestion I found out that hibernate is working just fine; in this case my objects are send using a REST interface (using Jackson) and the reference to the parent was lost as the unmarshalling of the JSON is done using fields.
So nothing to see here, move along...

Spring Data JPA/REST Update subcollection

I have a Spring Data Jpa/Rest web application.
I have created two entities, A and B, where entity A contains multiple B entities.
The DTO classes are defined as:
Getters/Setters are omitted.
#Entity
public class A implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany
#JoinColumn(name = "a_id")
private List<B> b = new LinkedList<>();
}
#Entity
public class B implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String bType;
}
The Respective repositories extend PagingAndSortingReposity.
At the moment, to create a record of type A, I create a collection of links to its subentities (namely Bs). It works fine, but when I attempt to update a record I find that using the same method updates only the first level of the json and the rest remains unchanged.
My question is: how to update entity A together with a new collection of entities B.
Class A has onetomany mapping
so in Class b there must be manytoone simeting like this
#JoinColumn(name = "A_ID", referencedColumnName = "ID")
#ManyToOne
private A aID;
If you add these lines
and add CascadeType.ALL in #ManyToOne
and merge parent entity changes are getting reflected to child table also.
After lots of searching, I received a help from a mentor and internet.
Since I am working with association resource it fails to do the task in such a way. The backend Java application seems to be configured correctly.
To update the content of the application one needs to set the content-type to text/uri-list as listed in the official documentation and simply send a string with link to each subobject.

Can I inject an instance of a model class annoted with #Entity into a controller using Spring?

I am pretty new in Spring and I have the following doubt. In a web application I have the following entity class that map a database table:
#Entity
#Table(name = "KM_PROJECT_INFO")
public class KM_ProjectInfo implements Serializable {
#Id
#GeneratedValue
private Long idProjectInfo;
#Column(name = "name")
private String name;
#Column(name = "technology")
private String technology;
#ManyToOne
#JoinColumn(name = "idCountry")
private KMCountry country;
#Column(name = "power")
private long power;
#Column(name = "cod")
private String cod;
#ManyToOne
#JoinColumn(name = "idProjectInfoStatus")
private KM_ProjectInfoStatus status;
#Column(name = "folderTech")
private long folderTech;
#Column(name = "folderProject")
private long folderProject;
// GETTER & SETTER
}
In a view there is a form where the user can insert the value of the fields of the previous entity class. When the user click the submit button of this form it is performed an action of a controller (in the specific case it is a Struts 2 action controller, but I think that this is not important).
In this action I have to retrieve the value inserted by the user into the form fields and use these values to set the matching fields of the previous entity class, then I have to persist it on the DB using Hibernate.
So my doubt is: the previous entity class is annoted using #Entity annotation. Can I simply inject it into my controller? Can I inject an instance of a class annoted with #Entity?
Tnx
You can make this happen', won't happen automatically as #Entity does not mark a class to be a spring bean. You can make it a Spring bean, but than Spring framework would take over managing the life-cycle of an entity object which should be an exclusive role of the JPA framework. Making this work in a sensible way would be a horrible struggle.
Luckily for you, from what you described you don't need to do this, simply create an instance of your Entity, populate it with form params and pass on the object to your service or DAO.
You just have to create object using simple java object creation:
KM_ProjectInfo obj = new KM_ProjectInfo();
And then use setter method to set properties and do database operations. You don't need to use spring bean creation.
You don't need to inject this as a spring bean into your controller.
Just create an instance of the Entity.
Set the entity fields as per the form parameters.
Persist the entity in the DB.

Categories