简体   繁体   English

@Transactional(readOnly = true)导致LazyInitializationException

[英]@Transactional(readOnly = true) leads to 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. 我已经以一种拥有方提取孩子的方式配置它(因此我没有得到LazyInitializationException ),而在相反的方向它是懒惰的。 This works. 这有效。

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 : 我现在想要调整事务(在DAO和Service类的类级别上只有@Transactional之前。我将方法getById设置为readOnly = true

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

After this change I get a LazyInitializationException in following snippet: 在此更改后,我在以下代码段中获得了一个LazyInitializationException

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

If I remove (readOnly = true) this works! 如果我删除(readOnly = true)这个工作! Can anyone explain this behavior? 谁能解释这种行为? I use Spring + Hibernate. 我使用Spring + Hibernate。 Kind of confusing as I don't see any reason why this should affect which data is loaded? 有点令人困惑,因为我没有看到任何理由为什么这会影响加载哪些数据?


EDIT: 编辑:

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<>();

Belongs to side: 属于一边:

@OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.structure",
cascade = CascadeType.ALL)
@OrderBy("pk.compound.id ASC")
private List<CompoundComposition> occurence;

Many-To-One in @Embeddable ID class @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;
}

EDIT 2: 编辑2:

Stack Trace 堆栈跟踪

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]

EDIT 3: 编辑3:

Also see my comment: 另见我的评论:

Log is very different with readOnly and it is missing the part were the relations are loaded, eg. 日志与readOnly非常不同,并且缺少了关系被加载的部分,例如。 some selects are missing in the log. 日志中缺少某些选项。

EDIT 4: 编辑4:

So I tired with a basic DriverManagerDataSource and no Connection pool. 所以我厌倦了一个基本的DriverManagerDataSource而没有连接池。 The issue is exactly the same. 问题完全一样。 For me looks like an issue in Hibernate. 对我来说,看起来像是Hibernate中的一个问题。

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

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.). 在拥有方面,在@OneToMany中我有orphanRemoval = true(我发现它是一致性所必需的。人们会认为数据库约束应该处理它......只是众多事情中的一个可以让你发疯。)。 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;
}

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. 我猜是因为如果你不能改变任何东西,你也不必删除孤儿,因此在只读tx中不需要任何这个设置背后的逻辑所需的数据。

So the obvious solution would be in above relation to change to FetchType.EAGER. 因此,显而易见的解决方案是与FetchType.EAGER的更改有关。 Wrong! 错误! If you do that you will not be able to update the owning side (Compound) using session.merge. 如果这样做,您将无法使用session.merge更新拥有方(复合)。 This will lead to a StackOverFlowError. 这将导致StackOverFlowError。

The real solution was actually already mentioned. 实际上已经提到了真正的解决方案。 Just leave the config as is but explicitly load the desired relations in the Service layer: 只需按原样保留配置,但在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;
}

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. 这看起来效率不高,似乎也打破了SQL的工作原理。 But then I'm in the lucky position that in most cases CompoundComposition will contain only 1 or 2 elements. 但后来我处于幸运的位置,在大多数情况下,CompoundComposition只包含1或2个元素。

Perhaps you could put 也许你可以放

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

in the body of the getById() method, so that the lazy loading happens within the transaction. getById()方法的getById() ,以便在事务中发生延迟加载。 I realize in this case you'd have to loop over i which might be inconvenient. 我意识到在这种情况下你必须循环i可能不方便。

Two things :- 两件事情 :-

Lazy fetch works on Collections Interface. 延迟提取适用于集合接口。 Since ... 从......

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

... this is not a collection interface (like List<Structure> would have been), it will be fetched in Eager fetch mode. ...这不是一个集合接口(如List<Structure> ),它将以Eager fetch模式获取。

Make service method as transactional. 使服务方法成为事务性的。 It seems that after fetching from the dao layer, your structure is detached with NEVER flush mode. 似乎从dao层获取后,您的结构将以NEVER flush模式分离。 This is the underlying ORM issue I guess. 这是我猜的潜在ORM问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 @Transactional 不适用于 readOnly = true - @Transactional not working with readOnly = true @Transactional和@Transactional(readOnly = true)有什么区别 - what is the difference between @Transactional and @Transactional(readOnly=true) Spring @Transactional(readOnly = true)恢复属性? - Spring @Transactional(readOnly=true) revert the properties? 使用@Transactional(readOnly = true) 有什么好处? - What are advantages of using @Transactional(readOnly = true)? 弹簧靴。 @Transactional 方法调用 @Transactional(readonly=true) 方法 - Spring Boot. @Transactional method calling a @Transactional(readonly=true) method 春天:@Transactional与readonly = true不调用conn.setReadOnly(true) - Spring: @Transactional with readonly=true not call conn.setReadOnly(true) 当@Transactional(readonly=true) 方法调用@Transactional(readonly=false) 方法时会发生什么,反之亦然? - What happens when @Transactional(readonly=true) method calls @Transactional(readonly=false) method and vice versa? LazyInitializationException 即使使用@Transactional - LazyInitializationException even with @Transactional LazyInitializationException和@Transactional无法正常工作 - LazyInitializationException and @Transactional not working @Transactional 上的 Spring Hibernate LazyInitializationException - Spring Hibernate LazyInitializationException on @Transactional
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM