【发布时间】:2015-08-04 19:29:18
【问题描述】:
请不要建议我为此使用交易注释。
我遇到了一个似乎与 Spring 处理事务有关的错误。
请看这两个测试用例,cmets在代码中:
以实体类为例:
@Entity
public class Person{
@Id
String name;
}
使用的一些方法:
public TransactionStatus requireTransaction() {
TransactionTemplate template = new TransactionTemplate();
template.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
return getTransactionManager().getTransaction(template);
}
public Session session() {
return getRepository().session();
}
public PlatformTransactionManager getTransactionManager() {
return getRepository().getTransactionManager();
}
这是第一个测试,testA();
@Test
public void testA() throws InterruptedException {
// We create the first transaction
TransactionStatus statusOne = requireTransaction();
// Create person one
Person pOne = new Person();
pOne.name = "PersonOne";
session().persist(pOne);
// ---> 111) NOTE! We do not commit! Intentionally!
// We requireTransaction again. We should be getting the same transaction status.
TransactionStatus statusTwo = requireTransaction();
if ( !statusTwo.isNewTransaction() ) {
System.out.println("isNewTransaction: false! As expected! Meaning we are getting the original transaction status!");
}
// Create person two
Person pTwo = new Person();
pTwo.name = "PersonTwo";
session().persist(pTwo);
// We will now be committing statusTwo which should actually be the first one, statusOne,
// since we are using propagation required and the previous transaction was never committed
// or rolledback or completed in any other fashion!
getTransactionManager().commit(statusTwo);
// !!!!!!! However !!!!!! Nothing is actually written to the database here!
// This must be a bug. It existed on Spring 4.0.4 and I have upgraded to 4.2.0 and still the same thing happens!
// Lets go on to the next test. testB() below.
// If we now, at 111) instead do, let me repeat the entire logic:
}
这里是第二个测试,testA();
@Test
public void testB() throws InterruptedException {
// We create the first transaction
TransactionStatus statusOne = requireTransaction();
Person pOne = new Person();
pOne.name = "PersonOne";
session().persist(pOne);
// -----> 111) NOW WE ARE COMMITTING INSTEAD, SINCE WE ARE ALMOST FORCED TO BUT DO NOT WANT TO
getTransactionManager().commit(statusOne);
// ----> 222) HOWEVER, NOW WE WILL NOT BE ABLE TO ROLLBACK THIS AT A LATER POINT
// We requireTransaction again. We should be getting A NEW transaction status.
TransactionStatus statusTwo = requireTransaction();
if ( statusTwo.isNewTransaction() ) {
System.out.println("isNewTransaction: true! As expected! Meaning we are getting a new transaction status!");
}
Person pTwo = new Person();
pTwo.name = "PersonTwo";
session().persist(pTwo);
getTransactionManager().commit(statusTwo);
// Now we will have two instances in the database, as expected.
// If we instead of committing statusTwo would have done:
// getTransactionManager().rollback(statusTwo)
// then only the last one will be rolledback which is not desired!
// Why are we forced to commit the first one to have any effect on future transactions!
// Delegation will not work like this!
}
说清楚了吗?
这显然是一个错误,不是吗?
为什么带有 PROPAGATION_REQUIRED 的 requireTransaction 的目的除了破坏同一个线程的未来提交之外?
为什么 testA() 中的 statusTwo 提交不足以同时提交第一个的工作?
应该以其他方式完成吗?我觉得不会吧?错误!
编辑 对于那些建议我使用执行方法的人,很好:
public PlatformTransactionManager getTransactionManager() {
return /** implement this **/ ;
}
@Test
public void testAA() throws InterruptedException {
insertPerson1();
insertPerson2();
}
public void requireTransaction(TransactionCallback<Object> action) {
TransactionTemplate template = new TransactionTemplate(getTransactionManager());
template.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
template.execute(action);
}
public void insertPerson1() {
requireTransaction(new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus status) {
Person pOne = new Person();
pOne.name = "PersonOne";
session().persist(pOne);
return null;
}
});
}
public void insertPerson2() {
requireTransaction(new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus status) {
Person pTwo = new Person();
pTwo.name = "PersonTwo";
session().persist(pTwo);
if ( true ) {
status.setRollbackOnly();
// throw new RuntimeException("aaaaaaa");
}
return null;
}
});
}
在 insertPerson2 上,即使我设置回滚或抛出异常,第一个人仍然被插入!
这意味着,不是一个共享事务,而是两个独立的事务。
【问题讨论】:
-
您的帖子显示了很多代码,但恐怕不是相关部分。你在用什么
PlatformTransactionManager?不要自己调用低级 API(您显然不应该这样做),而是重写您的测试以使用TransactionTemplate#execute方法。如果问题仍然存在,请在某处发布示例项目,我会看看。 Spring Framework 中的事务管理已经相当成熟,并且在 4.x 行中没有太大变化。该代码是否适用于以前版本的 Spring?
标签: spring hibernate transactions spring-transactions