Spring @事务性(readOnly=true)导致LazyInitializationException

Spring @事务性(readOnly=true)导致LazyInitializationException,spring,hibernate,transactions,Spring,Hibernate,Transactions,我与链接表中的一个附加列有多对多关系。我将其配置为拥有方获取渴望的子对象(因此我没有得到LazyInitializationException),相反,它是懒惰的。这很有效 现在我想对事务进行微调(在DAO和服务类的类级别上出现@Transactional之前)。我将方法getById设置为readOnly=true: @Transactional(readOnly = true) public Compound getById(Long id) { return compoundDA

我与链接表中的一个附加列有多对多关系。我将其配置为拥有方获取渴望的子对象(因此我没有得到
LazyInitializationException
),相反,它是懒惰的。这很有效

现在我想对事务进行微调(在DAO和服务类的类级别上出现
@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。有点让人困惑,因为我看不出这会影响加载哪些数据的任何原因


编辑:

关系定义的片段。这是一个多对多的链接表,其中包含一列

拥有方(如化合物包含结构):

编辑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:

所以我厌倦了使用基本的DriverManager数据源,而没有连接池。问题完全一样。对我来说,这就像是Hibernate中的问题。

也许你可以

value.getComposition().get(i).getStructure();

getById()的主体中
方法,因此延迟加载发生在事务中。我意识到在这种情况下,您必须循环
I
,这可能会很不方便。

这只是一个惊喜。我开始理解为什么有些人讨厌ORM…只是感觉我经常不得不花费数小时来解决一个奇怪的问题,而解决方案是一个非常复杂的问题cific注释集+一些代码,以克服上述注释的限制

首先是为什么会发生这种情况(为什么注释的意思是什么,但不是在逻辑意义上,这是这里的实际问题,因为使用常识是无用的。只有尝试和错误才有帮助)。在拥有方,在@OneToMany中,我有一个=true(我发现这是一致性所必需的。有人会认为数据库约束应该处理这一点……这只是让你发疯的众多因素之一。)似乎,如果事务不是只读的,那么此设置会导致某些数据被提取,即使它是惰性的,即:

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Structure getStructure() {
    return structure;
}
在只读事务中,这种抓取不会发生。我猜这是因为如果您不能更改任何内容,您也不必删除孤立项,因此在只读事务中不需要此设置背后的逻辑所需的任何数据

因此,显而易见的解决方案是在上面的关系中更改为FetchType.EAGER.Error!如果这样做,您将无法使用session.merge更新拥有方(化合物)。这将导致StackOverflower错误

实际上已经提到了真正的解决方案。只需保持配置不变,但在服务层中显式加载所需的关系:

@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个元素。

两件事:-

延迟获取在集合接口上工作。因为

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Structure getStructure() {
    return structure;
}
…这不是一个收集接口(如
列表
),它将以急切获取模式获取


将服务方法设置为事务性。从dao层获取后,您的结构似乎会以从不刷新模式分离。我想这就是ORM的基本问题。

类级别事务定义是什么?您可以发布模型类的片段吗?并且,您是否仍然在DAO类之首?尝试了不同的版本,从在服务类中使用@Transactional only到在所有DAO类中使用@Transactional only,效果相同。添加只读会导致LazyInitializationException。我不相信您的异常是事务模式的原因,当您调用ws-Lazy初始化异常?stacktrace也会有帮助。前两段总结了我在Hibernate/JPA/ORMs上花了6年左右的时间。
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Structure getStructure() {
    return structure;
}
@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;
}
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Structure getStructure() {
    return structure;
}