这是让我困惑的一个。我正在尝试实现一个基本的 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)
问题是您正试图访问 detached 对象中的集合。在将集合访问到当前会话之前,您需要重新附加对象。你可以通过
session.update(object);
使用 lazy=false
不是一个好的解决方案,因为您丢弃了休眠的延迟初始化功能。当 lazy=false
时,集合在请求对象的同时加载到内存中。这意味着如果我们有一个包含 1000 个项目的集合,它们都将加载到内存中,无论我们是否要访问它们。这不好。
请阅读此 article,其中解释了问题、可能的解决方案以及为什么以这种方式实施。此外,要了解会话和事务,您必须阅读 this other article。
这通常意味着拥有的 Hibernate 会话已经关闭。您可以执行以下操作之一来修复它:
无论哪个对象创建了这个问题,使用 HibernateTemplate.initialize(object name) 在你的 hbm 文件中使用lazy=false。
见我的文章。我遇到了同样的问题 - LazyInitializationException - 这是我最终想出的答案:
http://community.jboss.org/wiki/LazyInitializationExceptionovercome
设置lazy=false 不是答案 - 它可以一次加载所有内容,这不一定很好。示例:
1 条记录表 A 引用:
5 条记录表 B 引用:
25 条记录表 C 引用:
125 条记录表 D
...
等等。这是但是一个可能出错的例子。
--Tim Sabin
如果您使用带有 JPA 注释的休眠,那么这将很有用。在您的服务类中,应该有一个带有@PersistenceContext 的实体管理器设置器。将其更改为 @PersistenceContext(type = PersistenceContextType.EXTENDED)。然后你可以在任何地方访问惰性属性。
如果您使用延迟加载,您的方法必须使用注释
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
用于无状态会话 EJB
我们也遇到了这个错误。我们为解决这个问题所做的就是在 Hibernate 映射文件中添加了一个lazy=false。
看来我们有一个在加载另一个类 B 的 Session 中的类 A。我们正在尝试访问类 B 上的数据,但是这个类 B 与会话分离。
为了让我们访问这个 B 类,我们必须在 A 类的 Hibernate 映射文件中指定lazy=false 属性。例如,
<many-to-one name="classA"
class="classB"
lazy="false">
<column name="classb_id"
sql-type="bigint(10)"
not-null="true"/>
</many-to-one>
好吧,终于知道我失职的地方了。我错误地认为我应该将每个 DAO 方法包装在一个事务中。大错特错!我已经吸取了教训。我已经从所有 DAO 方法中提取了所有事务代码,并在应用程序/管理器层严格设置了事务。这完全解决了我所有的问题。数据在我需要的时候被适当地延迟加载,一旦我提交,就会被打包并关闭。
生活是美好的... :)
@org.springframework.transaction.annotation.Transactional(readOnly=true)
添加到服务层中的方法可以解决问题。 (在该层中,我们正在获取一个实体并将其传递给对 DAO 的另一个调用。)
如果您了解 lazy=false
的影响并且仍希望将其设为默认值(例如,出于原型设计目的),您可以使用以下任何一种:
如果您使用 XML 配置:将 default-lazy="false" 添加到您的
如果您使用注释配置:将 @Proxy(lazy=false) 添加到您的实体类
似乎只有您的 DAO 正在使用会话。因此,每次调用 DAO 方法时都会打开一个新会话,然后关闭。因此程序的执行可以恢复为:
// 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...
默认情况下,实体中的集合和关联是惰性的:它们是按需从数据库中加载的。因此:
sf.getSfSubmissionType().equals( sf2.getSfSubmissionType() )
正在引发异常,因为它请求从数据库进行新的加载,并且与实体加载关联的会话已经关闭。
有两种方法可以解决这个问题:
创建一个会话来包含我们所有的代码。因此,这意味着更改您的 DAO 内容以避免打开第二个会话
创建一个会话,然后在断言之前将您的实体更新(即重新连接)到该会话。 session.update(对象);
默认情况下,所有 one-to-many
和 many-to-many
关联在第一次访问时都会延迟获取。
在您的用例中,您可以通过将所有 DAO 操作包装到一个逻辑事务中来解决此问题:
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;
}
});
另一种选择是在加载实体时获取所有 LAZY 关联,以便:
SecurityFiling sf2 = sfdao.read( sf.getId() );
也应该获取 LAZY submissionType
:
select sf
from SecurityFiling sf
left join fetch.sf.submissionType
这样,您可以急切地获取所有惰性属性,并且您也可以在 Session 关闭后访问它们。
您可以获取尽可能多的 [one|many]-to-one
关联和一个“[one|many]-to-many”列表关联(因为运行笛卡尔积)。
要初始化多个“[one|many]-to-many”,您应该在加载根实体后立即使用 Hibernate.initialize(collection)。
如果您手动管理 Hibernate 会话,您可能需要在此处查看 sessionFactory.getCurrentSession() 和相关文档:
http://www.hibernate.org/hib_docs/v3/reference/en/html/architecture-current-session.html
我认为 Piko 在他的回复中表示有 hbm 文件。我有一个名为 Tax.java 的文件。映射信息保存在 hbm(=hibernate 映射)文件中。在 class 标签中有一个属性叫做lazy。将该属性设置为 true。以下 hbm 示例显示了将惰性属性设置为 false 的方法。
`身份证...'
如果您使用的是注释,请查看休眠文档。 http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/
我希望这有帮助。
对惰性字段使用 Hibernate.initialize
如果您使用 Spring 和 JPA 注释,避免在延迟初始化中出现会话问题的最简单方法是重放:
@PersistenceContext
至
@PersistenceContext(type = PersistenceContextType.EXTENDED)
不定期副业成功案例分享
lazy=false
用于所有 dao 级别,但事实证明应用程序性能因此而变慢,尝试设置lazy=true
但现在抛出了lazyException,任何关于如何解决此问题的建议。