I have the two entities similar to the ones below:
#Entity
#DynamicUpdate
public class EntityOne {
#Id
public long id;
public String name;
public String displayName;
#JsonManagedReference
#OneToMany(mappedBy = "id", cascade = CascadeType.ALL)
#OrderBy(value = "name")
public List<EntityTwo> entityTwoList;
}
entity two is
#Entity
#DynamicUpdate
public class EntityTwo {
#Id
public long id;
#JsonBackReference
#ManyToOne
#JoinColumn(name = "id")
public EntityOne entityOne;
}
service:
Lists.newArrayList(repository.findAll());
The service calls the findAll() method of the CRUDRepository. I don't want the list of entity two objects to load when calling findall() but this solution doesn't work.
Anything incorrect that i am doing here to lazy load the collection. I basically don't want the collection to be loaded until its specified.
By default the mappings are lazy load.
I am assuming jackson is trying to load your child objects.
Try followjackson-datatype-hibernate4:2.4.4
Add jackson-datatype-hibernate4:2.4.4 to your dependency and define following bean
#Bean
public Jackson2ObjectMapperBuilder configureObjectMapper() {
return new Jackson2ObjectMapperBuilder()
.modulesToInstall(Hibernate4Module.class);
}
I don't know about jackson but according to jpa specs you can not force LAZY loading neither can you rely upon it ... you just provide the lazy load hint and it's totally up to the provider to load it lazily or not (unlike EAGER , it forces loading)
First try this,i think you need to specify fetch type lazy or Eager
ManyToOne(fetch= FetchType.LAZY)
Related
I am using Spring Boot and Jackson and Hibernate to create an API. Hibernate connects to a MySQL database.
I understand the good practices but I'm stuck on a particular point.
I have an n:m relationship that contains an extra field.
Ex: Author(id, ...) -> Written(idAuthor, idBook, date) <- Book(id, ...)
I understand how to map a traditional n:m relationship, but this technique does not apply to me this time.
For this, I found a source on the internet that showed the solution: create an intermediate class in my code that contains an Author type object and a Book type object + my additional fields.
#Entity
#Table(name = "Author")
public class Author implements Serializable {
/...
#Id
#GeneratedValue
private int id;
#OneToMany(mappedBy = "author", cascade = CascadeType.ALL)
private Set<Written> written= new HashSet<>();
/...
}
#Entity
#Table(name = "Book")
public class Book implements Serializable{
/...
#Id
#GeneratedValue
private int id;
#OneToMany(mappedBy = "book", cascade = CascadeType.ALL)
private Set<Written> written= new HashSet<>();
/...
}
public class Written implements Serializable {
#Id
#ManyToOne
#JoinColumn(name = "idAuthor")
private Author author;
#Id
#ManyToOne
#JoinColumn(name = "idBook")
private Book book;
//Extra fields ....
}
That's a bidirectional link.
With this code, I get an infinite recursivity error:
Resolved [org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: java.util.ArrayList[0]->com.exampleAPI.api.model.Book["written"])]
I tried to use #JsonIgnore, #JsonManagedReference and #JsonBackReference on the Written class, also tried to use transient keyword, but nothing worked.
I can't find any source on the internet that could help me, and neither can the documentation for this particular case.
Can someone help me?
When unhandled bidirectional relationship occurs, Jackson faces infinite recursion.
I tried to use #JsonIgnore, #JsonManagedReference and #JsonBackReference on the Written class
You need to use #JsonManagedReference and #JsonBackReference annotations separately to prevent these cycles between Book and Written. A side note, transient has nothing to do with the persistence but the serialization. JPA works with the #Transient annotation.
public class Book implements Serializable {
#OneToMany(mappedBy = "book", cascade = CascadeType.ALL)
#JsonBackReference
private Set<Written> written= new HashSet<>();
...
}
public class Written implements Serializable {
#Id
#ManyToOne
#JoinColumn(name = "idBook")
#JsonManagedReference
private Book book;
...
}
Important: Don't send database entities through REST (probably what you are up to do). Better create a DAO object without bidirectional relationship and map entities into DAOs. There are several libraries able to do that: I highly recommend MapStruct, however ModelMapper is also an option. If there is a lower number of such entities, using constructors/getters/setters would be enough.
I'm using Spring and JPA (Hibernate with MySQL) and Lombok also.
Hi have this part of my entities:
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "entitya")
public class EntityA implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="ea_id")
Long id;
....
#ManyToOne
#JoinColumn(name="g_id", nullable=false)
private Group group;
....
}
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "group")
public class Group implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="g_id")
private Long id;
#OneToMany(mappedBy="group")
private List<EntityA> enitiesA = new ArrayList<>();
...
}
I implemented also the repository extends JPARepository.
Into my controllers, if I try to retrieve an EntityA by Id I obtain this exception:
failed to lazily initialize a collection of role: com.mytest.entity.Group.enitiesA, could not initialize proxy - no Session
For me it's strange because I need to retrieve only the object. I not use some getter methods on this. So, in theory, using the default fetch types, I don't need to have also the group list.
What's wrong?
Are you debugging your object with toString()?
In case it could be an error caused by the #Data annotation.
The generated toString() method contains all fields, so it might call the enitiesA variable, producing the lazy initialization error.
https://mdeinum.github.io/2019-02-13-Lombok-Data-Ojects-Arent-Entities/
Likely it's because you're accessing group.enitiesA outside of the transactional boundaries. If you want to do this, you can eager fetch them by adding eager fetch type to your OneToMany mapping such as
#OneToMany(mappedBy="group", fetch = FetchType.EAGER)
This will load the entire object graph when the parent is loaded.
If you still want to do lazy loading, look to encapsulate all of the calls into the children under the session that loaded the parent.
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.
I'm about to design my rest API. I wonder how can I handle objects such as one below:
#Entity
public class Foo {
#ManyToMany
private Set<Bar> barSet;
#OneToMany
private Set<Zzz> zzzSet;
}
As you see object I want to expose to my rest API consists of other entity collections. I'm using Spring 4 and Jackson. Is it possible to return objects like one above - or do I have to create classes with primitive values only?
Yes, it is possible but you have to handle 2 problems :
1) at serialization, Jackson will call the getter Foo.getBarSet(). This will crash because by default, Hibernate returns lazy collections for #OneToMany and #ManyToMany relationships.
If you don't need them, annotate them with #JsonIgnore :
#Entity
public class Foo {
#JsonIgnore
#ManyToMany
private Set<Bar> barSet;
#JsonIgnore
#OneToMany
private Set<Zzz> zzzSet;
}
If you need them, you must tell hibernate to load them. For example, you can annotate #ManyToMany and #OneToMany with fetch = FetchType.EAGER (It is not the only solution btw) :
#Entity
public class Foo {
#ManyToMany(fetch = FetchType.EAGER)
private Set<Bar> barSet;
#OneToMany(fetch = FetchType.EAGER)
private Set<Zzz> zzzSet;
}
2) It can also cause some infinite loops :
Serialization of Foo calls Foo.getBarSet()
Serialization of Bar calls Bar.getFoo()
Serialization of Foo calls Foo.getBarSet()
[...]
This can be handled with #JsonManagedReference and #JsonBackReference :
#Entity
public class Foo {
#JsonManagedReference
#OneToMany
private Set<Zzz> zzzSet;
And on the other side :
#Entity
public class Zzz {
#JsonBackReference
private Foo parent;
}
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/