Specifying whether or not to lazily load with Spring Data - java

I have a lazy fetch type collection in an entity. And I am using Spring Data (JpaRepository) for accessing the entities.
#Entity
public class Parent{
#Id
private Long id;
#OneToMany(mappedBy = "parentId", fetch = FetchType.LAZY)
private Set<Child> children;
}
I want two functions in service class and current implementation are as following:
"children" should be null when fetching parent
public Parent getParent(Long parentId){
return repo.findOne(parentId);
}
"children" should be filled when fetching parent:
public Parent getParentWithChildren(Long parentId){
Parent p = repo.findOne(parentId);
Hibernate.initialize(p.children);
return p;
}
When returning "Parent" entity from a RestController, following exception is thrown:
#RequestMapping("/parent/{parentId}")
public Parent getParent(#PathVariable("parentId") Long id)
{
Parent p= parentService.getParent(id);//ok till here
return p;//error thrown when converting to JSON
}
org.springframework.http.converter.HttpMessageNotWritableException:
Could not write content: failed to lazily initialize a collection of
role: com.entity.Parent.children, could not initialize proxy - no
Session (through reference chain: com.entity.Parent["children"]);
nested exception is
com.fasterxml.jackson.databind.JsonMappingException: failed to lazily
initialize a collection of role: com.entity.Parent.children, could not
initialize proxy - no Session (through reference chain:
com.entity.Parent["children"])

If you are looking to allow for different JSON representations of the same domain model depending on use case, then you can look at the following which will allow you to do so without requiring DTOs:
https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring
Alternatively, see also the 'Projections in Spring Data REST' section in the following
https://spring.io/blog/2014/05/21/what-s-new-in-spring-data-dijkstra#projections-in-spring-data-rest

The RestController should return a ParentDTO instead of Parent entity. ParentDTO can be populated in a Transactional service method.

The exception is thrown because the JSON serializer requires all properties to be already initialized. So, all REST Controllers that need to return a Parent, have to initialize the properties first:
#RequestMapping("/parent/{parentId}")
public Parent getParent(#PathVariable("parentId") Long id) {
return parentService.getParentWithChildren(id);
}
The getParentWithChildren Service method is run inside a transaction, and associated Hibernate Session is closed when the transaction is committed. This means you have to initialize all properties while the Hibernate Session is still open (inside the Service method).
You can also use the Spring Data entity graph support:
#Entity
#NamedEntityGraphs(#NamedEntityGraph(name = "Parent.children", attributeNodes = #NamedAttributeNode("children")))
public class Parent{
#Id
private Long id;
#OneToMany(mappedBy = "parentId", fetch = FetchType.LAZY)
private Set<Child> children;
}
And the getParentWithChildren method becomes:
#Repository
public interface ParentRepository extends CrudRepository<Parent, Long> {
#EntityGraph(value = "Parent.children", type = EntityGraphType.LOAD)
Parent getParentWithChildren(Long parentId);
}
So, you don't even need to implement the:
getParent
getParentWithChildren
These methods can be supplied by Spring Data.

First of all, you did not show us the Child Java class: I hope the property is called parentId and not parent:
public class Child {
#ManyToOne
private Parent parentId;
}
Solution 1: your code is actually correct, just that you MUST use a second layer of DTOs (simple POJO classes) for transporting your domain layer to the client/browser. If you do not do so, after you will solve your Lazy Exceptions you will get a problem with the circular dependency from Parent to Child and the JSON marshaller (Jackson) will try to encode a Child, then its Parent, then again its children, then again their Parent and so on. An example of DTO would be:
public class ParentDto {
private Long id;
private String prop1;
public ParentDto(Parent parent) {
this.id = parent.id;
this.prop1 = parent.prop1;
//...other properties
}
//here come all getters for the properties defined above.
}
Solution 2: Use #JsonIgnore for your public property Parent.getChildren(), so that the Jackson does not try to encode the children when marshalling the Parent instance.

Related

Hibernate tries to persist child twice after setting parent

I've got such Hibernate mapping:
Parent:
#OneToMany(mappedBy = "parent")
#Valid
private Set<Assignment> assignments;
Child:
#ManyToOne(cascade = {CascadeType.PERSIST})
#JoinColumn(name = "parent_id")
#Valid
private Parent parent;
I've got such situation: Parent is already persisted in database, I get new Assignments list. I make such thing (using Spring Data JPA):
parent.getAssignments().addAll(newAssignments);
parent.getAssignments().forEach(assignment -> assignment.setParent(parent));
parentRepository.save(parent);
I've got #PrePersist annotated method in base entity (extended by both), with validation that checks if createdDate, which is initialized in #PrePersist is not null. Base entity contains:
#NotNull
#Column(name = "created_date")
protected LocalDateTime createdDate;
#PrePersist
public void prePersist() {
setCreatedDate(LocalDateTime.now());
setModifiedDate(LocalDateTime.now());
validateEntity();
}
private void validateEntity() {
Set<ConstraintViolation<BaseEntity>> violations = Validation.buildDefaultValidatorFactory().getValidator().validate(this);
if(!violations.isEmpty()) {
throw new RuntimeException(new ValidationMessageBuilder().build(violations));
}
}
I've checked the objects under debugger, and for example Assignment is object #17609 and Parent is #17608.
Exception is raised, after invoking save() method.
java.lang.RuntimeException: may not be null => com.foo.bar.model.domain.Assignment.parent.assignments.createdDate.
at com.foo.bar.model.BaseEntity.validateEntity(BaseEntity.java:70)
at com.foo.bar.model.BaseEntity.prePersist(BaseEntity.java:58)
I've debugged and in #PrePersist method of Assignment entity Hibernate invokes it for another object, which is Assignment#17646, which contains Parent#17608, and parent contains collection of children with one element, which is #17609.
Why hibernate tries to persist another object?
these are my observations to your problem:
The Exception being thrown is not from Hibernate... it is being
thrown by your validation method: validateEntity(), because your
child assigments have a null createDate.
You're using EntityListeners for setting your createdDate and
modifiedDate and validating your bean values... However, it seems
(and this is me guessing) that your validation method is iterating
(and validating) over the child assigments long before hibernate
executes the #PrePersist on them!
My Suggestions to fix the problem:
Use this configuration:
Parent:
#OneToMany(mappedBy = "parent", CascadeType = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE})
private Set<Assignment> assignments;
Child:
#ManyToOne
#JoinColumn(name = "parent_id")
private Parent parent;
BaseClass: Rename your prePersist method as:
#PrePersist
#PreUpdate
public void prePersistOrUpdate() {
setCreatedDate(LocalDateTime.now());
setModifiedDate(LocalDateTime.now());
validateEntity();
}
Check (and modify if required) your base validation method. This
method should only validate the instance variables (as
createdDate) and avoid transverse child objects that will be
modified later for the EntityListener Method:
prePersistOrUpdate() (Beware, I remove the #Valid annotation
because I don't know for what it is used for... may be It is used by
your validation framework).
Modify the business logic (where the newAssigments) are persisted
as this:
parent.getAssignments().addAll(newAssignments);
parent.getAssignments().forEach(assignment -> assignment.setParent(parent));
parentRepository.merge(parent); // This should cascade and persist the new assigments!

Can not handle managed/back reference 'defaultreference' in jackson for composite key

Recently i got this error,
can not handle managed/back reference 'defaultreference' in jackson for composite key
I googled alot but found the below option to use,
JsonManagedReference and JsonBackReference
Reference
But my situation is,
Class Parent{
private int id;
#JsonManagedReference
Set<Child> childSet;
}
Class Child{
private ChildId childId;
private String name;
}
Class ChildId{
private int childKey;
#JsonBackReference
private Parent parent;
}
As you see, in the child class it has a composite key. I can not change this since it has relationship with DB.
Can anybody help me with this issue?
Note:
I'm using Jackson 2.4.3
I'm using Javers 1.2.9 for Object comparison
Update1:
As per suggestion, I have removed JsonManaged and JsonBack reference annotations and added JsonIgnore to Parent attribute in childId Class.
But im getting below error with Javers,
JaVers runtime error - diff for Set of ValueObjects is not supported
The issue is resolved.
The most weird way of solving.. ;)
Removed #JsonManagedReference in Parent.
Add #JsonBackReference in Parent object which is in Child's Id object.
Ex:
Class Parent{
private int id;
Set<Child> childSet;
}
Class Child{
private ChildId childId;
private String name;
}
Class ChildId{
private int childKey;
#JsonBackReference
private Parent parent;
}
Do you see this exception on deserializing JSON to Java objects?
If yes, the workaround that I used was to -
1. Remove the #JsonManagedReference and #JsonBackReference from the entities.
2. #JsonIgnore the Parent reference (for ex, in your ChildId class). So parent reference in ChildId is null on serializing.
3. To deserialize, send two seperate entities (Child and Parent) back to the service. Once both the objects are available, I set the Parent back into the ChildId class which helps to satisfy the circular reference.

Do I use the correct JPA annotations for persisting my entities in my H2 database?

my problem:
#Entity
public class Container {
#OneToMany(cascade = CascadeType.ALL)
private List<Element> containedElements;
public final List<Element> getContainedElements() {
return containedElements;
}
}
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Element {
#ManyToOne(cascade = CascadeType.ALL)
private Container myContainer;
public final Container getMyContainer() {
return myContainer;
}
public abstract Object getValue();
public abstract void setValue(final Object newValue);
}
#Entity
public class StringElement extends Element {
private String someValue;
public final Object getValue() {
return someValue;
}
public final void setValue(final Object newValue) {
someValue = newValue;
}
}
I have a container class containing probably many objects of an abstract class Element.
I have more than one implementation of this Element class, one of them StringElement.
Using the JPA API (provided by Hibernate) and a local H2 database and a small test class, I can persist entities of these classes and query the database for them and output them to the console.
Using Wildfly 8.0 (JBoss), the JPA API (provided by Hibernate) and a Wildfly-"managed" H2 database, I can persist entities, but when I query the database for a Container object, I cannot access the contained elements. Trying to do this results in the following error:
Caused by: java.lang.NumberFormatException: empty String
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1011)
at java.lang.Double.parseDouble(Double.java:540)
at org.h2.value.Value.convertTo(Value.java:846)
I can query the database for a list of all StringElements in the database and parse the results. I can access the Container via getMyContainer(). Then when I try to access an Element via getContainedElements().get(0), I get the above error again.
Did I use the correct JPA annotations? How can I have a list of abstract objects in my container?
#OneToMany annotation has FetchType.LAZY as default.
It looks like you are trying access containedElements when the Container entity is no longer managed by the persistence context.
You must read containedElements when the Container is managed by persistence context (if you want to leave FetchType.LAZY) or just change fetch to FetchType.EAGER:
#OneToMany(cascade = CascadeType.ALL, fetch=FetchType.EAGER )
private List<Element> containedElements;
I can finally answer this on my own:
The problem lies with multiple classes that inherit Element. Or rather, the cause of the error are two or more classes inheriting from Element which each have an attribute named someValue. The problem is that each attribute has a different primitive type.
Doing a select over all Elements (and subclasses of it) tries to join the tables of two Element subclasses and then tries to merge the someValue columns. As each column has a different type, the engine gives the merged column some type (e.g. Long) and tries to cast all values from the other columns to the decided type.
One DB engine merged the columns and made a Long column. This resulted in the above error. Another DB engine merged the columns and made a Varchar column. This resulted in no error because all my values could be easily transformed to Varchar.

self-reference field mapping in JPA

Lets say we have User entity class. User can be friends with other users. How can i map this self-reference collection field without creating a new entity called Connection or creating multiple entries in the database?
#Entity
public class User {
...
#ManyToMany
private Collection<User> friends;
...
}
USER_ID-FRIEND_ID
1 - 2
2 - 1 (duplicate... I don't need it)
Following is snapshot from my code for ElementEntity:
#OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
private List<ElementEntity> children;
#JoinColumn(name = "ParentId", referencedColumnName = "ElementId")
#ManyToOne(fetch = FetchType.LAZY)
private ElementEntity parent;
Where on database there are fields:
ElementId - primary key;
ParentId relation with parent
You can't - you need both records in the database.
Actually, for friendship relations, I'd say that a graph database like neo4j is the proper thing to use. There you have the two users and simply add an edge "friends".
At least you will need a relational table.
So you have a USER table and a FRIENDS:
user_id friend_id
1 2
But #Bozho answer is way better than mine (neo4j).
Well, in fact you can.
You can use annotations like #PreUpdate, #PrePersists, #PostUpdate and so to convert manually the elements of a collection. This way your entity can render then them way you want while in database you only store a raw text.
A more pausible alternative will be to use #Convert annotation, available since jpa 2.1 (#UserType in hibernate). It tells jpa to convert the field into another type everytime it read/save in database.
For it you should use #Convert anotation, specifying and AttributeConverter object.
For example
public class Parent {
#Id
private Integer id;
#Convert(converter = FriendConverter.class)
private Set<Parent>friends;
}
And converter class like the following:
#Component
public class FriendConverter implements AttributeConverter<List, String>{
#Autowired
private SomeRepository someRepository;
#Override
public String convertToDatabaseColumn(List attribute) {
StringBuilder sb = new StringBuilder();
for (Object object : attribute) {
Parent parent = (parent) object;
sb.append(parent.getId()).append(".");
}
return sb.toString();
}
#Override
public List convertToEntityAttribute(String dbData) {
String[] split = dbData.split(".");
List<Parent>friends = new ArrayList<>();
for (String string : split) {
Parent parent = someRepository.findById(Integer.valueOf(string));
friends.add(accion);
}
return friends;
}
}
It is a dummy implementation but it gives you the idea.
As a personal comment, I do recommend to map the relationship as it should. In the future it will avoid you problems. AttributeConverter comes in handy when working with enums

How to achive lazy loading using spring data jpa?

In my project, I am using Spring Data JPA and extend the JpaRepository interface for my data fetching class.
OrganizationMaster class :
#Entity
#Table(name="organization_master")
public class OrganizationMaster {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="organization_id")
private int organizationId;
#OneToMany(mappedBy="organizationMaster")
private List<CompanyMaster> companyMasters;
}
CompanyMaster Class:
Entity
#Table(name="company_master")
public class CompanyMaster {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="company_id")
private int companyId;
#ManyToOne
#JoinColumn(name="organization_id")
private OrganizationMaster organizationMaster;
}
My Controller
#RequestMapping(value = "/GetOrganization", method = RequestMethod.GET)
public
#ResponseBody
List<OrganizationMaster> getOrganization(){
return organizationService.getOrganization();
}
OrganizationService:
public interface OrganizationService {
List<OrganizationMaster> getOrganization();
}
OrganizationServiceImpl:
#Service
public class OrganizationServiceImpl implements OrganizationService{
#Autowired
private OrganizationDao organizationDao;
#Override
public List<OrganizationMaster> getOrganization() {
return organizationDao.findAll();
}
}
OrganizationDao Interface:
public interface OrganizationDao extends JpaRepository<OrganizationMaster,Long> {
}
My Output Response is:
[{"organizationId":5,"companyMasters":[{"companyId":29},{"companyId":30}]}]
But my need is
[{"organizationId":5}]
When I am trying to get data from the organization master using findall() method it also fetches data from the company master based on the relationship. How can I achieve lazy fetching (get data only from organization master) using spring data JpaRepository
All XToOne associations are default EAGER. You have a bidirectional relationship, so you should use FetchType.LAZY on your #ManyToOne side.
#ManyToOne(fetch = FetchType.LAZY)
Also if you use any serializer (like json serializer) when it serialize it calls getter methods and it may causes to load lazy items unneccessarly.
Another consideration is while using Lombok, #Data annotation causes to load lazy items without need. So be careful when using Lombok.
So in your case, beacuse of you return entity itself, while serialization it serializes the child too, it causes to load lazly child entity.
You need to return a dto which represents only your parent entity to prevent serialization of child entity. If you call child with getter method, it laods lazly child entity from database.
Take a look for further information associations:
https://vladmihalcea.com/the-best-way-to-map-a-onetomany-association-with-jpa-and-hibernate/
I believe this question is asked before!you can use this annotation:
#OneToMany( fetch = FetchType.LAZY )
read this article for better view in this point:
https://howtodoinjava.com/hibernate/lazy-loading-in-hibernate/

Categories