即使提供了@EnableTransactionManagement,@Transactional 注释也不会回滚 RuntimeException
@Transactional annotation does not rollback RuntimeException even if @EnableTransactionManagement is provided
我有以下应用程序设置:
@SpringBootApplication
@EnableTransactionManagement
public class MyApp extends SpringBootServletInitializer {
...
}
带有一个 class,它具有以下内容:
public class DoStaff {
public void doStaffOnAll(List<MyObject> myObjects) {
for (int i=0; i<myObjects.size(); i++) {
try {
doStaffOnSingle(myObjects.get(i), i);
} catch (Exception e) {
e.printStrackTrace();
}
}
}
@Transactional
public void doStaffOnSingle(MyObject myObject, int i) {
repository.save(myObject);
if (i%2==0) {
throw new RuntimeException();
}
}
}
因此,如果我用 MyObject
的列表调用 DoStaff.doStaffOnAll
,代码会保存列表中的所有元素,但还会为每个第二个元素抛出 运行time 异常。
由于 doStaffOnSingle
有 @Transactional
注释,我预计每隔一个元素都会回滚。
但是如果我 运行 这段代码,每个元素都会成功保存在数据库中。这是为什么?我做错了什么?
In proxy
mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation (in effect, a method within the target object calling another method of the target object) does not lead to an actual transaction at runtime even if the invoked method is marked with @Transactional
. Also, the proxy must be fully initialized to provide the expected behavior, so you should not rely on this feature in your initialization code (that is, @PostConstruct
).
将 doStaffOnAll()
移动到不同的 Spring 组件,它将起作用。
或更改为aspectj
模式。
我建议移动方法,并设计代码以使事务边界清晰明了,即 class 上的所有 public 方法都启动一个事务,或者 class 开始交易。
您的交易边界在哪里应该始终非常清楚,例如在分层设计中,您通常会使 @Service
层也成为事务层,即从更高层到服务层的任何调用都是原子事务。
@Transactional
注释之所以能够发挥作用,是因为有一个代理对象。
由于您直接调用该方法,因此您不会获得那种魔力。在 doStaffOnAll
方法中,您直接调用 doStaffOnSingle
方法。因此,没有添加任何交易行为。
尝试使用自调用来调用该方法。
@Service
public class DoStaff {
@Autowired
private DoStaff doStaff;
public void doStaffOnAll(List<MyObject> myObjects) {
for (int i=0; i<myObjects.size(); i++) {
doStaff.doStaffOnSingle(..) // invoke like this
}
}
@Transactional
public void doStaffOnSingle(MyObject myObject, int i) {
}
}
Since the doStaffOnSingle has @Transactional annotation, I would
expect that every second element will be rolled back.
默认事务模式将提交所有内容或不提交任何内容。我想你会想要使用 REQUIRES_NEW 传播。
在此处查看支持的传播类型。
我有以下应用程序设置:
@SpringBootApplication
@EnableTransactionManagement
public class MyApp extends SpringBootServletInitializer {
...
}
带有一个 class,它具有以下内容:
public class DoStaff {
public void doStaffOnAll(List<MyObject> myObjects) {
for (int i=0; i<myObjects.size(); i++) {
try {
doStaffOnSingle(myObjects.get(i), i);
} catch (Exception e) {
e.printStrackTrace();
}
}
}
@Transactional
public void doStaffOnSingle(MyObject myObject, int i) {
repository.save(myObject);
if (i%2==0) {
throw new RuntimeException();
}
}
}
因此,如果我用 MyObject
的列表调用 DoStaff.doStaffOnAll
,代码会保存列表中的所有元素,但还会为每个第二个元素抛出 运行time 异常。
由于 doStaffOnSingle
有 @Transactional
注释,我预计每隔一个元素都会回滚。
但是如果我 运行 这段代码,每个元素都会成功保存在数据库中。这是为什么?我做错了什么?
In
proxy
mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation (in effect, a method within the target object calling another method of the target object) does not lead to an actual transaction at runtime even if the invoked method is marked with@Transactional
. Also, the proxy must be fully initialized to provide the expected behavior, so you should not rely on this feature in your initialization code (that is,@PostConstruct
).
将 doStaffOnAll()
移动到不同的 Spring 组件,它将起作用。
或更改为aspectj
模式。
我建议移动方法,并设计代码以使事务边界清晰明了,即 class 上的所有 public 方法都启动一个事务,或者 class 开始交易。
您的交易边界在哪里应该始终非常清楚,例如在分层设计中,您通常会使 @Service
层也成为事务层,即从更高层到服务层的任何调用都是原子事务。
@Transactional
注释之所以能够发挥作用,是因为有一个代理对象。
由于您直接调用该方法,因此您不会获得那种魔力。在 doStaffOnAll
方法中,您直接调用 doStaffOnSingle
方法。因此,没有添加任何交易行为。
尝试使用自调用来调用该方法。
@Service
public class DoStaff {
@Autowired
private DoStaff doStaff;
public void doStaffOnAll(List<MyObject> myObjects) {
for (int i=0; i<myObjects.size(); i++) {
doStaff.doStaffOnSingle(..) // invoke like this
}
}
@Transactional
public void doStaffOnSingle(MyObject myObject, int i) {
}
}
Since the doStaffOnSingle has @Transactional annotation, I would expect that every second element will be rolled back.
默认事务模式将提交所有内容或不提交任何内容。我想你会想要使用 REQUIRES_NEW 传播。
在此处查看支持的传播类型。