Lazy loading with CrudRepository - java

I have a simple parent-child relation (one to many). And I am retrieving parents in two endpoints. One to get a list of parents and one to get a single parent. On the list, I don't want to serialize a list of children for each parent. I want to show a list of children only when I am getting a single parent.
I am working with spring boot and CrudRepository.
I tried
spring.jpa.open-in-view=false
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
also fetch = FetchType.LAZY and in the end, I was writing custom HQL with FETCH keyword.
Parent Model
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
#JoinColumn(name = "parent_id")
private List<Child> children;
Child Model
#Column(name = "parent_id")
#JsonIgnore
private Long parentId;
Parent Repository
public interface ParentRepository extends CrudRepository<Parent, Long>
Parent Service
List<Parent> findAll() {
return StreamSupport
.stream(repository.findAll().spliterator(), false)
.collect(Collectors.toList());
}
As I said I want to sometimes serialize a list of children in this relation and in some cases not.

You may be able to do this using JsonView or JsonFilter with Jackson. Another option would be to separate the concerns, i.e. use a DTO to control the serialized view of the entity.
Here is a link to a tutorial on some of these Jackson features: https://www.baeldung.com/jackson-serialize-field-custom-criteria

Most likely it’s due to the default Open Session in View. Starting with version 2.0
If you added below line in your application.properties configuration file, then it should work
spring.jpa.open-in-view=false

Ok, I finally have done it. I wrote a dedicated HQL for Parent without children.
#Query("SELECT new Parent(a.parentId, a.name, a.address, a.city) FROM Parent a WHERE a.otherId= :id")
List<Parent> findAllBySomeOtherIdWithoutChildren(#Param("id") Long id);
It looks like this is the fastest way for it. Thanks for all suggestions.

Related

Fetching a list of parent entities with a filtered collection of children in Spring Data JPA

So I've been stuck on this problem for about half a day so I am wondering if I am just over-complicating things.
My application has three different Java object classes: Grandparent, Parent, and Child. Each Grandparent contains a List of Parents, and each Parent contains a List of Children. Child has an "isWellBehaved" property, which is a boolean.
We are using Spring Data JPA and Hibernate in order to map the data to a database. Our application contains a lot of nested entities and circular relationships and we are relying on projections to keep our request size down.
The Problem: given a grandparent id, I want to return a list of all Parents (as projections). I want each of the Parents to contain a list of Child projections, but only if the Child is well behaved. The rest of the children in the collection should be filtered out from the collection.
What would be the simplest way to achieve this? We are not using Hibernate filters at the moment and I am not keen on introducing them as we are not likely to need them anywhere else (either way, would it be suited for this purpose?). I have used JPA Criteria API predicates (very little) but find it difficult to adapt that to this particular scenario. Is a native query the way to go? I've started going in that direction but am having some issues mapping all the fields to our Spring entity due to all the nested dependencies so just want to make sure I am even headed in the right direction before I continue.
My (simplified) parent entity class looks like this:
#Entity
#Table(name="parent"
public class Parent {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="parent_id")
Integer id;
Integer grandparentId;
#OneToMany(mappedBy = "parent")
List<Child> children;
}
Child class:
#Entity
#Table(name="child"
public class Child {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="child_id")
Integer id;
#ManyToOne
#JoinColumn(name="parent_id")
Parent parent;
boolean isWellBehaved;
}
Parent repository interface:
#RepositoryRestResource(excerptProjection = ParentProjection.class)
public interface ParentRepository extends JpaProjectionRepository<Parent, Integer, ParentProjection> {
List<ParentProjection> findAllByGrandparent_Id(Integer grandpaId);
}
You can use #Where annotation of hibernate on collection. It will be something like
#Entity
#Table(name="parent"
public class Parent {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="parent_id")
Integer id;
Integer grandparentId;
#Where(clause = "isWellBehaved=true")
#OneToMany(mappedBy = "parent")
List<Children> children;
}
As you have said:
I want each of the Parents to contain a list of Child projections, but only if the Child is well behaved.
List<childerns>allChilderns=parentsList.stream().map(parent>dao.findchildernByParentId()).collect(Collectors.List());
allChilderns.stream().filter(childern->childern.isWellBehaved()==true).collect(Collectors.toList());
Get all the parents by GrandParentId--the one which you are doing.
Once you got all the parents,for each parent findchildernByParentId.
and then filter out the childern on the basis of condition.
Let me know:)

spring boot jpa manyToOne(column="fatherId") join the same entity works on a recursive way until the fatherId is null

Entity{
String code;
String parentCode;
...
#ManyToOne
#JoinColumn(name="parentCode",referencedColumnName="code")
Entity parentEntity;
}
My entity class is like this. what i want to do is using findAll() to get an entity list with each entity get its own direct parent. But spring jpa will get parent's parent until the root , i need to avoid this.
Thank you!
Since a default fetch type for a #ManyToOne relation is an FetchType.EAGER I think you have just add a fetch type as LAZY explicitly:
Entity{
String code;
String parentCode;
...
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="parentCode",referencedColumnName="code")
Entity parentEntity;
}
It's not about Spring JPA but JPA itself. When you add a relationship, following defaults apply unless specified otherwise.
#xxToOne - FetchType.EAGER
#xxToMany- FetchType.LAZY
So, in your example you have a #ManyToOne which has a default EAGER fetch and one join query is appended. If your parent has another #xxToOne it adds one more join and so on. It's good to know the boundaries of your entities and decide which type of FetchType is required.
Even if you add like this:
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="parentCode",referencedColumnName="code")
Entity parentEntity;
.. if you parent has more relationships you might be getting everything loaded while fetching parent. Thus, all relationships need to be Lazy. It's a design choice based on entities.
But be aware about the ORM's N+1 problem : JPA Hibernate n+1 issue (Lazy & Eager Diff)

Spring Jpa: change FetchType for entity

Suppose I have Foo entity with one field annotated with #ManyToOne(fetch = FetchType.LAZY). Usually this field is not needed, but in some queries we need all elements with all related entities. I don't want to query database one by one to get associated entities (n+1 select issue). Is there any way to specify fetchType? Something like:
#Repository
public interface FooRepository extends CrudRepository <Foo, String> {
List<Foo> findAll(FetchType fetchType);
}
#OneToMany(fetch = FetchType.LAZY)
List<Address> address;
Let's say you have above code.
You can avoid n+1 issue if you do address.size()
getAdress().size()
so that hibernate will load all elements at once instead of loading one by one.

Skip child to fetching of parent - JPA

I am facing an issue where the data is getting fechted recursively. I wanted to avoid the child to fetch the parent data. Which is causing a recursive issue.
I have mentioned the code below
Pojo Structure
class Parent {
..
#OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
private List<Child> childs;
..
}
class Child {
..
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "parentId")
private Parent parent;
..
}
Fetching the data like this
` em = EMF.get().createEntityManager();
Query q = em.createQuery("Select p from Parent p", Parent.class);
List<Parent> parents = q.getResultList();
// Till this point all looks good but when the code gets executed
parent.getChilds();
`
It is fetching the data like this:
Parent
child1
Parent
child2
Parent
child2
Parent
..
..
child2
..
Which I dont need I just want the data like this:
Parent1
child1
child2
Parent2
child1
child2
child3
While FetchType.EAGER is a contract, FetchType.LAZY is only a hint, because lazy fetching is not always possible. This may depend e.g. on the JPA provider you use as well as on its configuration. Lazy fetching is particularly problematic with to-one relationships.
If every Child has a Parent, try adding optional=false to your #ManyToOne. This might enable lazy fetching.
Since the Parent entity is already loaded into the persistence context, populating Children.parent shouldn't trigger queries against the database. Are you actually seeing queries being executed? How do you know Children.parent is being loaded? If you are accessing the value to check that fact, chances are you are actually triggering the on-demand loading yourself.
It'll work as infinite loop for hefty data. Best practice is to mention #JsonIgnore at child class column.
Thank me Later
To avoid this problem of cyclic references, I used a Mapper with MapStruct (see official documentation for a quick setup) :
Then I can easily write a mapper to ignore a property like this :
public interface SecondaryObjectMapper {
#Mapping(target = "primaryObject.secondaries", ignore=true),
SecondaryObjectDto toSecondaryObjectDto(SecondaryObject source);
}
and use it like this for example :
repository.findAll(pageable).map(secondaryObjectMapper::toSecondaryObjectDto)
To Avoid this issue , Please declare below annotation Parent & Child getter methods
#JsonBackReference
public Parent getParent() {
return parent;
}
#JsonManagedReference
public List<Child> getChilds() {
return childs;
}

Two way relationship with JsonBackReference & JsonManagedReference

ANSWER
I'll solve my problem regarding to this blog Jackson – Bidirectional Relationships
Thanks you.
UPDATE 2
The problem is about JsonBackReference and JsonManagedReference annotations.
With my two way relationship, I have to explicitly select one way for serialization with JsonBackReference and JsonManagedReference.
But here, I am in case to use the opposit way "Parent->Child" for a specific requierement (using the way "Child->Parent" by default)
When I inversed those two annotations, my JSON is what I'm looking for, for the special requierment.
Any idea on how to use JACKSON in a two way relationship ?
Thank you.
UPDATE 1
Here is a code simple using EntityGraph (thanks to #NeilStockton suggestion), but still don't serialize the lazy attribute in JSON :-(
Parent
#Entity
public class Parent {
#Id
#GeneratedValue
private Long id;
#column
private String parentAttribute;
#OneToOne(mappedBy = "parent", optional = false)
#JsonBackReference
private Child child;
Child
#Entity
public class Child {
#Id
#GeneratedValue
private Long id;
#column
private String childAttribute;
#OneToOne(optional = false, cascade = CascadeType.ALL)
#JsonManagedReference
private Parent parent;
Parent Repository
public interface ParentRepository extends CrudRepository<Parent> {
#EntityGraph(attributePaths = { "child" })
//a hack to use findAll with default lazy/eager mapping
Collection<Parent> findByIdNotNull();
}
Generated query :
Hibernate:
select
parent0_.id as id1_33_0_,
child1_.id as id1_32_1_,
parent0_.parent_attribute as parent_attribute2_33_0_,
child1_.child_attribute as child_attribute2_32_1_,
from
test.parent parent0_
left outer join
test.child child1_
on parent0_.id=child1_.parent_id
where
parent0_.id is not null
JSON (no child):
[ {
"id": 1
"parentAttribute": "I am the parent"
} ]
Any idea on how to force Jackson Hibernate4Module to serialize if present ?
Thank you.
I have a Spring Boot 1.3.1 back-office using JPA/hibernate for mapping entities. The front-end is an Angular2 application. The communication is a REST/JSON.
My question is about forcing EAGER loading in some queries when I have a Lazy relationship.
The solution using JOIN FETCH helped me in DAO layer (Repositories). The entity is now completely loaded in a single query as I want in controllers layer. But the serialized JSON still incomplete due to Hibernate4Module.
Bellow Hibernate4Module features can't help :-(
FORCE_LAZY_LOADING
USE_TRANSIENT_ANNOTATION
SERIALIZE_IDENTIFIER_FOR_LAZY_NOT_LOADED_OBJECTS
REQUIRE_EXPLICIT_LAZY_LOADING_MARKER
REPLACE_PERSISTENT_COLLECTIONS
Any idea is welcome. Thanks.
Finally, I solved my problem by using Custom Projection with a constructor in the select part of the query. In the new projection class, there is no "JsonIgnore" or any JPA annotation that make field not serialized by Jackson. I added more data in that projection for reach use.
Hope it'll help.

Categories