73
votes

Here's one that has me perplexed. I'm trying to implement a basic Hibernate DAO structure, but am having a problem.

Here's the essential code:

int startingCount = sfdao.count();
sfdao.create( sf );
SecurityFiling sf2 = sfdao.read( sf.getId() );
sfdao.delete( sf );
int endingCount = sfdao.count();

assertTrue( startingCount == endingCount );
assertTrue( sf.getId().longValue() == sf2.getId().longValue() );
assertTrue( sf.getSfSubmissionType().equals( sf2.getSfSubmissionType() ) );
assertTrue( sf.getSfTransactionNumber().equals( sf2.getSfTransactionNumber() ) );

It fails on the third assertTrue where it's trying to compare a value in sf to the corresponding value in sf2. Here's the exception:

org.hibernate.LazyInitializationException: could not initialize proxy - no Session
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:86)
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:140)
    at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:190)
    at com.freightgate.domain.SecurityFiling_$$_javassist_7.getSfSubmissionType(SecurityFiling_$$_javassist_7.java)
    at com.freightgate.dao.SecurityFilingTest.test(SecurityFilingTest.java:73)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:40)
14

14 Answers

68
votes

The problem is that you are trying to access a collection in an object that is detached. You need to re-attach the object before accessing the collection to the current session. You can do that through

session.update(object);

Using lazy=false is not a good solution because you are throwing away the Lazy Initialization feature of hibernate. When lazy=false, the collection is loaded in memory at the same time that the object is requested. This means that if we have a collection with 1000 items, they all will be loaded in memory, despite we are going to access them or not. And this is not good.

Please read this article where it explains the problem, the possible solutions and why is implemented this way. Also, to understand Sessions and Transactions you must read this other article.

14
votes

This generally means that the owning Hibernate session has already closed. You can do one of the following to fix it:

  1. whichever object creating this problem, use HibernateTemplate.initialize(object name)
  2. Use lazy=false in your hbm files.
10
votes

See my article. I had the same problem - LazyInitializationException - and here's the answer I finally came up with:
http://community.jboss.org/wiki/LazyInitializationExceptionovercome
Setting lazy=false is not the answer - it can load everything all at once, and that's not necessarily good. Example:
1 record table A references:
5 records table B references:
25 records table C references:
125 records table D
...
etc. This is but one example of what can go wrong.
--Tim Sabin

7
votes

If you are using hibernate with JPA annotations then this will be useful. In your service class there should be a setter for entity manager with @PersistenceContext. change this to @PersistenceContext(type = PersistenceContextType.EXTENDED). Then you can access lazy property in any where.

4
votes

if you are using Lazy loading your method must be annotated with

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) for Stateless Session EJB

3
votes

We encountered this error as well. What we did to solve the issue is we added a lazy=false in the Hibernate mapping file.

It appears we had a class A that's inside a Session that loads another class B. We are trying to access the data on class B but this class B is detached from the session.

In order for us to access this Class B, we had to specify in the class A's Hibernate mapping file the lazy=false attribute. For example,

     <many-to-one name="classA" 
                 class="classB"
                 lazy="false">
        <column name="classb_id"
                sql-type="bigint(10)" 
                not-null="true"/>
    </many-to-one>  
2
votes

Okay, finally figured out where I was remiss. I was under the mistaken notion that I should wrap each DAO method in a transaction. Terribly wrong! I've learned my lesson. I've hauled all the transaction code from all the DAO methods and have set up transactions strictly at the application/manager layer. This has totally solved all my problems. Data is properly lazy loaded as I need it, wrapped up and closed down once I do the commit.

Life is goodly... :)

2
votes

If you know about the impact of lazy=false and still want to makes it as default (e.g., for prototyping purposes), you can use any of the following:

  • if you are using XML configuration: add default-lazy="false" to your <hibernate-mapping> element
  • if you are using annotation configuration: add @Proxy(lazy=false) to your entity class(es)
2
votes

It seems only your DAO are using session. Thus a new session is open then close for each call to a DAO method. Thus the execution of the program can be resumed as:

// open a session, get the number of entity and close the session
int startingCount = sfdao.count();

// open a session, create a new entity and close the session
sfdao.create( sf );

// open a session, read an entity and close the session
SecurityFiling sf2 = sfdao.read( sf.getId() );

// open a session, delete an entity and close the session
sfdao.delete( sf );

etc...

By default, collection and association in an entity are lazy: they are loaded from the database on demand. Thus:

sf.getSfSubmissionType().equals( sf2.getSfSubmissionType() )

is throwing an exception because it request a new loading from the database, and the session associated with the loading of the entity has already been closed.

There is two approaches to resolve this problem:

  • create a session to enclosed all our code. Thus it would mean changing your DAO content to avoid opening a second session

  • create a session then update (i.e. reconnect) your entity to this session before the assertions.

    session.update(object);

2
votes

By default, all one-to-many and many-to-many associations are fetched lazily upon being accessed for the first time.

In your use case, you could overcome this issue by wrapping all DAO operations into one logical transaction:

transactionTemplate.execute(new TransactionCallback<Void>() {
    @Override
    public Void doInTransaction(TransactionStatus transactionStatus) {

        int startingCount = sfdao.count();

        sfdao.create( sf );

        SecurityFiling sf2 = sfdao.read( sf.getId() );

        sfdao.delete( sf );

        int endingCount = sfdao.count();

        assertTrue( startingCount == endingCount );
        assertTrue( sf.getId().longValue() == sf2.getId().longValue() );
        assertTrue( sf.getSfSubmissionType().equals( sf2.getSfSubmissionType() ) );
        assertTrue( sf.getSfTransactionNumber().equals( sf2.getSfTransactionNumber() ) );

        return null;
    }
});

Another option is to fetch all LAZY associations upon loading your entity, so that:

SecurityFiling sf2 = sfdao.read( sf.getId() );

should fetch the LAZY submissionType too:

select sf
from SecurityFiling sf
left join fetch.sf.submissionType

This way, you eagerly fetch all lazy properties and you can access them after the Session gets closed too.

You can fetch as many [one|many]-to-one associations and one "[one|many]-to-many" List associations (because of running a Cartesian Product).

To initialize multiple "[one|many]-to-many", you should use Hibernate.initialize(collection), right after loading your root entity.

1
votes

If you are managing the Hibernate session manually, you may want to look into sessionFactory.getCurrentSession() and associated docs here:

http://www.hibernate.org/hib_docs/v3/reference/en/html/architecture-current-session.html

1
votes

I think Piko means in his response that there is the hbm file. I have a file called Tax.java. The mapping information are saved in the hbm (=hibernate mapping) file. In the class tag there is a property called lazy. Set that property to true. The following hbm example shows a way to set the lazy property to false.

` id ...'

If you are using Annotations instead look in the hibernate documenation. http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/

I hope that helped.

1
votes

use Hibernate.initialize for lazy field

1
votes

If you using Spring and JPA annotation, the simpliest way to avoid problem with session in lazy initialize is replaysing:

@PersistenceContext   

to

@PersistenceContext(type = PersistenceContextType.EXTENDED)