@Transactional(readOnly = true)导致LazyInitializationException

时间:2012-10-09 12:55:46

标签: spring hibernate transactions

我与链接表中的其他列有多对多的关系。我已经以一种方式配置它,让拥有的一方吸引孩子们渴望(所以我没有得到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。有点令人困惑,因为我没有看到为什么这会影响加载哪些数据的原因?


编辑:

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

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

@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中的一个问题。

3 个答案:

答案 0 :(得分:5)

这只是哇。我开始明白为什么有些人讨厌ORM ...只是觉得我经常花费数小时来解决一个奇怪的问题而且解决方案是一组非常具体的注释+一些代码来解决所说的局限性注释

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

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Structure getStructure() {
    return structure;
}

在只读事务中,不会发生此提取。我猜是因为如果你不能改变任何东西,你也不必删除孤儿,因此在只读tx中不需要这个设置背后的逻辑所需的任何数据。

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

实际上已经提到了真正的解决方案。只需按原样保留配置,但在服务层中明确加载所需的关系:

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

答案 1 :(得分:0)

也许你可以把

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

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

答案 2 :(得分:0)

两件事:-

延迟获取可在Collections Interface上使用。由于...

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Structure getStructure() {
    return structure;
}

...这不是一个收集接口(就像List<Structure>那样,它将以Eager提取模式进行提取。

将服务方法设为事务性。从dao层获取后,您的结构似乎不再使用NEVER刷新模式分离。我猜这是潜在的ORM问题。