TransactionAttribute注释(@REQUIRES_NEW)被忽略

我有一个问题,两个单独的事务以与它们实际执行的相反的顺序刷新到数据库。

这是商业案例:RemoteJob-RemoteJobEvent一对多关系。 每次创建新事件时,都会获取一个时间戳,并在RemoteJob和RemoteJobEvent的lastModified字段中设置,并保留两个记录(一个更新+一个插入)。

这是代码中的样子:

class Main { @TransactionAttribute(TransactionAttributeType.REQUIRED) public void mainMethod(...) { RemoteJob job = remoteJobDAO.findById(...); // ... addEvent(job, EVENT_CODE_10); // Here the separate transaction should have ended and its results // permanently visible in the database. We refresh the job then // to update it with the added event: remoteJobDAO.refresh(job); // calls EntityManager.refresh() // ... boolean result = helper.addEventIfNotThere(job); } // Annotation REQUIRES_NEW here to enforce a new transaction; the // RemoteJobDAO.newEvent() has REQUIRED. @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public void addEvent(RemoteJob job, RemoteJobEvent event) { remoteJobDAO.newEvent(job, event); } } class Helper { @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public boolean addEventIfNotThere(RemoteJob job) { // This loads the job into the persistence context associated with a new transaction. job = remoteJobDAO.findById(job.getId()); // Locking the job record – this method is using as a semaphore by 2 threads, // we need to make sure only one of them completes it. remoteJobDAO.lockJob(job, LockModeType.WRITE); // Refreshing after locking to be certain that we have current data. remoteJobDAO.refresh(job); // ... here comes logic for checking if EVENT_CODE_11 is not already there if (/* not yet present */) { remoteJobDAO.newEvent(job, EVENT_CODE_11); } return ...; // true - event 11 was there, false - this execution added it. } } 

总结一下:在mainMethod()我们已经处于事务上下文中。 然后,我们将其挂起以生成一个新事务,以在方法addEvent()创建EVENT_CODE_10。 返回此方法后,我们应该为每个人提交并显示其结果(但需要刷新mainMethod()的上下文)。 最后,我们进入addEventIfNotThere()方法(再次出现一个新事务),事实certificate没有人添加EVENT_CODE_11,所以我们这样做并返回。 因此,数据库中应包含两个事件。

这就是麻烦:OpenJPA似乎在addEventIfNotThere()完成之后立即刷新事件添加事务! 更重要的是,它以错误的顺序执行,并且版本列值清楚地表明第二个事务没有前一个事务的结果的信息,即使第一个应该已经提交(注意日志顺序,lastModified字段值)和事件代码):

 2011-07-08T10:45:51.386 [WorkManager.DefaultWorkManager : 7] TRACE [openjpa.jdbc.SQL] -  executing prepstmnt 1859546838 INSERT INTO RemoteJobEvent (id, eventCode, lastModified, version, remotejobid) VALUES (?, ?, ?, ?, ?) [params=(long) 252, (short) 11, (Timestamp) 2011-07-08 10:45:51.381, (int) 1, (long) 111] 2011-07-08T10:45:51.390 [WorkManager.DefaultWorkManager : 7] TRACE [openjpa.jdbc.SQL] -  executing prepstmnt 60425114 UPDATE RemoteJob SET lastModified = ?, version = ? WHERE id = ? AND version = ? [params=(Timestamp) 2011-07-08 10:45:51.381, (int) 3, (long) 111, (int) 2] 2011-07-08T10:45:51.401 [WorkManager.DefaultWorkManager : 7] TRACE [openjpa.jdbc.SQL] -  executing prepstmnt 923940626 INSERT INTO RemoteJobEvent (id, eventCode, lastModified, version, remotejobid) VALUES (?, ?, ?, ?, ?) [params=(long) 253, (short) 10, (Timestamp) 2011-07-08 10:45:51.35, (int) 1, (long) 111] 2011-07-08T10:45:51.403 [WorkManager.DefaultWorkManager : 7] TRACE [openjpa.jdbc.SQL] -  executing prepstmnt 1215645813 UPDATE RemoteJob SET lastModified = ?, version = ? WHERE id = ? AND version = ? [params=(Timestamp) 2011-07-08 10:45:51.35, (int) 3, (long) 111, (int) 2] 

当然,这会产生一个OptimisticLockException – 它在两个环境中的行为方式相同:使用Apache Derby / Tomcat / Atomikos Transaction Essentials进行测试,并使用WebSphere 7.0 / Oracle 11进行目标。

我的问题是:这怎么可能,交易边界不受尊重? 据我所知,JPA提供商可以一个事务中自由选择SQL排序,但它不能重新排序整个事务,可以吗?

有关我们环境的更多信息:提供的代码是Spring 3.0.5 JMS消息处理程序(DefaultMessageListenerContainer)的一部分; Spring也用于bean注入,但基于注释的事务管理使用系统事务管理器(Websphere的/ Atomikos,如上所述),这就是使用EJB3而不是Spring事务注释的原因。

我希望这引起一些兴趣,在这种情况下,如果需要,我很乐意提供更多信息。

我没有读过关于Spring代理是如何工作的,也就是那些负责基于注释的事务支持的人。

事实certificate,当从同一个类中调用方法时,将忽略addEvent的REQUIRES_NEW注释。 Spring事务代理在这种情况下不起作用,所以代码在当前事务中运行 – 这是完全错误的,因为它在调用helper.addEventIfNotThere()完成后结束(长)。 另一方面,后一种方法从另一个类调用的,因此REQUIRES_NEW真正启动并作为单独的事务提交。

我将addEvent()方法移动到一个单独的类,问题消失了。 另一种解决方案可能是改变配置的工作方式; 更多信息: Spring Transaction Management参考 。

另一种选择是使用AspectJ编写Spring的AnnotationTransactionAspect,如Spring文档的第11.5.9节所述