@Transactional(readOnly = true)导致LazyInitializationException [英] @Transactional(readOnly = true) leads to LazyInitializationException

查看:56
本文介绍了@Transactional(readOnly = true)导致LazyInitializationException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我与链接表中的其他列有多对多关系.我对它的配置方式是拥有方获取渴望的孩子(这样我就不会得到 LazyInitializationException ),而在相反的方向上它是惰性的.这行得通.

I have a many-to-many relation with an additional column in the link table. I've configured it in a way that the owning side fetches children eager (so I don't get LazyInitializationException) and in the opposite direction it is lazy. This works.

我现在想微调交易(在DAO和Service类的类级别上只有 @Transactional 之前.我将方法 getById 设置为readOnly = true :

I now wanted to fine-tune the transactions (before there was just @Transactional on class level of DAO and Service classes. I set method getById to readOnly = true:

@Transactional(readOnly  = true)
public Compound getById(Long id) {
    return compoundDAO.getById(id);
}

此更改后,我在以下代码段中得到了 LazyInitializationException :

After this change I get a LazyInitializationException in following snippet:

Compound compound = compoundService.getById(6L);        
Structure structure = compound.getComposition().get(0).getStructure();
System.out.println("StructureId: "+ structure.getId()); // LazyInitializationException

如果我删除(readOnly = true),则可以正常运行!谁能解释这种行为?我使用Spring + Hibernate.有点令人困惑,因为我看不到任何原因会影响加载哪些数据?

If I remove (readOnly = true) this works! Can anyone explain this behavior? I use Spring + Hibernate. Kind of confusing as I don't see any reason why this should affect which data is loaded?

关系定义的摘要.这是一个多对多链接表中的一列.

Snippets of relationship definitions. This is a many-to-many with a column in the link table.

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

Owning side (eg Compound contains Structures):

@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类中的一对多

Many-To-One in @Embeddable ID class

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Compound getCompound() {
    return compound;
}

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

堆栈跟踪

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]

另请参阅我的评论:

日志与readOnly完全不同,它缺少加载关系的部分,例如.日志中缺少一些选择.

Log is very different with readOnly and it is missing the part were the relations are loaded, eg. some selects are missing in the log.

因此,我对基本的DriverManagerDataSource和无连接池感到厌倦.问题是完全一样的.对我来说,这似乎是Hibernate中的一个问题.

So I tired with a basic DriverManagerDataSource and no Connection pool. The issue is exactly the same. For me looks like an issue in Hibernate.

推荐答案

这真是太好了.我开始理解为什么有些人讨厌ORM ...只是觉得我经常需要花费数小时来解决一个怪异的问题,而解决方案是一组非常具体的注释+一些代码来解决上述限制注释.

This is just wow. I'm starting to understand why some people hate ORMs...Just feels like I'm constantly having to spend hours to solve a weird issue and the solution is a very specific set of annotations + some code to work around the limitations of said annotations.

为什么会发生这种情况(为什么要使用批注,而不是在逻辑上有意义,这是此处的实际问题,因为使用常识是没有用的.只有反复试验才有帮助).在拥有方面,在@OneToMany中,我具有orphanRemoval = true(我发现这是保持一致性所必需的.人们会认为数据库约束应处理此问题……这只是可能使您发疯的许多原因之一.).看来,如果事务不是只读的,则此设置会导致某些数据被提取,即使它很懒,也就是这里:

First to why this happens (why meaning with which annotations, but not in terms of making logical sense, which is the actual problem here as using common-sense is useless. Only trial and error helps). In the owning side, in @OneToMany I have orphanRemoval = true (which I have found out is required for consistency. one would think database constraints should handle that...just one of the many things that can drive you crazy.). It seems that if the transaction is not read-only, then this setting leads to some data being fetched even so its lazy, namely here:

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

在只读事务中,不会发生此获取.我猜想是因为如果您无法更改任何内容,那么您也将不必删除孤儿,因此在只读tx中不需要此设置背后的逻辑所需的任何数据.

In a read-only transaction, this fetching does not happen. I would guess because if you can't change anything you will also not have to remove orphans and hence any data that the logic behind this setting requires is not needed in a read-only tx.

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

So the obvious solution would be in above relation to change to FetchType.EAGER. Wrong! If you do that you will not be able to update the owning side (Compound) using session.merge. This will lead to a StackOverFlowError.

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

The real solution was actually already mentioned. Just leave the config as is but explicitly load the desired relations in the Service layer:

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

I admit I'm tending to fall in the premature optimization trap. This doesn't look very efficient and also seems to break how SQL works in the first place. But then I'm in the lucky position that in most cases CompoundComposition will contain only 1 or 2 elements.

这篇关于@Transactional(readOnly = true)导致LazyInitializationException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆