[英]@Transactional(readOnly = true) leads to LazyInitializationException
我与链接表中的其他列有多对多的关系。 我已经以一种拥有方提取孩子的方式配置它(因此我没有得到LazyInitializationException
),而在相反的方向它是懒惰的。 这有效。
我现在想要调整事务(在DAO和Service类的类级别上只有@Transactional
之前。我将方法getById
设置为readOnly = true
:
@Transactional(readOnly = true)
public Compound getById(Long id) {
return compoundDAO.getById(id);
}
在此更改后,我在以下代码段中获得了一个LazyInitializationException
:
Compound compound = compoundService.getById(6L);
Structure structure = compound.getComposition().get(0).getStructure();
System.out.println("StructureId: "+ structure.getId()); // LazyInitializationException
如果我删除(readOnly = true)
这个工作! 谁能解释这种行为? 我使用Spring + Hibernate。 有点令人困惑,因为我没有看到任何理由为什么这会影响加载哪些数据?
编辑:
关系定义的片段。 这是一个多对多链接表中的列。
拥有方(例如化合物包含结构):
@OneToMany(fetch = FetchType.EAGER, mappedBy = "pk.compound",
cascade = CascadeType.ALL, orphanRemoval = true)
@OrderBy("pk.structure.id ASC")
private List<CompoundComposition> composition = new ArrayList<>();
属于一边:
@OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.structure",
cascade = CascadeType.ALL)
@OrderBy("pk.compound.id ASC")
private List<CompoundComposition> occurence;
@Embeddable ID类中的多对一
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Compound getCompound() {
return compound;
}
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Structure getStructure() {
return structure;
}
编辑2:
堆栈跟踪
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:165) ~[hibernate-core-4.1.7.Final.jar:4.1.7.Final]
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:272) ~[hibernate-core-4.1.7.Final.jar:4.1.7.Final]
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185) ~[hibernate-core-4.1.7.Final.jar:4.1.7.Final]
at org.bitbucket.myName.myApp.entity.Structure_$$_javassist_0.getId(Structure_$$_javassist_0.java) ~[classes/:na]
at org.bitbucket.myName.myApp.App.main(App.java:31) ~[classes/:na]
编辑3:
另见我的评论:
日志与readOnly非常不同,并且缺少了关系被加载的部分,例如。 日志中缺少某些选项。
编辑4:
所以我厌倦了一个基本的DriverManagerDataSource而没有连接池。 问题完全一样。 对我来说,看起来像是Hibernate中的一个问题。
这只是哇。 我开始明白为什么有些人讨厌ORM ...只是觉得我经常花费数小时来解决一个奇怪的问题而且解决方案是一组非常具体的注释+一些代码来解决所说的局限性注释。
首先要解释为什么会发生这种情况(为什么意味着哪些注释,但不是在逻辑意义上,这是使用常识的实际问题是无用的。只有试验和错误有帮助)。 在拥有方面,在@OneToMany中我有orphanRemoval = true(我发现它是一致性所必需的。人们会认为数据库约束应该处理它......只是众多事情中的一个可以让你发疯。)。 似乎如果事务不是只读的,那么这个设置会导致一些数据被提取,即使它是懒惰的,即:
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Structure getStructure() {
return structure;
}
在只读事务中,不会发生此提取。 我猜是因为如果你不能改变任何东西,你也不必删除孤儿,因此在只读tx中不需要任何这个设置背后的逻辑所需的数据。
因此,显而易见的解决方案是与FetchType.EAGER的更改有关。 错误! 如果这样做,您将无法使用session.merge更新拥有方(复合)。 这将导致StackOverFlowError。
实际上已经提到了真正的解决方案。 只需按原样保留配置,但在Service层中显式加载所需的关系:
@Transactional(readOnly = true)
@Override
public Compound getById(Long id) {
Compound compound = compoundDAO.getById(id);
for (CompoundComposition composition : compound.getComposition()){
Hibernate.initialize(composition.getStructure());
}
return compound;
}
我承认我倾向于陷入过早的优化陷阱。 这看起来效率不高,似乎也打破了SQL的工作原理。 但后来我处于幸运的位置,在大多数情况下,CompoundComposition只包含1或2个元素。
也许你可以放
value.getComposition().get(i).getStructure();
在getById()
方法的getById()
,以便在事务中发生延迟加载。 我意识到在这种情况下你必须循环i
可能不方便。
两件事情 :-
延迟提取适用于集合接口。 从......
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Structure getStructure() {
return structure;
}
...这不是一个集合接口(如List<Structure>
),它将以Eager fetch模式获取。
使服务方法成为事务性的。 似乎从dao层获取后,您的结构将以NEVER flush模式分离。 这是我猜的潜在ORM问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.