@Transactional(readOnly = true)会导致LazyInitializationException

我与链接表中的附加列有多对多关系。 我已经配置它的方式,拥有一方提取孩子渴望(所以我没有得到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。 有点令人困惑,因为我没有看到任何理由为什么这会影响哪些数据被加载?


编辑:

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

拥有方(例如Compound包含结构):

@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中看起来像一个问题。


这只是哇。 我开始明白为什么有些人讨厌ORMs ......就像我不断花费数小时解决一个奇怪的问题,解决方案是一组非常具体的注释+一些代码来解决上述限制注释。

首先,为什么会发生这种情况(为什么使用哪种注释的含义,而不是用逻辑意义来说,这是使用常识的实际问题,这是无用的,只有试错法才有用)。 在拥有的方面,在@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可能不方便。

链接地址: http://www.djcxy.com/p/64463.html

上一篇: @Transactional(readOnly = true) leads to LazyInitializationException

下一篇: Specific Template Friendship in C++