今天在进行数据库操作时出现:Transaction rolled back because it has been marked as rollback only异常,刚开始没仔细看一直以为是spring的配置文件,因为我的spring配置事务片段如下:
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="get*" read-only="true" />
<tx:method name="*" />
</tx:attributes>
</tx:advice>
我这里将service的所有get方法定义为只读事务,起初以为是这里的配置文件,所就将get的只读去掉,可仍然出现类似的错误。可见问题不是这个事务配置的问题,经过google一番后,应该是事务被提交了两次导致的错误。再仔细查看代码发现是在service中嵌套调用了另外一个service的更新操作,从而导致了事务被两次提交。代码类似如下:
...
public void update() {
MyObj obj1 = aservice.get(1);
aservice.update(obj1);
Query query = em.createQuery("UPDATE ....");
query.executeUpdate();
}
...
update方法是一个service的方法,在该方法中调用了另外一个aservice的update方法,由于get方法是只读事务,所以第一条语句不会报错,当aservice执行完update后,其实事务已经被提交了,同时spring将该事务标志为rollback only,等到最后执行executeUpdate方法时,由于事务已经提交,所以就会被抛出前面提到的异常。可见,spring在处理事务时,多个service嵌套调用时使用的都是同一个事务,而不是每个不同的方法都使用新事务。
如果要采用spring的线程池进行后台的业务处理的话,尽量把业务方法重构到一个service里面进行调用,否则也会报告上面的错误。
更加详细的资料请参阅 http://openmrs-mailing-list-archives.1560443.n2.nabble.com/transaction-rollback-error-td1659793.html