异常: 无法初始化代理

这里有一个让我感到困惑的问题,我正在尝试实现一个基本的 Hibernate DAO 结构,但是遇到了一个问题。

下面是基本代码:

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() ) );

它在第三个 assertTrue 上失败,因为它试图将 sf 中的值与 sf2中的相应值进行比较。这里有个例外:

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)
207934 次浏览

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.

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

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... :)

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/

我希望这对你有帮助。

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

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) for Stateless Session EJB

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>

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.

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.

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)

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

use Hibernate.initialize for lazy field

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

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.

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)