How to fix org.hibernate.LazyInitializationException - could not initialize proxy - no Session
Asked Answered
F

25

290

I get the following exception:

Exception in thread "main" org.hibernate.LazyInitializationException: could not initialize proxy - no Session
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:167)
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:215)
    at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:190)
    at sei.persistence.wf.entities.Element_$$_jvstc68_47.getNote(Element_$$_jvstc68_47.java)
    at JSON_to_XML.createBpmnRepresantation(JSON_to_XML.java:139)
    at JSON_to_XML.main(JSON_to_XML.java:84)

when I try to call from main the following lines:

Model subProcessModel = getModelByModelGroup(1112);
System.out.println(subProcessModel.getElement().getNote());

I implemented the getModelByModelGroup(int modelgroupid) method firstly like this :

public static Model getModelByModelGroup(int modelGroupId, boolean openTransaction) {

    Session session = SessionFactoryHelper.getSessionFactory().getCurrentSession();     
    Transaction tx = null;

    if (openTransaction) {
        tx = session.getTransaction();
    }

    String responseMessage = "";

    try {
        if (openTransaction) {
            tx.begin();
        }
        Query query = session.createQuery("from Model where modelGroup.id = :modelGroupId");
        query.setParameter("modelGroupId", modelGroupId);

        List<Model> modelList = (List<Model>)query.list(); 
        Model model = null;

        for (Model m : modelList) {
            if (m.getModelType().getId() == 3) {
                model = m;
                break;
            }
        }

        if (model == null) {
            Object[] arrModels = modelList.toArray();
            if (arrModels.length == 0) {
                throw new Exception("Non esiste ");
            }

            model = (Model)arrModels[0];
        }

        if (openTransaction) {
            tx.commit();
        }

        return model;

   } catch(Exception ex) {
       if (openTransaction) {
           tx.rollback();
       }
       ex.printStackTrace();
       if (responseMessage.compareTo("") == 0) {
           responseMessage = "Error" + ex.getMessage();
       }
       return null;
    }
}

and got the exception. Then a friend suggested me to always test the session and get the current session to avoid this error. So I did this:

public static Model getModelByModelGroup(int modelGroupId) {
    Session session = null;
    boolean openSession = session == null;
    Transaction tx = null;
    if (openSession) {
        session = SessionFactoryHelper.getSessionFactory().getCurrentSession(); 
        tx = session.getTransaction();
    }
    String responseMessage = "";

    try {
        if (openSession) {
            tx.begin();
        }
        Query query = session.createQuery("from Model where modelGroup.id = :modelGroupId");
        query.setParameter("modelGroupId", modelGroupId);

        List<Model> modelList = (List<Model>)query.list(); 
        Model model = null;

        for (Model m : modelList) {
            if (m.getModelType().getId() == 3) {
                model = m;
                break;
            }
        }

        if (model == null) {
            Object[] arrModels = modelList.toArray();
            if (arrModels.length == 0) {
                throw new RuntimeException("Non esiste");
            }

            model = (Model)arrModels[0];

            if (openSession) {
                tx.commit();
            }
            return model;
        } catch(RuntimeException ex) {
            if (openSession) {
                tx.rollback();
            }
            ex.printStackTrace();
            if (responseMessage.compareTo("") == 0) {
                responseMessage = "Error" + ex.getMessage();
            }
            return null;        
        }
    }
}

but still, get the same error. I have been reading a lot for this error and found some possible solutions. One of them was to set lazyLoad to false but I am not allowed to do this that's why I was suggested to control the session

Forebrain answered 5/2, 2014 at 10:12 Comment(0)
B
109

What is wrong here is that your session management configuration is set to close session when you commit transaction. Check if you have something like:

<property name="current_session_context_class">thread</property>

in your configuration.

In order to overcome this problem you could change the configuration of session factory or open another session and only then ask for those lazy loaded objects. But what I would suggest here is to initialize this lazy collection in getModelByModelGroup itself and call:

Hibernate.initialize(subProcessModel.getElement());

when you are still in active session.

And one last thing. A friendly advice. You have something like this in your method:

for (Model m : modelList) {
    if (m.getModelType().getId() == 3) {
        model = m;
        break;
    }
}

Please insted of this code just filter those models with type id equal to 3 in the query statement just couple of lines above.

Some more reading:

session factory configuration

problem with closed session

Bilection answered 5/2, 2014 at 11:1 Comment(2)
Thank you! I solved my problem using openSession() instead of getCurrentSession() as one of the links you gave me suggested this, but now I'm affraid if it is wrong to do soForebrain
No it is probably fine. But read some more to be able to fully control your sessions and transactions. It is realy important to know the basics because all higher level technologies like Spring, Hibernate and more operate on the very same concept.Bilection
P
294

If you using Spring mark the class as @Transactional, then Spring will handle session management.

@Transactional
public class MyClass {
    ...
}

By using @Transactional, many important aspects such as transaction propagation are handled automatically. In this case if another transactional method is called the method will have the option of joining the ongoing transaction avoiding the "no session" exception.

WARNING If you do use @Transactional, please be aware of the resulting behavior. See this article for common pitfalls. For example, updates to entities are persisted even if you don't explicitly call save

Protestant answered 28/8, 2015 at 17:45 Comment(9)
I cannot overstate the importance of this answer. I'd seriously recommend trying this option first.Dalpe
Also note that you have to add @EnableTransactionManagement to your configuration in order to enable transactions. "if another transactional method is called the method will have the option of joining the ongoing transaction" this behavior is different for the different ways transactions are implemented, i.e. interface proxy vs class proxy or AspectJ weaving. Refer to the documentation.Unprovided
Should we understand the Spring Transactional annotation is thus advised, not only for modifying transactions, but also for accessing only ones ?Chouinard
Note that Spring will also pick up the javax.persistence.Transactional annotation if you want to stick to JPA.Elderberry
I'd seriously recommend to use this annotation on top of class only for testing. Real code should mark each method as transaction in class separately. Unless all the methods in class will require opened connection with transaction to database.Hiltner
Isn't it safe to put @Transactional(readOnly = true) instead of just @Transactional ?Helianthus
@Transactional didn't work for me on my SpringBoot integration Test case. I marked this anotation on the Entity class where the error occurs when loading child objects method on the toString().Chuffy
The @Transactional is from org.springframework.transaction.annotation.Transactional;Guideboard
It's a pitty. I have @Transactional annotations for my service class, @EnableTransactionalManagement too, EntityManager is annotated with @PersistenceContext, but I still get org.hibernate.LazyInitializationException: could not initialize proxy [app.models.User#2] - no SessionIndene
E
111
WARNING: This solution creates a NEW TRANSACTION for every LAZY object that is fetched outside a transaction. Use at your own risk.

You can try to set the following in hibernate.cfg.xml or persistence.xml

<property name="hibernate.enable_lazy_load_no_trans">true</property>

The problem to keep in mind with this property are well explained here

Erotic answered 4/12, 2014 at 20:33 Comment(8)
Can you explain its meaning as well?Fredrickafredrickson
I'm also curious what this does. It did fix the issue I was having, but I'd like to understand why.Bushcraft
After thousands of "just put lazy=false in your configuration file" and "just initialize every object with Hibernate.initialize()" finally a concrete and viable solution for me. This option should be made standard in hibernate!Roxannaroxanne
for persistence.xml: <property name="hibernate.enable_lazy_load_no_trans" value="true"/>Frizzly
DO NOT USE THIS PROPERTY, IF SPRING MANAGES YOUR TRANSACTIONS THIS PROPERTY WILL LEAD TO TRANSACTIONS EXPLOSION, SIMPLY SPRING WILL SHUTDOWN THE APPLICATIONWidthwise
To doing that you will use an anti-pattern ... so better don't do that.Felipa
This is an anti-pattern - read here - an article by Vlad Mihalcea - vladmihalcea.com/…Morocco
To deal with performance issues, instead of this, I would highly recommend best the dto solutionMatron
B
109

What is wrong here is that your session management configuration is set to close session when you commit transaction. Check if you have something like:

<property name="current_session_context_class">thread</property>

in your configuration.

In order to overcome this problem you could change the configuration of session factory or open another session and only then ask for those lazy loaded objects. But what I would suggest here is to initialize this lazy collection in getModelByModelGroup itself and call:

Hibernate.initialize(subProcessModel.getElement());

when you are still in active session.

And one last thing. A friendly advice. You have something like this in your method:

for (Model m : modelList) {
    if (m.getModelType().getId() == 3) {
        model = m;
        break;
    }
}

Please insted of this code just filter those models with type id equal to 3 in the query statement just couple of lines above.

Some more reading:

session factory configuration

problem with closed session

Bilection answered 5/2, 2014 at 11:1 Comment(2)
Thank you! I solved my problem using openSession() instead of getCurrentSession() as one of the links you gave me suggested this, but now I'm affraid if it is wrong to do soForebrain
No it is probably fine. But read some more to be able to fully control your sessions and transactions. It is realy important to know the basics because all higher level technologies like Spring, Hibernate and more operate on the very same concept.Bilection
S
102

The best way to handle the LazyInitializationException is to use the JOIN FETCH directive:

Query query = session.createQuery("""
    select m
    from Model m
    join fetch m.modelType
    where modelGroup.id = :modelGroupId
    """
);

Anyway, DO NOT use the following Anti-Patterns as suggested by some of the answers:

Sometimes, a DTO projection is a better choice than fetching entities, and this way, you won't get any LazyInitializationException.

Scissel answered 13/9, 2016 at 8:10 Comment(12)
How Can I Identify which call is having problem ? I am finding it as hard to identify the call. Is there any way ? For testing purpose I used FetchType=EAGER, but this is not correct solution, right ?Regretful
Just use logging. And EAGER is bad, yes.Scissel
Then, you should use either DTOs or initialize all associations before leaving the @Transactional service.Scissel
We should advocate the best enterprise practice but not the quick fix.Gloomy
This is the best fixSpiceberry
Why is EAGER bad? In @OneToMany too?Thorman
Here's why.Scissel
Hi @VladMihalcea, is JOIN FETCH equivalent to EAGER?Adelbert
JOIN FETCH means JOIN + SELECT joined table column. FetchType.EAGER means to always fetch the entity, and it can lead to secondary queries as well. So, they are not necessarily the same.Scissel
In my understanding, JOIN FETCH will always fetch the children entities as same as FetchType.EAGER. If the children entities are always needed, maybe it's better to use FetchType.EAGER, isn't it?Adelbert
Are the grandchildren entities also included with JOIN FETCH or do we need to add more JOIN in that query?Adelbert
@ĐỗCôngBằng Confusing FetchType.EAGER and JOIN FETCH so that you conclude that using FetchType.EAGER would be better is terrible. Once you use FetchType.EAGER, you will never be able to fetch that association lazily. On the other hand, if you make it FetchType.LAZY, you have the flexibility of not fetching it when you don't need it and fetching it with JOIN FETCH when you need it.Scissel
O
28

if you use spring data jpa , spring boot you can add this line in application.properties

spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
Odle answered 15/6, 2018 at 21:24 Comment(5)
This is considered as an anti-pattern. vladmihalcea.com/…Dealing
If it's an antipattern, could you please suggest an alternative? I'm also working with Spring Data and the other answers I've looked at don't help me.Mannerless
Thanks this works for me.Guano
It is working fine, But I don't know what is the error, and why is it happening.Amphicoelous
@SayedHussainullahSadat, explanation hereMatron
P
26

WARNING: This solution has performance issues as it loads the whole tree of data at once. Use it at your own risk.


I was getting the same error for a one to many relationships for below annotation.

@OneToMany(mappedBy="department", cascade = CascadeType.ALL)

Changed as below after adding fetch=FetchType.EAGER, it worked for me.

@OneToMany(mappedBy="department", cascade = CascadeType.ALL, fetch=FetchType.EAGER)
Phelia answered 20/9, 2017 at 10:21 Comment(3)
Yes it may fix it but now you are loading the whole tree of data. This will have negative performance impacts in most casesBrietta
solved my problem thank you so muchEdithe
To deal with performance issues, instead of this, I would highly recommend best the dto solutionMatron
M
11

This exception because of when you call session.getEntityById(), the session will be closed. So you need to re-attach the entity to the session. Or Easy solution is just configure default-lazy="false" to your entity.hbm.xml or if you are using annotations just add @Proxy(lazy=false) to your entity class.

Marketplace answered 7/9, 2016 at 14:17 Comment(2)
How do you re-attach the entity to the session?Ensphere
this answer prompted me to change my getById to findById , and solved my problemsCondescend
A
7

This means that the object which you are trying to access is not loaded, so write a query that makes a join fetch of the object which you are trying to access.

Eg:

If you are trying to get ObjectB from ObjectA where ObjectB is a foreign key in ObjectA.

Query :

SELECT objA FROM ObjectA obj JOIN FETCH obj.objectB objB
Anglophobia answered 23/4, 2018 at 11:36 Comment(0)
W
6

I encountered the same issue. I think another way to fix this is that you can change the query to join fetch your Element from Model as follows:

Query query = session.createQuery("from Model m join fetch m.element where modelGroup.id = :modelGroupId")
Walcoff answered 13/4, 2015 at 8:44 Comment(0)
I
5

Faced the same Exception in different use case.

enter image description here

Use Case : Try to read data from DB with DTO projection.

Solution: Use get method instead of load.

Generic Operation

public class HibernateTemplate {
public static Object loadObject(Class<?> cls, Serializable s) {
    Object o = null;
    Transaction tx = null;
    try {
        Session session = HibernateUtil.getSessionFactory().openSession();
        tx = session.beginTransaction();
        o = session.load(cls, s); /*change load to get*/
        tx.commit();
        session.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return o;
}

}

Persistence Class

public class Customer {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "Id")
private int customerId;

@Column(name = "Name")
private String customerName;

@Column(name = "City")
private String city;

//constructors , setters and getters

}

CustomerDAO interface

public interface CustomerDAO 
     {
   public CustomerTO getCustomerById(int cid);
     }

Entity Transfer Object Class

public class CustomerTO {

private int customerId;

private String customerName;

private String city;

//constructors , setters and getters

}

Factory Class

public class DAOFactory {

static CustomerDAO customerDAO;
static {
    customerDAO = new HibernateCustomerDAO();
}

public static CustomerDAO getCustomerDAO() {
    return customerDAO;
}

}

Entity specific DAO

public class HibernateCustomerDAO implements CustomerDAO {

@Override
public CustomerTO getCustomerById(int cid) {
    Customer cust = (Customer) HibernateTemplate.loadObject(Customer.class, cid);
    CustomerTO cto = new CustomerTO(cust.getCustomerId(), cust.getCustomerName(), cust.getCity());
    return cto;
}

}

Retrieving data: Test Class

CustomerDAO cdao = DAOFactory.getCustomerDAO();
CustomerTO c1 = cdao.getCustomerById(2);
System.out.println("CustomerName -> " + c1.getCustomerName() + " ,CustomerCity -> " + c1.getCity());

Present Data

enter image description here

Query and output generated by Hibernate System

Hibernate: select customer0_.Id as Id1_0_0_, customer0_.City as City2_0_0_, customer0_.Name as Name3_0_0_ from CustomerLab31 customer0_ where customer0_.Id=?

CustomerName -> Cody ,CustomerCity -> LA

Inversely answered 14/7, 2019 at 19:18 Comment(0)
F
4

This means you are using JPA or hibernate in your code and performing modifying operation on DB without making the business logic transaction. So simple solution for this is mark your piece of code @Transactional

Fuse answered 26/10, 2018 at 9:47 Comment(1)
Thank you! I had the problem on one method, while on other similar methods it worked just fine. Thanks to you, I figured out that I simply forget to make it transactional, what fixed it for me. +1Mantooth
O
3

There are several good answers here that handle this error in a broad scope. I ran into a specific situation with Spring Security which had a quick, although probably not optimal, fix.

During user authorization (immediately after logging in and passing authentication) I was testing a user entity for a specific authority in a custom class that extends SimpleUrlAuthenticationSuccessHandler.

My user entity implements UserDetails and has a Set of lazy loaded Roles which threw the "org.hibernate.LazyInitializationException - could not initialize proxy - no Session" exception. Changing that Set from "fetch=FetchType.LAZY" to "fetch=FetchType.EAGER" fixed this for me.

Orvilleorwell answered 15/3, 2016 at 1:55 Comment(0)
T
3
  • springBootVersion = '2.6.7'
  • hibernate = 5.6.8.Final'

For me i get the error in:

MyEntity myEntity = myEntityRepository.getById(id);

I change to this:

MyEntity myEntity = myEntityRepository.findById(id).orElse(null);

and i add @ManyToOne(fetch = FetchType.EAGER) in entity

Tankersley answered 26/4, 2022 at 13:13 Comment(0)
I
2

If you are using JPQL, use JOIN FETCH is the easiest way: http://www.objectdb.com/java/jpa/query/jpql/from#LEFT_OUTER_INNER_JOIN_FETCH_

Intransigence answered 23/3, 2017 at 21:55 Comment(0)
A
2

In Spring Application Just Add

@Transactional(readOnly = true)

on your Function.

Remind that import spring Transactional annotation

import org.springframework.transaction.annotation.Transactional;

Aldon answered 14/6, 2021 at 13:11 Comment(1)
Do you mind explaining why this might work?? I haven't tested it, however I just don't get how this might solve the issue.Matron
Q
2

Use @NamedEntityGraph. Eagar fetch will deteriorate the performance. Refer https://thorben-janssen.com/lazyinitializationexception/ for in-depth explanation.

Quita answered 18/10, 2021 at 5:5 Comment(0)
P
1

If you are using Grail's Framework, it's simple to resolve lazy initialization exception by using Lazy keyword on specific field in Domain Class.

For-example:

class Book {
    static belongsTo = [author: Author]
    static mapping = {
        author lazy: false
    }
}

Find further information here

Plica answered 27/10, 2016 at 12:38 Comment(0)
C
1

In my case a misplaced session.clear() was causing this problem.

Chilpancingo answered 30/10, 2018 at 14:4 Comment(0)
P
1

This happened to me when I was already using @Transactional(value=...) and was using multiple transaction managers.

My forms were sending back data that already had @JsonIgnore on them, so the data being sent back from forms was incomplete.

Originally I used the anti pattern solution, but found it was incredibly slow. I disabled this by setting it to false.

spring.jpa.properties.hibernate.enable_lazy_load_no_trans=false

The fix was to ensure that any objects that had lazy-loaded data that weren't loading were retrieved from the database first.

Optional<Object> objectDBOpt = objectRepository.findById(object.getId());

if (objectDBOpt.isEmpty()) {
    // Throw error
} else {
    Object objectFromDB = objectDBOpt.get();
}

In short, if you've tried all of the other answers, just make sure you look back to check you're loading from the database first if you haven't provided all the @JsonIgnore properties and are using them in your database query.

Peppi answered 14/11, 2020 at 0:37 Comment(0)
M
1
TLDR: Always serialize DTO's, avoid returning entities.

The reason for the issue is that when retrieving lazily-loaded data, first step is populate the main object, and second to retrieve the data within its proxies. For this, an open Session in Hibernate is needed.

The problem arises when the second step happens after the transaction has closed, which leads to a LazyInitializationException.

I'm going to summarize some solutions here while adding mine:

1. Avoid entity serialization, **instead serialize a dto**
2. Add `@JsonIgnore` to your `LAZY` attributes
3. Change `FetchType` to `EAGER`
4. Set `hibernate.enable_lazy_load_no_trans=true` 

Only points 1 and 2 are recommended, the other 2 aren't meant to be used at serialization phase. As they have a HUGE performance cost there, they both make a hit to the database to retrieve extra data. Option 4, actually creates a new transaction for every lazy object so please AVOID!.

My grain of sand in this question is to highly recommend to explicitly use ONLY options 1 and 2, and also FetchType.EAGER only for business logic, not as a solution for this issue.

Matron answered 19/10, 2023 at 18:25 Comment(0)
I
0

All answers about adding JOIN FETCH (or left join fetch) are correct, I want only to add this: if you have converter be sure the getAsObject sub uses a "find" than includes in the sql the Join Fetch too. I lost much time to fix a similar problem, and the problem was in the converter.

Imminent answered 16/2, 2022 at 17:34 Comment(0)
M
0

I was getting this error in my JAX-RS application when I was trying to get all the Departments. I had to add the @JsonbTransient Annotation to the attributes of both classes. My entities are Department and Employee, and the DB relationship is Many to Many.

Employee.java

...
@ManyToMany
@JoinTable(
        name = "emp_dept",
        joinColumns = {@JoinColumn(name = "emp_id", referencedColumnName = "id")},
        inverseJoinColumns = {@JoinColumn(name = "dept_id", referencedColumnName = "id")}
)
@JsonbTransient
private Set<Department> departments = new HashSet<Department>();
...

Department.java

...
@ManyToMany(mappedBy = "departments")
@JsonbTransient
private Set<Employee> employees = new HashSet<Employee>();
...
Myrtie answered 2/8, 2022 at 8:40 Comment(0)
E
-3

you could also solved it by adding lazy=false into into your *.hbm.xml file or you can init your object in Hibernate.init(Object) when you get object from db

Ealing answered 31/7, 2014 at 10:12 Comment(2)
generally adding lazy=false is not a good idea. that is why lazy is true by defaultNonprofit
The OP clearly said beforehand that he's not allowed to do that.Appeal
S
-3

uses session.get(*.class, id); but do not load function

Strobotron answered 25/8, 2015 at 21:21 Comment(1)
can you please explain this?Toothless
C
-3

Do the following changes in servlet-context.xml

    <beans:property name="hibernateProperties">
        <beans:props>

            <beans:prop key="hibernate.enable_lazy_load_no_trans">true</beans:prop>

        </beans:props>
    </beans:property>
Congou answered 9/4, 2016 at 0:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.