如何使用多个@Transactional 注解的方法?
How do I use multiple @Transactional annotated methods?
我有一个 Spring-boot 项目,其中有一个带有 2 个 @Transactional
注释方法的服务 bean。
这些方法执行只读 JPA(休眠)操作以从 HSQL 文件数据库中获取数据,同时使用实体中的 JPA 存储库和延迟加载的 getter。
我还有一个处理命令的 cli bean(使用 PicoCLI)。从这些命令之一,我尝试调用两个 @Transactional
注释方法,但在执行第二种方法期间出现以下错误:
org.hibernate.LazyInitializationException - could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:602)
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:217)
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:581)
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:148)
at org.hibernate.collection.internal.PersistentSet.iterator(PersistentSet.java:188)
at java.util.Spliterators$IteratorSpliterator.estimateSize(Spliterators.java:1821)
at java.util.Spliterator.getExactSizeIfKnown(Spliterator.java:408)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:566)
at <mypackage>.SomeImpl.getThings(SomeImpl.java:<linenr>)
...
如果我用 @Transactional
本身标记调用两个 @Transactional
注释方法的方法,该代码似乎可以工作(因为我认为现在只有 1 个顶级事务?)。
我只是想知道为什么我不能在单个会话中启动多个事务,或者如果有 none,为什么第二个事务不启动新会话。
所以我的问题是:
- 这是否与 hibernate 如何启动会话、事务如何关闭会话或与 HSQL 数据库相关的任何事情有关?
- 添加包含事务是否是解决问题的正确方法
或者这只是在对抗症状?
- 从一个方法中使用多个
@Transactional
注释方法的最佳方法是什么?
编辑:我想明确表示,我不会在事务方法之外公开实体,所以从表面上看,这两种事务方法应该相互独立工作。
EDIT2:更多说明:交易方法需要在 api 中可用,并且 api 的用户应该能够调用多个这些交易方法,而无需使用交易注释,没有得到 LazyInitializationException
Api:
public interface SomeApi {
List<String> getSomeList();
List<Something> getThings(String somethingGroupName);
}
实施:
public class SomeImpl implements SomeApi {
@Transactional
public List<String> getSomeList() {
return ...; //Do jpa stuff to get the list
}
@Transactional
public List<Something> getThings(String somethingGroupName) {
return ...; //Do other jpa stuff to get the result from the group name
}
}
第 3 方的使用(他们可能不知道什么是事务性):
public someMethod(String somethingGroupName) {
...
SomeApi someApi = ...; // Get an implementation of the api in some way
List<String> someList = someApi.someList();
if (someList.contains(somethingGroupName) {
System.out.println(someApi.getThings(somethingGroupName));
}
...
}
您似乎在交易结束后从您的实体访问一些未初始化的数据。在那种情况下,持久性提供程序可能会抛出延迟初始化异常。
如果您需要检索一些未随实体急切加载的信息,您可以使用以下两种策略之一:
- 调用方法也用@Transactional注解,就像你做的那样:它不会为每次调用启动一个新的事务,而是使打开的事务处于活动状态,直到你的调用方法结束,避免异常;或
- 使用 JOIN FETCH JPQL 习惯用法使被调用的方法急切地加载所需的字段。
事务边界需要对您的场景进行一些分析。请阅读 并搜索更好的书籍或教程来掌握它。可能只有你才能恰当地定义你的需求。
我发现开箱即用的 hibernate 不会重新打开会话,因此在第一个事务结束后不会启用延迟加载,无论后续的 jpa 语句是否在事务中。然而在 hibernate 中有一个 属性 来启用这个特性:
spring:
jpa:
properties:
hibernate.enable_lazy_load_no_trans: true
这将确保如果没有会话,则会创建一个临时会话。我相信它也会阻止会话在交易后结束,但我不确定。
部分功劳来自其他 Whosebug 问题的以下答案:
警告:在 hibernate 4.1.8 中存在一个可能导致数据丢失的错误!确保您使用的是 4.2.12、4.3.5 或更新版本的 hibernate。参见:https://hibernate.atlassian.net/browse/HHH-7971.
我有一个 Spring-boot 项目,其中有一个带有 2 个 @Transactional
注释方法的服务 bean。
这些方法执行只读 JPA(休眠)操作以从 HSQL 文件数据库中获取数据,同时使用实体中的 JPA 存储库和延迟加载的 getter。
我还有一个处理命令的 cli bean(使用 PicoCLI)。从这些命令之一,我尝试调用两个 @Transactional
注释方法,但在执行第二种方法期间出现以下错误:
org.hibernate.LazyInitializationException - could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:602)
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:217)
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:581)
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:148)
at org.hibernate.collection.internal.PersistentSet.iterator(PersistentSet.java:188)
at java.util.Spliterators$IteratorSpliterator.estimateSize(Spliterators.java:1821)
at java.util.Spliterator.getExactSizeIfKnown(Spliterator.java:408)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:566)
at <mypackage>.SomeImpl.getThings(SomeImpl.java:<linenr>)
...
如果我用 @Transactional
本身标记调用两个 @Transactional
注释方法的方法,该代码似乎可以工作(因为我认为现在只有 1 个顶级事务?)。
我只是想知道为什么我不能在单个会话中启动多个事务,或者如果有 none,为什么第二个事务不启动新会话。
所以我的问题是:
- 这是否与 hibernate 如何启动会话、事务如何关闭会话或与 HSQL 数据库相关的任何事情有关?
- 添加包含事务是否是解决问题的正确方法 或者这只是在对抗症状?
- 从一个方法中使用多个
@Transactional
注释方法的最佳方法是什么?
编辑:我想明确表示,我不会在事务方法之外公开实体,所以从表面上看,这两种事务方法应该相互独立工作。
EDIT2:更多说明:交易方法需要在 api 中可用,并且 api 的用户应该能够调用多个这些交易方法,而无需使用交易注释,没有得到 LazyInitializationException
Api:
public interface SomeApi {
List<String> getSomeList();
List<Something> getThings(String somethingGroupName);
}
实施:
public class SomeImpl implements SomeApi {
@Transactional
public List<String> getSomeList() {
return ...; //Do jpa stuff to get the list
}
@Transactional
public List<Something> getThings(String somethingGroupName) {
return ...; //Do other jpa stuff to get the result from the group name
}
}
第 3 方的使用(他们可能不知道什么是事务性):
public someMethod(String somethingGroupName) {
...
SomeApi someApi = ...; // Get an implementation of the api in some way
List<String> someList = someApi.someList();
if (someList.contains(somethingGroupName) {
System.out.println(someApi.getThings(somethingGroupName));
}
...
}
您似乎在交易结束后从您的实体访问一些未初始化的数据。在那种情况下,持久性提供程序可能会抛出延迟初始化异常。
如果您需要检索一些未随实体急切加载的信息,您可以使用以下两种策略之一:
- 调用方法也用@Transactional注解,就像你做的那样:它不会为每次调用启动一个新的事务,而是使打开的事务处于活动状态,直到你的调用方法结束,避免异常;或
- 使用 JOIN FETCH JPQL 习惯用法使被调用的方法急切地加载所需的字段。
事务边界需要对您的场景进行一些分析。请阅读
我发现开箱即用的 hibernate 不会重新打开会话,因此在第一个事务结束后不会启用延迟加载,无论后续的 jpa 语句是否在事务中。然而在 hibernate 中有一个 属性 来启用这个特性:
spring:
jpa:
properties:
hibernate.enable_lazy_load_no_trans: true
这将确保如果没有会话,则会创建一个临时会话。我相信它也会阻止会话在交易后结束,但我不确定。
部分功劳来自其他 Whosebug 问题的以下答案:
警告:在 hibernate 4.1.8 中存在一个可能导致数据丢失的错误!确保您使用的是 4.2.12、4.3.5 或更新版本的 hibernate。参见:https://hibernate.atlassian.net/browse/HHH-7971.