Get Started
@Transactional Annotation—Declarative Transaction
@Service
public class UserServiceImpl implements UserService{
@Transactional
@Override
public void insertUserDeclarative(WpUser wpUser) {
userDao.insertUser(wpUser);
// 增加一个异常,此时事务会生效,即wpUser插入到数据库这个操作,会被rollback
int i = 1/0;
}
}
测试:
@SpringBootTest
class BootTransactionDemoApplicationTests {
@Autowired
private UserService userService;
@Test
void contextLoads() {
WpUser wpUser = new WpUser();
wpUser.setName("测试人1号");
wpUser.setAge(25);
userService.insertUserDeclarative(wpUser);
}
}
通过上面的demo,我们可以知道,给insertUserDeclarative方法添加了一个@Transactional注解,就可以开启事务了,当insertUserDeclarative方法抛出异常后,就会进行事务的rollback,从而让插入到数据库的数据,也实现了rollback。
PlatformTransactionManager—Programmatic txManager
@Service
public class UserServiceImpl implements UserService{
@Autowired
private PlatformTransactionManager txManager;
@Override
public void insertUserProgrammatic(WpUser wpUser) {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// explicitly setting the transaction name is something that can be done only programmatically
def.setName("insertUserTx");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus txStatus = txManager.getTransaction(def);
try {
userDao.insertUser(wpUser);
// 增加一个异常,此时事务会生效,即wpUser插入到数据库这个操作,会被rollback
int i = 1/0;
}catch (Exception e) {
txManager.rollback(txStatus);
throw e;
}
txManager.commit(txStatus);
}
}
测试
@SpringBootTest
class BootTransactionDemoApplicationTests {
@Autowired
private UserService userService;
@Test
void contextLoads() {
WpUser wpUser = new WpUser();
wpUser.setName("测试人1号");
wpUser.setAge(25);
userService.insertUserProgrammatic(wpUser);
}
}
通过上面的demo,我们可以看到,通过PlatformTransactionManager ,来实现编程式事务,出现异常后,调用PlatformTransactionManager 的rollback方法,来实现事务回滚。
我们知道,jdbc原生的事务,是通过设置connection.setAutoCommit(false);
、connection.commit();
、connection.rollback();
来实现的。
而spring事务其实也就是,对这些代码进行了封装。
比如,上面的PlatformTransactionManager的编程式事务,在调用getTransaction方法时,就是设置connection的autoCommit为false。调用链如下:
org.springframework.transaction.PlatformTransactionManager#getTransaction
----->org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction
----->org.springframework.transaction.support.AbstractPlatformTransactionManager#startTransaction
-----> org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin con.setAutoCommit(false);
TransactionTemplate—Programmatic txManager
@Service
public class UserServiceImpl implements UserService{
@Autowired
private TransactionTemplate txTemplate;
@Override
public void insertUserByTxtemplate(WpUser wpUser) {
Integer rows = txTemplate.execute(new TransactionCallback<Integer>() {
@Override
public Integer doInTransaction(TransactionStatus status) {
try {
int affectedRows = userDao.insertUser(wpUser);
// 增加一个异常,此时事务会生效,即wpUser插入到数据库这个操作,会被rollback
// int i = 1 / 0;
return affectedRows;
} catch (Exception e) {
status.setRollbackOnly();
return 0;
}
}
});
System.out.println("rows:"+rows);
}
}
测试
@SpringBootTest
class BootTransactionDemoApplicationTests {
@Autowired
private UserService userService;
@Test
void contextLoads() {
WpUser wpUser = new WpUser();
wpUser.setName("测试人1号");
wpUser.setAge(25);
userService.insertUserByTxtemplate(wpUser);
}
}
通过上面的demo,我们可以通过TransactionTemplate,来实现事务的控制,包括提交和回滚。
multiple transaction
多个事务的Declarative式写法
如果有多个事务,那么必须,要用一个额外的大事务,来包裹多个小事务,这样才能做到行动一致。
如果不用一个额外的大事务,那么多个小事务,是无法做到,一起提交 或者 一起回滚的。
并且,要指定@Transactional(propagation= Propagation.REQUIRED)
大事务如下:
@Service
public class UserDeptServiceImpl implements UserDeptService{
@Autowired
private UserService userService;
@Autowired
private DeptService deptService;
@Transactional(propagation= Propagation.REQUIRED)
@Override
public void insertUserDeptDeclarative(WpUser wpUser, WpDept wpDept) {
// 这里,我们想实现一个效果:插入用户 和 部门,假设有任何一个失败了,那么另一个也回滚
userService.insertUserDeclarative(wpUser);
deptService.insertDeptDeclarative(wpDept);
}
}
2个小事务,分别如下
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserDao userDao;
@Autowired
private PlatformTransactionManager txManager;
@Autowired
private TransactionTemplate txTemplate;
@Transactional(propagation= Propagation.REQUIRED)
@Override
public void insertUserDeclarative(WpUser wpUser) {
userDao.insertUser(wpUser);
// 增加一个异常,此时事务会生效,即wpUser插入到数据库这个操作,会被rollback
// int i = 1/0;
}
}
------------------------------
@Service
public class DeptServiceImpl implements DeptService{
@Autowired
private DeptDao deptDao;
@Transactional(propagation= Propagation.REQUIRED)
@Override
public int insertDeptDeclarative(WpDept wpDept) {
int affectedRows = deptDao.insertDept(wpDept);
int i = 1/0;
return affectedRows;
}
}
测试
@SpringBootTest
class BootTransactionDemoApplicationTests {
@Test
void testUserDept() {
WpUser wpUser = new WpUser();
wpUser.setName("测试人1号");
wpUser.setAge(25);
WpDept wpDept = new WpDept();
wpDept.setName("测试部");
userDeptService.insertUserDeptDeclarative(wpUser,wpDept);
}
}
多个事务的Programmatic写法
和上面一样,如果有多个事务,那么必须,要用一个额外的大事务,来包裹多个小事务,这样才能做到行动一致。
如果不用一个额外的大事务,那么多个小事务,是无法做到,一起提交 或者 一起回滚的。
2个小事务,分别如下:
@Service
public class UserServiceImpl implements UserService{
@Autowired
private PlatformTransactionManager txManager;
@Override
public void insertUserProgrammatic(WpUser wpUser) {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// explicitly setting the transaction name is something that can be done only programmatically
def.setName("insertUserTx");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus txStatus = txManager.getTransaction(def);
try {
userDao.insertUser(wpUser);
// 增加一个异常,此时事务会生效,即wpUser插入到数据库这个操作,会被rollback
// int i = 1 / 0;
txManager.commit(txStatus);
} catch (Exception e) {
txManager.rollback(txStatus);
throw e; // 这里,必须要将异常抛出
}
}
}
这里,需要注意:在catch中,除了回滚事务,还必须要将异常抛出。 这里先给出结论,下面会解释,为什么要将异常抛出。
@Service
public class DeptServiceImpl implements DeptService{
@Autowired
private PlatformTransactionManager txManager;
@Override
public void insertDeptProgrammatic(WpDept wpDept) {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// explicitly setting the transaction name is something that can be done only programmatically
def.setName("insertDeptTx");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus txStatus = txManager.getTransaction(def);
try {
deptDao.insertDept(wpDept);
// 增加一个异常,此时事务会生效,即wpUser插入到数据库这个操作,会被rollback
int i = 1 / 0;
txManager.commit(txStatus);
} catch (Exception e) {
txManager.rollback(txStatus);
throw e; // 这里,必须要将异常抛出
}
}
}
大事务,如下:
@Service
public class UserDeptServiceImpl implements UserDeptService{
@Autowired
private UserService userService;
@Autowired
private DeptService deptService;
@Autowired
private PlatformTransactionManager txManager;
@Override
public void insertUserDeptProgrammatic(WpUser wpUser, WpDept wpDept) {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// explicitly setting the transaction name is something that can be done only programmatically
def.setName("insertUserDeptTx");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus txStatus = txManager.getTransaction(def);
try {
userService.insertUserProgrammatic(wpUser);
deptService.insertDeptProgrammatic(wpDept);
txManager.commit(txStatus);
} catch (Exception e) {
txManager.rollback(txStatus);
throw e; // 这里,必须要将异常抛出
}
}
}
测试
@Test
void testUserDept() {
WpUser wpUser = new WpUser();
wpUser.setName("测试人1号");
wpUser.setAge(25);
WpDept wpDept = new WpDept();
wpDept.setName("测试部");
userDeptService.insertUserDeptProgrammatic(wpUser,wpDept);
}
通过上面的demo,可以看到,只要2个小事务,任何一个事务回滚了,其他的事务,也会一起被回滚。
上面的流程如下:
需要注意的是:
-
虽然事务A,调用了commit方法,但是因为spring实际上,并且真的去执行commit操作,只是记录下来了。
-
而事务B,调用了rollback方法,spring实际上,也没有真的去执行rollback操作,也只是记录下来了。
-
等到,大事务,调用rollback方法,此时,spring才真正的去执行rollback操作,结果就是,事务A和B中的操作,都回滚了
为什么会这样呢?我们看下源码就知道了
// org.springframework.transaction.support.AbstractPlatformTransactionManager#processCommit
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
boolean unexpectedRollback = false;
prepareForCommit(status);
triggerBeforeCommit(status);
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Releasing transaction savepoint");
}
unexpectedRollback = status.isGlobalRollbackOnly();
status.releaseHeldSavepoint();
}
else if (status.isNewTransaction()) { // 这一步,是最关键的一步
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}
unexpectedRollback = status.isGlobalRollbackOnly();
doCommit(status);
}
else if (isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = status.isGlobalRollbackOnly();
}
// Throw UnexpectedRollbackException if we have a global rollback-only
// marker but still didn't get a corresponding exception from commit.
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction silently rolled back because it has been marked as rollback-only");
}
}
catch (UnexpectedRollbackException ex) {
// can only be caused by doCommit
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
throw ex;
}
catch (TransactionException ex) {
// can only be caused by doCommit
if (isRollbackOnCommitFailure()) {
doRollbackOnCommitException(status, ex);
}
else {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
}
throw ex;
}
catch (RuntimeException | Error ex) {
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(status);
}
doRollbackOnCommitException(status, ex);
throw ex;
}
// Trigger afterCommit callbacks, with an exception thrown there
// propagated to callers but the transaction still considered as committed.
try {
triggerAfterCommit(status);
}
finally {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
}
}
finally {
cleanupAfterCompletion(status);
}
}
上面的代码中,只有status.isNewTransaction()
为true,才会去真正执行commit。
而事务A的status.isNewTransaction()
其实是false,所以,没有真正执行commit。
事务B的status.isNewTransaction()
也是false,
而大事务的status.isNewTransaction()
才是true,所以大事务调用commit方法,才会真正去执行commit操作。
接下来,我们再来看另一个问题,上面说过,在捕获异常后,除了要调用rollback方法,还需要将异常抛出。现在,我们在这里解释下原因:
通过上面的流程图,我们发现,如果不将异常抛出,很可能会出现:事务A调用了rollback方法后,事务B又调用commit方法,而这2个事务,其实是同一个事务,导致出现了org.springframework.transaction.IllegalTransactionStateException: Transaction is already completed - do not call commit or rollback more than once per transaction
多个事务的TransactionTemplate写法
和上面一样,如果有多个事务,那么必须,要用一个额外的大事务,来包裹多个小事务,这样才能做到行动一致。
如果不用一个额外的大事务,那么多个小事务,是无法做到,一起提交 或者 一起回滚的。
2个小事务,分别如下:
@Service
public class UserServiceImpl implements UserService{
@Autowired
private TransactionTemplate txTemplate;
@Override
public void insertUserByTxtemplate(WpUser wpUser) {
Integer rows = txTemplate.execute(new TransactionCallback<Integer>() {
@Override
public Integer doInTransaction(TransactionStatus status) {
try {
int affectedRows = userDao.insertUser(wpUser);
// 增加一个异常,此时事务会生效,即wpUser插入到数据库这个操作,会被rollback
int i = 1 / 0;
return affectedRows;
} catch (Exception e) {
status.setRollbackOnly();
throw e;
}
}
});
System.out.println("insertUserByTxtemplate rows:"+rows);
}
}
@Service
public class DeptServiceImpl implements DeptService{
@Autowired
private TransactionTemplate txTemplate;
@Override
public void insertDeptByTxtemplate(WpDept wpDept) {
Integer affectedRows = txTemplate.execute(new TransactionCallback<Integer>() {
@Override
public Integer doInTransaction(TransactionStatus status) {
try {
int rows = deptDao.insertDept(wpDept);
// int i = 1/0;
return rows;
} catch (Exception e) {
status.setRollbackOnly();
throw e;
}
}
});
System.out.println("insertDeptByTxtemplate rows:"+affectedRows);
}
}
大事务如下:
@Service
public class UserDeptServiceImpl implements UserDeptService{
@Autowired
private UserService userService;
@Autowired
private DeptService deptService;
@Autowired
private TransactionTemplate txTemplate;
@Override
public void insertUserDeptByTemplate(WpUser wpUser, WpDept wpDept) {
Integer affectedRows = txTemplate.execute(new TransactionCallback<Integer>() {
@Override
public Integer doInTransaction(TransactionStatus status) {
try {
userService.insertUserByTxtemplate(wpUser);
deptService.insertDeptByTxtemplate(wpDept);
return 1;
} catch (Exception e) {
status.setRollbackOnly();
System.err.println(e.getMessage());
return 0;
}
}
});
}
}
测试
@SpringBootTest
class BootTransactionDemoApplicationTests {
@Autowired
private UserDeptService userDeptService;
@Test
void testUserDept() {
WpUser wpUser = new WpUser();
wpUser.setName("测试人1号");
wpUser.setAge(25);
WpDept wpDept = new WpDept();
wpDept.setName("测试部");
userDeptService.insertUserDeptByTemplate(wpUser,wpDept);
}
}