丁雪丰老师在《玩转Spring全家桶》中第二章Spring的事务抽象中的示例地址:https://github.com/digitalsonic/geektime-spring-family/tree/master/Chapter%202/declarative-transaction-demo,感兴趣的朋友可以下载运行,使用的H2数据库,无需更改配置直接运行。
主要是下面三个方法:
package geektime.spring.data.declarativetransactiondemo;
import com.stark.commons.spring.core.context.ApplicationContextUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@Component
public class FooServiceImpl implements FooService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
@Transactional
public void insertRecord() {
jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('AAA')");
}
@Override
@Transactional(rollbackFor = RollbackException.class)
public void insertThenRollback() throws RollbackException {
jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('BBB')");
throw new RollbackException();
}
@Override
public void invokeInsertThenRollback() throws RollbackException {
insertThenRollback();
}
}
其中invokeInsertThenRollback()方法内部调用了insertThenRollback()方法,但是事务并没有被回滚,因为其走的是内部调用,而Spring实现声明式事务依赖的是代理和增强,代理在运行时会截获对被注解方法的调用,并在方法调用前后添加事务管理的逻辑(例如开启、提交或回滚事务)。内部调用不会调用代理对象的方法,因此事务无效。详细可看org.springframework.transaction.interceptor.TransactionAspectSupport类中invokeWithinTransaction方法。需要以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
在实际环境中,可通过如下方式完善:
1、在pom.xml中新增依赖
<dependency>
<groupId>com.stark.commons</groupId>
<artifactId>commons-spring-core</artifactId>
<version>1.2.0-SNAPSHOT</version>
</dependency>
2、在serviceImpl中新增方法
private FooServiceImpl getThis() {
return ApplicationContextUtil.getBean("fooService", FooServiceImpl.class);
}
3、修改内部调用
@Override
public void invokeInsertThenRollback() throws RollbackException {
getThis().insertThenRollback();
}
这样会发现事务仍然进行了回滚,原理是通过 ApplicationContextUtil.getBean()
方法获取 Bean,是从 Spring 容器中获取实际的代理对象,可以确保在方法调用时能够正确的应用事务代理。