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;
}
Related
I am trying to replicate some functionality that I was using in Spring Data JPA in the new reactive Data r2dbc. I am aware that r2dbc is not a full-fledged ORM but wanted to understand as what could be the best way to replicate the below scenario in r2dbc:
public class Doctor extends BaseModel {
//other fields and id
#NotNull
#Enumerated(EnumType.STRING)
#ElementCollection(fetch = FetchType.LAZY, targetClass = Language.class)
#CollectionTable(name = "doctor_language",
joinColumns = #JoinColumn(name = "doctor_id"))
#Column(name = "language")
private List<Language> languages = new ArrayList<>();
#OneToMany(fetch = FetchType.LAZY, targetEntity = DoctorHealthProvider.class, mappedBy =
"doctor")
private List<DoctorHealthProvider> providers = new ArrayList<>();
// other fields
}
A simple findById call on DoctorRepository (which extends JpaRepository) will give me doctor object with list of languages from doctor_language table and list of health providers from health_provider table if I use Spring Data JPA
I was reading about projections but couldn't seem to figure out the best way to implement the above in Reactive Spring. Any help/guidelines/direction is appreciated.
Thanks
You may have a look at https://github.com/lecousin/lc-spring-data-r2dbc but looks like not yet fully ready to be used. But if your needs are not too complex it may do the job.
For example you can declare links like that:
#Table
public class TableWithForeignKey {
...
#ForeignKey(optional = false)
private LinkedTable myLink;
...
}
#Table
public class LinkedTable {
...
#ForeignTable(joinkey = "myLink")
private List<TableWithForeignKey> links;
...
}
When you want the links to be mapped, you can use either lazy loading or select with join.
If you use the methods findBy... (findById, findAll...) of the Spring repository, only the table of the repository will be loaded. In this case, you can use lazy loading. For that you need to declare a methods, with default body, and your method will be automatically implemented:
public Flux<TableWithForeignKey> lazyGetLinks() {
return null; // will be implemented
}
Another way is to make joins directly in the request. There is currently no support to automatically do joins in a repository (like #EntitiGraph in JPA), but you can implement your methods like this:
public interface MyRepository extends LcR2dbcRepository<LinkedTable, Long> {
default Flux<LinkedTable> findAllAndJoin() {
SelectQuery.from(LinkedTable.class, "root") // SELECT FROM LinkedTable AS root
.join("root", "links", "link") // JOIN root.links AS link
.execute(getLcClient()); // execute the select and map entities
}
}
The result will be all LinkedTable instances, with the list of links loaded together from the database.
Alternatively try Micronaut framework, it adopts the good parts from all existing frameworks and provides a lot of missing features in spring/SpringBoot.
For example, Micronaut Data R2dbc, it provides a collection of annotations(including one to many, many to many, embedded) to define relations like JPA, more simply you can use JPA annotations and JPA like Specifciation to customize query via type safe Criteria APIs.
Create a web application via https://micronaut.io/launch/, add R2dbc and Database drivers to dependencies, you can bring your Spring experience to Micronaut without extra effort.
My Micronaut Data R2dbc example: https://github.com/hantsy/micronaut-sandbox/tree/master/data-r2dbc
My project use to have a lot of #Transactional method everywhere. Now because of business logic I do not want to rollback when I have an issue, but want to set my object to an error status (aka saved in the db, so definitly not rollback), so I removed a few #Transactional to start.
Now the issue is where there is a lazy loading there is no session, thus spawning a LazyInitializationException.
Now here are my following trouble-shooting and solution seeking so far :
We're using annotation configuration, so no xml configuration here.
For each action using the database, an EntityManager (defined as an attribute and #Autowired in the service) is created and then deleted (I can clearly see it in the logs when adding the configuration to see them), which apparently is normal according to the Spring documentation.
Using #PersistenceContext or #PersistenceUnit, either with a EntityManagerFactory or with EntityManager doesn't work.
I can load the lazy-loaded attribute I want to use with Hibernate.initialize(), and it then doesn't spawn a LazyInitializationException.
Now my question is : Why can't hibernate do it by itself ? It's seems trivial to me that if I'm using a lazy loading, I want Hibernate to create a session (which he seems perfectly able to do when doing Hibernate.initialize()) to load the date automatically.
Would there be a way to spawn a new entity manager to be use inside my method so Hibernate doesn't create and recreate one all the time ? I really feel like I'm missing something basic about Hibernate, lazy loading and sessions that makes this whole mess a lot less complicated.
Here is an example :
#Entity
#Table(name = "tata")
public class Tata {
#Id
#Column(name = "tata_id")
private Long id;
// getter / setter etc
}
#Entity
#Table(name = "toto")
public class Toto {
#Id
#Column(name = "toto_id")
private Long id;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "tata_id")
private Tata tata;
// getter / setter etc
}
#Service("totoManager")
public class TotoManager extends GenericManagerImpl {
#Autowired
private EntityManager entityManager;
#Autowired
private TotoRepository totoRepository;
public void doSomethingWithTotos() throws XDExceptionImpl {
List<Toto> totos = this.totoRepository.findAll();
for (toto toto : totos) {
// LazyInitializationException here
LOGGER.info("tata : " + toto.getTata().getId());
}
}
}
Hibernate can do it by itself. With setting property hibernate.enable_lazy_load_no_trans=true (for spring boot it should be spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true) you can load any lazy property when transaction is closed. This approach has huge drawback: each time you load lazy property, hibernate opens session and creates transaction in background.
I would recommed fetch lazy properties by entityGraphs. So you doesnt have to move persistent context do upper level or change fetch type in your entities.
Try to read something about Open session in view to understand why Lazy loading does not work out of session/transaction. If you want you can set property spring.jpa.open-in-view=true and it will load your lazy loaded data.
Using Hibernate 5, Spring 4
Please consider below codes and mapping between two entities:
User class
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "user")
private TruckOwner truckOwner;
//getter setters below
TruckOwner class
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id", nullable = false)
private User user;
//getter setter below
When my code tries to update values inside user class like below code:
UserServiceImpl class
#Override
#Transactional(propagation = Propagation.REQUIRED, readOnly = false)
public void resetPassword(Long userId,String newPassword) {
User user = userDAO.findById(userId);
user.setPassword(newPassword);
System.out.println(user.getTruckOwner().getTruckOwnerId());
userDAO.merge(user);
}
When calling userDAO.merge(user); I get below error:
non-transient entity has a null id: com.mymodel.TruckOwner
I am facing this kind of problem in many places in my project, please help me with a proper solution to this problem and why is TruckOwner class has everything null set by hibernate?
We should know the implementation of the userdao merge method but I guess it's called the merge method of hibernate Session interface
In any case the not transient object is the TruckOwner object; hibernat will not fetch the object when you call System.out.println(user.getTruckOwner().getTruckOwnerId()); moreover in that point you are out from hibernate session and if you call any other getter of truckOwner except getTruckOwnerId() you should get the org.hibernate.LazyInitializationException (or similar.. I don't remember correctly)
I guess you have 2 option:
as suggested by staszko032 you should change fetch type to EAGER
When you load the user object by using the userDAO.findById(userId); you should fetch the truckOwner object inside the hibernate session by calling any other method inside the userDAO.findById(userId); implementation and inside the hibernate session
I hope it's useful
Angelo
Try this for the truck class:
#Entity
#Table(name = "truckOwner")
public class TruckOwner{
...
private User user;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "truckOwner", cascade = CascadeType.ALL)
public User getUser() {
return this.user;
}
}
And this for the User class:
#Entity
#Table(name = "user")
public class User{
private TruckOwner truckOwner;
#OneToOne(fetch = FetchType.LAZY)
#PrimaryKeyJoinColumn
public TruckOwner getTruckOwner() {
return this.truckOwner ;
}
}
Eager mode is not a solution if you are making a production application. Problem is in your session is already closed when your are trying to getTruckOwner. Try to propagate session to all resetPassword method.
First you should not be using merge here! You should almost never use merge.
Merge should be used when you have an unmanaged entity (serialized or loaded by a previous persistence context) and wish to merge it into the current persistence context, to make it a managed entity. Your entity is already managed since a persistence context was loaded with your DAO inside a container managed transaction. This means you don't have to do even have to call save, any changed to a managed entity will be detected and persisted when the transaction commits.
On the surface JPA looks easy, because a lot of the complexity is not visible on the surface, god knows I banged my head against the wall when I started with TopLink 7 years ago, but after reading about Object life cycles and application versus container managed persistence context, I made a lot more mistakes, and it was much easier to decipher the error messages.
Another solution will be changing the fetch type to EAGER mode in User class. With LAZY mode, hibernate doesn't retrieve TruckOwner connected with user, as it is not explicitly needed in your case. Eventually TruckOwner is null for user, however it has nullable = false option set, and that's why merge fails.
I have an entity setup like this:
#Entity
#Table(name = "APP", uniqueConstraints = #UniqueConstraint(columnNames = "APP_KEY"))
public class Application implements java.io.Serializable {
...
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "application_Id", referencedColumnName = "application_Id")
private Set<Document> documents = new HashSet<Document>(0);
}
Now, in some situations I don't want the list of documents to be returned. However when I serialize this object the "getDocuments()" method will be called.
There will not be an active transaction at this time, so I don't want one of those "no session" errors. I just want to ignore it and have the getDocuments() method return empty, not throw an exception and not try to fetch more data.
Take a look at the FetchGroup capability ref:
Take a look at the jpql fetch join's...
Select e from Employee e join fetch e.phones p where p.areaCode = '613'
This was covered for a similar question here: How to properly express JPQL "join fetch" with "where" clause as JPA 2 CriteriaQuery?
I do not know your serialization setup, however:
If you are using Jackson, you can use this : https://github.com/FasterXML/jackson-datatype-hibernate which works quite nicely.
If you have a manual serialization setup, you should probably not be calling methods directly, without prior knowledge of their initialization status. What you could do is use something like Hibernate.isInitialized(object.Method)
https://docs.jboss.org/hibernate/orm/5.2/javadocs/org/hibernate/Hibernate.html#isInitialized-java.lang.Object-
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