I googled a lot and It is really bizarre that Spring Boot (latest version) may not have the lazy loading is not working. Below are pieces of my code:
My resource:
public ResponseEntity<Page<AirWaybill>> searchAirWaybill(CriteraDto criteriaDto, #PageableDefault(size = 10) Pageable pageable{
airWaybillService.searchAirWaybill(criteriaDto, pageable);
return ResponseEntity.ok().body(result);
}
My service:
#Service
#Transactional
public class AirWaybillService {
//Methods
public Page<AirWaybill> searchAirWaybill(AirWaybillCriteriaDto searchCriteria, Pageable pageable){
//Construct the specification
return airWaybillRepository.findAll(spec, pageable);
}
}
My Entity:
#Entity
#Table(name = "TRACKING_AIR_WAYBILL")
#JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="#airWaybillId") //to fix Infinite recursion with LoadedAirWaybill class
public class AirWaybill{
//Some attributes
#NotNull
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "FK_TRACKING_CORPORATE_BRANCH_ID")
private CorporateBranch corporateBranch;
}
And when debugging, I still getting all lazy loaded attributed loaded. See image below.
One of my questions is could Jackson be involved in such behaviour?
Is there any way that I may have missed to activate the lazy loading?
EDIT
Another question, could the debugger be involved in ruining the lazy loading?
EDIT 2:
For specification build, I have :
public static Specification<AirWaybill> isBranchAirWayBill(long id){
return new Specification<AirWaybill>() {
#Override
public Predicate toPredicate(Root<AirWaybill> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
return cb.equal(root.join("corporateBranch",JoinType.LEFT).get("id"),id);
}
};
}
Hibernate Session exists within method with #Transactional.
Passing entity outside Service class is a bad practise because session is being closed after leaving your search method. On the other hand your entity contains lazy initialised collections, which cannot be pulled once session is closed.
The good practise is to map entity onto transport object and return those transport objects from service (not raw entities).
SpringBoot by default has enabled:
spring.jpa.open-in-view = true
That means transaction is always open. Try to disable it.
more information here
Most likely you are debugging while still being inside the service, thus while the transaction is still active and lazy loading can be triggered (any method called on a lazy element triggered the fetch from the database).
The problem is that lazy loading cannot occur while being outside of the transaction. And Jackson is parsing your entity definitely outside the boundaries of one.
You either should fetch all the required dependencies when building your specification or try with the #Transactional on the resource level (but try that as of last resort).
Just so that you know, LAZY fetching strategy is only a hint.. not a mandatory action. Eager is mandatory:
The LAZY strategy is a hint to the persistence provider runtime that
data should be fetched lazily when it is first accessed. The
implementation is permitted to eagerly fetch data for which the LAZY
strategy hint has been specified.
When using a debugger, you are trying to access the value of your variables. So, at the moment you click that little arrow on your screen, the value of the variable in question is (lazily) loaded.
I suppose you are using Hibernate as JPA.
From specification:
The EAGER strategy is a requirement on the persistence provider runtime that data must be eagerly fetched. The LAZY strategy is a hint to the persistence provider runtime that data should be fetched lazily when it is first accessed. The implementation is permitted to eagerly fetch data for which the LAZY strategy hint has been specified. https://docs.jboss.org/hibernate/jpa/2.2/api/javax/persistence/FetchType.html
Hibernate ignores fetch type specially in OneToOne and ManyToOne relationships from non owning side.
There are few options how to force Hibernate use fetch type LAZY if you really need it.
The simplest one is to fake one-to-many relationship. This will work because lazy loading of collection is much easier then lazy loading of single nullable property but generally this solution is very inconvenient if you use complex JPQL/HQL queries.
The other one is to use build time bytecode instrumentation. For more details please read Hibernate documentation: 19.1.7. Using lazy property fetching. Remember that in this case you have to add #LazyToOne(LazyToOneOption.NO_PROXY) annotation to one-to-one relationship to make it lazy. Setting fetch to LAZY is not enough.
The last solution is to use runtime bytecode instrumentation but it will work only for those who use Hibernate as JPA provider in full-blown JEE environment (in such case setting "hibernate.ejb.use_class_enhancer" to true should do the trick: Entity Manager Configuration) or use Hibernate with Spring configured to do runtime weaving (this might be hard to achieve on some older application servers). In this case #LazyToOne(LazyToOneOption.NO_PROXY) annotation is also required.
For more informations look at this:
http://justonjava.blogspot.com/2010/09/lazy-one-to-one-and-one-to-many.html
Just a guess: you are forcing a fetch while building your specification.
I expect something like
static Specification<AirWaybill> buildSpec() {
return (root, query, criteriaBuilder) -> {
Join<AirWaybill, CorporateBranch> br = (Join) root.fetch("corporateBranch");
return criteriaBuilder.equal(br.get("addressType"), 1);
};
}
If this is the case, try changing root.fetch to root.join
The retrieved data already lazy but you are using debug mode its return value when click to watch a data from a debugger.
You can solve this problem with wit 2 steps with jackson-datatype-hibernate:
kotlin example
Add In build.gradle.kts:
implementation("com.fasterxml.jackson.datatype:jackson-datatype-hibernate5:$jacksonHibernate")
Create #Bean
#Bean
fun hibernate5Module(): Module = Hibernate5Module()
Notice that Module is com.fasterxml.jackson.databind.Module, not java.util.Module
Another consideration is while using Lombok, #Data/#Getter annotation causes to load lazy items without need. So be careful when using Lombok.
This was my case.
I think I might have a solution. You can give this a try. This worked for me after 4 hours of hit and trial -
User Entity :
class User {
#Id
String id;
#JsonManagedReference
#OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private List<Address> addressDetailVOList = new ArrayList<Address>();
}
Address entity :
class Address {
#JsonBackReference
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "userId")
private User user;
}
Your parent class will use #JsonManagedReference, and child class will use #JsonBackReference. With this, you can avoid the infinite loop of entity objects as response and stack overflow error.
I also faced the same issue with Spring data JPA. I added the below annotation & able to get the customer records for a given ORDER ID
Customer to Order : one to Many
Order to customer is lazy load.
Order.java
#ManyToOne(cascade = CascadeType.ALL,targetEntity = CustomerEntity.class,fetch = FetchType.LAZY)
#Fetch(FetchMode. JOIN)
#JoinColumn(name = "CUSTOMER_ID",referencedColumnName = "CUSTOMER_ID",insertable = false,updatable = false)
#LazyToOne(LazyToOneOption.PROXY)
Private CustomerEntity customer
Customer.java
#Entity
#TabLe(name = "CUSTOMER" ,
uniqueConstraints = #UniqueConstraint(columnNames= {"mobile"}))
public class CustomerEntity {
#GeneratedVaLue(strategy = GenerationType.IDENTITY)
#CoLumn(name = "customer_id" )
private Integer customerld;
private String name;
private String address;
private String city;
private String state;
private Integer zipCode;
private Integer mobileNumber;
#OneToMany(mappedBy = " customer" )
#Fetch(FetchMode.JOIN)
#LazyToOne(LazyToOneOption.PROXY)
private List<OrderEntity> orders;
}
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.
What is the good way to force initialization of Lazy Loaded field in each object of collection?
At this moment the only thing that comes to my mind is to use for each loop to iterate trough collection and call getter of that field but it's not very effiecient. Collection can have even 1k objects and in that case every iteration will fire to db.
I can't change the way I fetch objects from DB.
Example of code.
public class TransactionData{
#ManyToOne(fetch = FetchType.LAZY)
private CustomerData customer;
...
}
List<TransactionData> transactions = getTransactions();
You may define Entity Graphs to overrule the default fetch types, as they are defined in the Mapping.
See the example below
#Entity
#NamedEntityGraph(
name = "Person.addresses",
attributeNodes = #NamedAttributeNode("addresses")
)
public class Person {
...
#OneToMany(fetch = FetchType.LAZY) // default fetch type
private List<Address> addresses;
...
}
In the following query the adresses will now be loaded eagerly.
EntityGraph entityGraph = entityManager.getEntityGraph("Person.addresses");
TypedQuery<Person> query = entityManager.createNamedQuery("Person.findAll", Person.class);
query.setHint("javax.persistence.loadgraph", entityGraph);
List<Person> persons = query.getResultList();
In that way you are able to define specific fetch behaviour for each differet use-case.
See also:
http://www.thoughts-on-java.org/jpa-21-entity-graph-part-1-named-entity/
https://docs.oracle.com/javaee/7/tutorial/persistence-entitygraphs001.htm
By the way: afaik do most JPA provider perform eager loading of #XXXtoOne relations, even if you define the mapping as lazy. The JPA spec does allow this behaviour, as lazy loading is always just a hint that the data may or may not be loaded immediately. Eager Loading on other other hand has to be performed immediately.
What you can do is something like this:
//lazily loaded
List<Child> childList = parent.getChild();
// this will get all the child in memory of that particular Parent
Integer childListSize = childList.size();
But if you eager load then all the child will be loaded for each of the parents. This should be your best bet.
I'm using SpringBoot, when a repository is called for the AModel, the repository is executing queries for BModel, CModel and DModel even when I am not calling for either CModel or DModel. any idea why this is happening and how I can prevent it?
#Entity
public class AModel extends Model {
#OneToOne(fetch = FetchType.EAGER)
#JsonIgnore
private BModel;
}
#Entity
public class BModel extends Model {
#OneToOne(fetch = FetchType.LAZY)
private CModel;
#OneToOne(fetch = FetchType.LAZY)
private DModel;
}
#Query("select a from com.project.models.AModel a where a.id = :id")
#Override
Candidate findOne(#Param("id")Long id);
The reason here is that when the entity AModel includes entity BModel, which in turn includes CModel and DModel. It has to fetch CModel and DModel when the fetch for AModel is called, otherwise your queries won't be able to complete if the objects of CModel and DModel are not fetched for, and the entire purpose of making the fetchType as Eager for AModel will be gone.
This happens cause of the oneToOne relations from BModel to CModel and DModel.
When you define a relation with FetchType.LAZY, then hibernate needs to substitute the object with a proxy, so that when you access this the first time, it can load it.
Now with oneToOne relation which is nullable, hibernate has no chance to know, if the relation is null or not without execute a select, cause the table in relation usually use the same primary key.
So if your relations are nonnullable, then define optional = false and no eager fetching is done. If this is not the case you could also use a oneToMany relation instead.
See also this stackoverflow question
I have a weird problem with two entities with one-to-many relation in JPA. I am using Glassfish 3.1.2.2 with EclipseLink 2.3.2. This is the first entity:
#NamedQueries({
#NamedQuery(name="SampleQueryGroup.findAll", query="SELECT g FROM SampleQueryGroup g")
})
#Entity
public class SampleQueryGroup implements Serializable {
// Simple properties, including id (primary key)
#OneToMany(
mappedBy = "group",
fetch = FetchType.EAGER,
cascade = {CascadeType.REMOVE, CascadeType.MERGE}
)
private List<SampleQuery> sampleQueries;
// Gettes/setters, hashcode/equals
}
And this is the second one:
#Entity
public class SampleQuery implements Serializable {
// Simple properties, including id (primary key)
#ManyToOne(cascade = {CascadeType.PERSIST})
private SampleQueryGroup group;
// Gettes/setters, hashcode/equals
}
I have a stateless session bean which uses an injected EntityManager to run SampleQueryGroup.findAll named query. I also have a CDI managed bean which calls the SSB method and iterates through SampleQueryGroup.getSampleQueries() for each SampleQueryGroup returned by the method. I didn't paste the code as it is pretty straightforward and somehow standard for any Java EE application.
The problem is the eager fetch does not work and getSampleQueries() returns an empty list. However, when I change the fetch type back to FetchType.LAZY, everything works and I get the list correctly populated. I don't understand why this happens. Does it have anything to do with internal caching mechanisms?
My guess is that when you add a new SampleQuery you are not adding it to the SampleQueryGroup sampleQueries, so when you access it, it is not their. When it is LAZY you do not trigger it until you have inserted the SampleQuery, so then it is there.
You need to maintain both sides of your relationships. (you could also disable caching, or refesh the object, but your code would still be broken).
See,
http://en.wikibooks.org/wiki/Java_Persistence/Relationships#Object_corruption.2C_one_side_of_the_relationship_is_not_updated_after_updating_the_other_side