Hibernate lazily loading fields - java

I have this issue with hibernate. the scenario is that i'am remaking my project. my previous project is with JMS which runs smoothly with my POJOs. and now i am trying to recreate it using Apache Mina(socket programming) with Google's Gson.
I am having problem with lazy loading of my fields, because the project that uses JMS runs smooth with lazy loading but now i am trying to retrieve some value in the database but there is always a hibernate exceptiong stating that failed to lazily initialize a collection of role: class.field, no session or session was closed which i want to load lazily, i dont want to retrieve it everytime.
public class User {
#Id
#GeneratedValue
#Column(name = "iduser")
private Integer iduser;
#Column(name = "firstname")
private String firstname;
#Column(name = "lastname")
private String lastname;
// this field causes the lazy loading exception
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
#BatchSize(size = 4)
private Set<Transaction> transaction;
}
and i am retrieving it in my DAO like this
public User findByUser(String username, String pass) {
// TODO Auto-generated method stub
try {
User instance = (User) getSession().createCriteria(User.class)
.add(Restrictions.eq(USERNAME, username))
.add(Restrictions.eq(PASS, pass)).uniqueResult();
return instance;
} catch (RuntimeException re) {
throw re;
}
}
the method where i call the service
public ModelAndView processSignin(User currentUser, BindingResult result,
HttpServletRequest request) {
User user = service.signin(currentUser);
//i try to convert it to GSON but when i comment this line of code it works fine. but when gson tries to convert it to json string it seems that the annotations are being executed
System.out.println(gson.toJson(user));
}
signin method that calls the DAO class
public User signinDealer(User user) {
// TODO Auto-generated method stub
User currentUser = userDAO.signinUser(user);
return currentUser;
}

If you use Spring and Hibernate (which seems the case according to the tags of the question), you may want to configure Spring transaction management (small tutorial: http://www.baeldung.com/2011/12/26/transaction-configuration-with-jpa-and-spring-3-1/).
Then, you'll have to annotate any method (on a Spring bean) that uses a Hibernate entity with #Transactional. This will guarantee that any Hibernate object will be loaded in the context of a session, and that if you happen to trigger a lazy initialization, Hibernate will be able to fetch the objects using that session.
In your case you may want to have the method that both loads and serializes the User to JSON be annotated with #Transactional. Note that if you have 2 Spring application contexts (one root and one for Spring MVC), the annotation will most likely only work on beans defined in the root application context (so annotating the controller might not work, see #Transactional methods in #Controller class are not considred as transactional).
#Transactional
public String signinAndSerialize(String username, String password) {
User user = service.signin(username, password);
return gson.toJson(user);
}

When working with Hibernate, the lazy loading only works when the getter of the field is invoked inside the same transaction that loads the entire object. That means, if the getter is invoked when the transaction has finished, and the Hibernate session closed, the lazy loading exception is thrown.
You can make the conversion inside the transaction block, or exclude the specific field from serialization as explained here

By default Hibernate enables lazy loading. You could do following:
First evaluate whether you really need the relationship data. If you don't need the relationship data then there are ways / annotations to ignore it during JSON conversion. For eg. annotating the field with #JsonIgnore (import org.codehaus.jackson.annotate.JsonIgnore;).
If you really need the data then retrieve it while your hibernate session is still open.
Explicitly convert to EagerLoading by adding following attribute to your #OneToMany annotation fetch=FetchType.EAGER. However, this will have a performance impact.

Related

JPA Lazy loading is not working in Spring boot

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;
}

LazyInitializationException : Why can't Hibernate create a session on lazy loading?

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.

Hibernate: How to make entity and all associations readOnly by default? (or auto-evict the association from the session)

I have an entity which belongs to a customer entity.
I would like the entity including all associations to be kind of read-only.
public class Foo{
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "customer_id")
private Customer customer;
#Basic
#Column(name = "firstname")
private String firstName;
// getters for both fields ....
}
Here is what I want:
Calls to setters of foo should not be persisted.
Calls to myFoo.getCustomer() should return a readonly customer so that calls to setters like myFoo.getCustomer().setSomething("should not be persisted") should not work.
Example:
List<Foo> list = fooDAO.getList();
for (Foo foo : liust) {
String f1 = foo.getCustomer().getSomeField(); // should work
foo.getCustomer.setSomeField("change that should not be persisted"); // calling this setter should not have an effect or should even throw an UnsupportedOperationException() or something
foo.setFirstName("change should not be persisted"); // also this should not be persisted.
}
Currently my solution for the association is kind of manual:
public Customers getCustomer() {
// detach the referenced object from the Hibernate session
// to avoid that changes to these association are persisted to the database
getCustomersDAO().evict(customer); // calls session.evict(o) under the hood
return customer;
}
Here is my question:
What ways are there to avoid changes to associations being persisted to the Database? E.g. using an annotation?
In general I would like this behaviour to be the default.
But it should also be possible to allow changes to be persisted. So I need it configurable. So I though about doing it on the Query-level.
My environment:
Hibernate v3.6
Spring 3.2 with HibernateDAOSupport /
HibernateTemplate and annotation based Transaction-handling.
I ran into this issue. I was storing filename as a property of an object in the database, and then trying to manipulate it in the application after reading it to reflect storage path based on environment. I tried making the DAO transaction readOnly, and hibernate still persisted changes of the property to the db. I then used entityManager.detach(object) before I manipulated the property, and that worked. But, I then later got tangled up into issues where other service methods that were dependent the auto persistence of objects to the db weren't behaving as they should.
The lesson I learned is that if you need to manipulate a property of a object that you don't want persisted, then make a "transient" property for that object with it's own getters and setters, and make sure the transient property setter isn't passed properties from the entity model that then become manipulated. For example, if you have a property "filename" of String, and you have a transient property of "s3key", the first thing the "s3Key" property setter should do is make a new String object passed into the setter for it to use in the manipulation.
#Transient
private String s3Key;
public String getS3Key {
String s3key = s3Prefix + "/" + <other path info> + this.filename;
return s3key;
}
Though all the "readOnly" and "detach" stuff for jpa/hibernate allows for custom handling of persistence of objects to the db, it just seems going to that length to customize the behavior of the objects to that extent causes supportability problems later, when persisting entity changes automatically is expected or counted on in other service functions. My own experience has been it is best to use a programming pattern of making use of transient properties for handling changes to entities you don't want persisted.

PersistenceContext and EJB transaction boundary

i am struggling with the following thing:
i have a stateless bean as a repository for a entity, this bean has a entity manager declared.
When i call this bean from another stateless bean , a entity is returned, then if a call a relation in this new returned entity a exception is thrown for "org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role". As i understand, the persistence context its attached to the transaction, or create a new one if transaction doesnt exist, but in this case, the transaction exist and its started in the client stateless bean that calls the repository bean.
here its a simple example:
#Entity
public class Config{
Long id;
String description;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="equipment")
private Equipment equipment;
}
#Entity
public class Equipment{
Long id;
String name;
#OneToMany(mappedBy = "equipment")
Config config;
}
#Stateless
public class EquipmentRepo{
#PersistenceContext(type=PersistenceContextType.TRANSACTION)
EntityManager em;
public Equipment find(Long id) {
return em.find(Equipment.class, id);
}
}
#Stateless
public class ServiceFacade {
#Inject
EquipmentRepo repo;
public List<Config> findEquipmentConfig(Long id) {
Equipment element = repo.find(id);
List<Config> configurations = element.getConfig();
return configurations;
}
}`
The lazy initialization also requires an active transaction. If you were to check carefully, the cause of the exception would be something to do with missing transaction.
When the stateless method returns, the active transaction is ended, and since the transaction is container managed, hibernate expects an active transaction when you call the entity method to load the referenced lazy loaded properties.
This problem is normally solved by ensuring that your entity domains are not used beyond the transaction/service level. When a service returns, it should return a dto of the mapped entity, with required fields. In this case therefore, if you require the lazy loaded properties, it will still be executed within the same transaction and mapped to your own dto.
First of all, I'm assuming that your Equipment class should look like:
#Entity
public class Equipment{
Long id;
String name;
#OneToMany(mappedBy = "equipment")
List<Config> config;
}
i.e config should be a collection.
#OneToMany relationships are lazily initialised by default.
When you execute:
Equipment element = repo.find(id);
it returns a single Equipment object that has a collection of Config proxies rather the entities themselves.
If you iterated over the element.config within the findEquipmentConfig method then the JPA implementation would load each Config entity as it is referenced.
This works because the entity manager and transaction are still active.
But you're iterating from the method that called findEquipmentConfig which I imagine is a servlet or some other kind of web controller that has no transactional context active.
Therefore you get the org.hibernate.LazyInitializationException.
At this point you might be considering adding a dummy load loop to preload all the configs.
But this is a bad idea because it leads to the infamous N+1 SELECT problem and an application that scales poorly. If an equipment entity had a 1000 config items associated with it then your application will execute 1001 SELECT statements to load the object (the extra one is to load the initial Equipment entity).
It is in fact one of the motivations for the join fetch query syntax that is available in JPA QL.
You can change your find method so that it looks something like:
public Equipment find(Long id) {
TypedQuery<Equipment> query = em.createQuery(
"SELECT e FROM Equipment e LEFT JOIN FETCH e.config WHERE e.id = :id", Equipment.class);
return query.setParameter("id", id).getSingleResult();
}
This will ensure that all of your equipment config is loaded at once.

One DAO per entity - how to handle references?

I am writing an application that has typical two entities: User and UserGroup. The latter may contain one or more instances of the former. I have following (more/less) mapping for that:
User:
public class User {
#Id
#GeneratedValue
private long id;
#ManyToOne(cascade = {CascadeType.MERGE})
#JoinColumn(name="GROUP_ID")
private UserGroup group;
public UserGroup getGroup() {
return group;
}
public void setGroup(UserGroup group) {
this.group = group;
}
}
User group:
public class UserGroup {
#Id
#GeneratedValue
private long id;
#OneToMany(mappedBy="group", cascade = {CascadeType.REMOVE}, targetEntity = User.class)
private Set<User> users;
public void setUsers(Set<User> users) {
this.users = users;
}
}
Now I have a separate DAO class for each of these entities (UserDao and UserGroupDao). All my DAOs have EntityManager injected using #PersistenceContext annotation, like this:
#Transactional
public class SomeDao<T> {
private Class<T> persistentClass;
#PersistenceContext
private EntityManager em;
public T findById(long id) {
return em.find(persistentClass, id);
}
public void save(T entity) {
em.persist(entity);
}
}
With this layout I want to create a new user and assign it to existing user group. I do it like this:
UserGroup ug = userGroupDao.findById(1);
User u = new User();
u.setName("john");
u.setGroup(ug);
userDao.save(u);
Unfortunately I get following exception:
object references an unsaved transient instance - save the transient
instance before flushing: x.y.z.model.User.group ->
x.y.z.model.UserGroup
I investigated it and I think it happens becasue each DAO instance has different entityManager assigned (I checked that - the references in each DAO to entity manager are different) and for user entityManager does not manager the passed UserGroup instance.
I've tried to merge the user group assigned to user into UserDAO's entity manager. There are two problems with that:
It still doesn't work - the entity manager wants to overwrite the existing UserGroup and it gets exception (obviously)
even if it worked I would end up writing merge code for each related entity
Described case works when both find and persist are made using the same entity manager. This points to a question(s):
Is my design broken? I think it is pretty similar to recommended in this answer. Should there be single EntityManager for all DAOs (the web claims otherwise)?
Or should the group assignment be done inside the DAO? in this case I would end up writing a lot of code in the DAOs
Should I get rid of DAOs? If yes, how to handle data access nicely?
any other solution?
I am using Spring as container and Hibernate as JPA implementation.
Different instances of EntityManager are normal in Spring. It creates proxies that dynamically use the entity manager that is currently in a transaction if one exists. Otherwise, a new one will be created.
The problem is that your transactions are too short. Retrieving your user group executes in a transaction (because the findById method is implicitly #Transactional ). But then the transaction commits and the group is detached. When you save the new user, it will create a new transaction which fails because the user references a detached entity.
The way to solve this (and to do such things in general) is to create a method that does the whole operation in a single transaction. Just create that method in a service class (any Spring-managed component will work) and annotate it with #Transactional as well.
I don't know Spring, but the JPA issue is that you are persisting a User that has a reference to a UserGroup, but JPA thinks the UserGroup is transient.
transient is one of the life-cycle states a JPA entity can be in. It means it's just created with the new operator, but has not been persisted yet (does not have a persistent identity yet).
Since you obtain your UserGroup instance via a DAO, it seems like something is wrong there. Your instance should not be transient, but detached. Can you print the Id of the UserGroup instance just after your received it from the DAO? And perhaps also show the findById implementation?
You don't have cascade persist on the group relation, so this normally should just work if the entity was indeed detached. Without a new entity, JPA simply has no way to set the FK correctly, since it would need the Id of the UserGroup instance here but that (seemingly) doesn't exist.
A merge should also not "overwrite" your detached entity. What is the exception that you're getting here?
I only partially agree with the answers being given by the others here about having to put everything in one transaction. Yes, this indeed may be more convenient as the UserGroup instance will still be 'attached', but it should not be -necessary-. JPA is perfectly capable of persisting new entities with references to either other new entities or existing (detached) entities that were obtained in another transaction. See e.g. JPA cascade persist and references to detached entities throws PersistentObjectException. Why?
I am not sure how but I've managed to solve this. The user group I was trying to assign the user to had NULL version field in database (the field annotated with #Version). I figured out it was an issue when I was testing GWT RequestFactory that was using this table. When I set the field to 1 everything started to work (no changes in transaction handling were needed).
If the NULL version field really caused the problem then this would be one of the most misleading exception messages I have ever got.

Categories