How to achive lazy loading using spring data jpa? - java

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/

Related

Relationship table mapped as entity in JPA

I'm trying to map one specific many to many table on my database as an entity in JPA (cause I have some specific attributes on my relationship table and I wanted to retrieve this as the class attributes two). But having issues while declaring the IDs.
#Data
#EqualsAndHashCode(onlyExplicitlyIncluded = true)
#Entity
#Table(name = "user_plan")
public class UserPlan implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#OneToOne
private User user;
#Id
#OneToOne
private Plan plan;
private Integer billingDay;
#Enumerated(EnumType.STRING)
private BillingType billingType;
#Enumerated(EnumType.STRING)
private PlanStatus planStatus;
}
The application starts successfully but when I try to map some repository to manage this table, Hibernate throws an error:
java.lang.IllegalArgumentException: This class [class com.demo.domain.model.UserPlan] does not define an IdClass
How can I use the JPA entity annotation to manage this relationship table? Is it possible?
I cannot simply declare one property in the user class of Plan model and mark it as #ManyToMany, cause the plan table does not have the property that I need to execute some operations, which are declared on UserPlan class, also I cannot move these properties to Plan class, cause the Plan table is just a template of a plan, and the UserPlan have all the specific data (billingDay, billingType and planStatus).
JPA supports relationship tables as a Java class? Or it can be mapped only as a property?
Thanks
You are using multiple #Id annotations. To do so you need to create PrimaryKey class:
public class PrimaryKey implements Serializable {
private User user;
private Plan plan;
// Getter and Setter
}
And you need to add #IdClass(PrimaryKey.class) annotation to your entity class.
If you have a Repository don't forget to change id type to PrimaryKey:
public interface YourRepository
extends SomeRepositoryInterface<UserPlan, PrimaryKey> {
//...
}
Also check this question

Evaluate lazily with spring-data

I would like to have the roles set fetch lazily. Hibernate fetchType.Lazy doesn't work for this cases, where spring data is used. I 've been trying lots of possibilities like Entitygraph but none of them works on this, or I'm using them wrong.
I have the next classes:
A class User:
#Entity
#JsonRootName(value = "user")
#Table(name = "web_users", schema = "t_dw_comercial")
public class User {
#Id
private int userId;
private String fullName;
#OneToMany(fetch=FetchType.LAZY)
#JoinTable(name="web_users_roles",
joinColumns = {#JoinColumn(name="user_id")},
inverseJoinColumns = {#JoinColumn(name="role_id")}
)
private List<Role> roles;
}
A class Role:
#Entity
#JsonRootName(value = "roles")
#Table(name = "web_roles", schema = "t_dw_comercial")
public class Role {
#Id
private int roleId;
private String roleName;
}
Service:
#Service
public class UserService implements IUserService{
#Autowired
UserRepository repository;
public User findUserByLdapId(String loginName) {
return repository.findUserByLdapId(loginName);
}
}
Repository:
#Repository
public interface UserRepository extends CrudRepository<User, Long>{
#Query("SELECT u FROM User u where u.ldapId= ?1")
public User findUserByLdapId(String loginName);
}
Controller:
#Controller
#RestController
public class UserController {
#Autowired
private IUserService userService;
#CrossOrigin
#RequestMapping(value = "/dashboard", params = {"user"}, method = RequestMethod.GET, produces = "application/json")
public ResponseEntity<User> getUser(#RequestParam(value = "user") String ldapId) {
User user = userService.findUserByLdapId(ldapId);
if(user == null)
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
return new ResponseEntity<>(user, HttpStatus.OK);
};
}
So a json would looks like:
{
"user": {
"userId": 1,
"fullName": "Carolina Ponce",
"roles":[]
}
}
Thanks in advance!
It seems you're asking for two different things: how to fetch #OneToMany association lazily (which should work out of the box) and how to make your JSON look like the above (which has nothing to do with how your JPA entity fetching is configured).
If you serialize your entity using Jackson, by default all the fields will get serialized, including those that are fetched lazily. If the persistence context is still open when you begin to serialize the entities, Jackson will simply trigger lazy loading by accessing the property. As a result, regardless of whether you use FetchType.EAGER or FetchType.LAZY, roles will be included in the result (I assume it is the case, since you'd be getting a LazyInitializationException if the context was closed).
The solution is, simply, to tell Jackson to refrain from serializing roles using #JsonIgnore if you don't want the property in the result.
First of all Hibernate and Spring Data are totally different tools designed for different purposes and they work with each other just fine. You can read more about the differences between both here: What Is the Difference Between Hibernate and Spring Data JPA?
Spring Data has nothing to do with how you fetch entity's related collections, Hibernate, as an implementation of JPA, does.
I assume you indeed fetch your collection lazily and that happens during serialization of your entity so the mechanism works as it should.
I suggest you to create some sort of DTO object that you could return from your controller rather than returning an entity. It seems like you don't want to expose user's roles so creating a DTO without roles may be a good idea. Other solution is to instruct Jackson to ommit roles property during serialization using #JsonIgnore annotation.

Getting infinite Json response when using many to one mapping in spring [duplicate]

This question already has answers here:
Infinite Recursion with Jackson JSON and Hibernate JPA issue
(29 answers)
Closed 3 years ago.
I am trying to create Many to one mapping between two entities in spring. However when I try to fetch the values using a restController I get
Java.lang.IllegalStateException: Cannot call sendError() after the
response has been committed
error and an infinite JSON response. Adding JSON ignore solves this issue but then I don't get that column in my response at all. I don't know how to fix this. Please help. Thanks in advance.
Here are my entities:
#Entity
#Table(name="tbl1")
public class Data {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "customer")
private String customer;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name="data_id", insertable = true, nullable = false, updatable = false)
private DataList dataList1;
}
#Entity
#Table(name="tbl2")
public class DataList {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="data_id")
private Long dataId;
#Column(name="user_name")
private String userName;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER,mappedBy = "dataList1")
private List<Data> data1;
}
Repositories:
#Repository
#Transactional
public interface DataList extends JpaRepository<DataList,Long> {
}
#Repository
#Transactional
public interface Data extends JpaRepository<Data,Long> {
}
My error: Java.lang.IllegalStateException: Cannot call sendError() after the response has been committed
1) In general putting fetch = FetchType.EAGER on #OneToMany is not an advisable practice. Try to set it to LAZY instead.
2) Make sure that Data and DataList entities properly implement equals() and hashCode() methods.
It is happening as JSON serializer is trying to serialize both the entities recursively again and again.
So, while serializing Data, its member dataList1 is of type DataList which further contains List<Data>, this loop will be infinite.
Ideally, in such scenarios, the entities should be mapped to some other model meant for the serialization and response or one of the model needs to be #JsonIgnore to avoid this recursive loop going.
EAGER is a bad practice. Use LAZY, and when you need the related entities, use fetch join query.
Most likely the problem here in the bi-directional relation. You fetch DataList with Data list. And each Data in List ListData refers again.
More likely here to be a stack overflow when serializing json. So remember one rule: never give in controllers hibernate entities. Write a mapper for map this entities to Dto objects.
You may think that it is extremely boring to map some models to another. But here in another way. Hibernate entities should not be passed to front end. My application uses several types of objects: Entities (when fetch from DB), DTO (When map entities to DTO and give their in service components), Shared (when map DTO to Shared and share as a response to the controller between my microservices) and Model (when map from Response to Model and give to the template engine, for example freemarker). You may not need this hierarchy of models.
Create DTO's:
#AllArgsConstructor
#NoArgsConstructor
#Getter
public class DataListDto {
private Long dataId;
private String userName;
private List<DataDto> data1;
}
#AllArgsConstructor
#NoArgsConstructor
#Getter
public class DataDto {
private long id;
private String customer;
}
write your mapper:
public class DataListMapper {
public static DataListDto mapToDto(DataList dataList) {
return new DataListDto(dataList.getDataId(), dataList.getUserName(), dataList.getData1().stream().map(x -> DataListMapper.mapToDto(x).collect(Collectors.toList)))
}
public static DataDto mapToDto(Data data) {
return new DataDto(data.getId(), data.getCustomer());
}
}
your service:
public class DataListService {
#Autowired
private DataListRepository dataListRepository;
public List<DataListDto> getAllData() {
List<DataList> dataLists = this.dataListRepository.findAll();
return dataLists.stream().map(x->DataListMapper.mapToDto(x)).collect(Collectors.toList());
}
}

Spring data lazy load not working

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)

Specifying whether or not to lazily load with Spring Data

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.

Categories