验证事务

这一节在之前的代码中验证一下事务是否可以回滚

抛出Exception

修改之前UserServiceImpl类的addUser方法如下,在该方法中抛出一个Exception:

@Override
public void addUser(User user) throws Exception {

    userDao.addUser(user);

    throw new Exception();

}

此时事务是不会回滚的,因为默认情况下只有发生运行时异常之后spring才会进行事务的回滚,而这里抛出的Exception显然不是运行时异常。此时可以在方法上加入如下注解来设置当发生Exception之后会进行回滚:

@Transactional(rollbackFor = {Exception.class})

抛出RuntimeException

修改之前UserServiceImpl类的addUser方法如下,在该方法中抛出一个RuntimeException:

@Override
public void addUser(User user) throws Exception {

    userDao.addUser(user);

    throw new RuntimeException();

}

因为抛出了运行时异常,所以此时spring会进行事务回滚操作。

需要注意的是倘若在方法中捕获异常的话,那么会导致事务不会回滚:

 @Override
public void addUser(User user)  {

    userDao.addUser(user);

    try {
        throw new RuntimeException();
    } catch (RuntimeException e) {
        e.printStackTrace();
    }

}

这时可以在catch中再次将异常抛出,此时事务会发生回滚:

@Override
public void addUser(User user)  {

    userDao.addUser(user);

    try {
        throw new RuntimeException();
    } catch (RuntimeException e) {
        e.printStackTrace();
        //再次抛出异常
        throw new RuntimeException();
    }

}

下面这种写法会发生运行时异常,所以事务会进行回滚操作

 @Override
public void addUser(User user)  {

    userDao.addUser(user);

    int a = 1/0;
}

在使用spring默认的事务配置时,只有在抛出RuntimeException之后事务才会回滚,不要在Service层捕获异常(try…catch),如果一定要捕获的话,在catch中再次抛出异常即可。

验证事务的传播行为

之前简单介绍了一下事务的传播行为,现在通过代码来验证一下。如果要验证事务传播行为的话,这里需要新建一个Service,这里来新增一个需求,在添加用户的时候向日志表中添加一条数据。

新建一个日志表t_log,里面添加一个字段username

CREATE TABLE `t_log`  (
  `username` varchar(255)
)

创建mapper

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.monkey1024.dao.LogDao">

    <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

    <insert id="addLog">
        INSERT INTO t_log(username) VALUES (#{username})
    </insert>

</mapper>

创建dao

public interface LogDao {
    void addLog(String username);
}

创建service

@Service
public class LogServiceImpl implements LogService {

    @Autowired
    private LogDao logDao;

    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void addLog(String username) throws Exception {
        logDao.addLog(username);
    }
}

在userService中将LogService注入,并调用addLog方法

 //注入service
@Autowired
private LogService logService;

@Transactional(propagation = Propagation.REQUIRED)
@Override
public void addUser(User user) throws Exception {
    userDao.addUser(user);
    logService.addLog(user.getName());
    throw new NullPointerException();
}

在addUser方法中调用了addLog方法,并且抛出了一个空指针异常,倘若传播行为设置为Propagation.REQUIRED的话,这两个方法都会进行回滚。

倘若在addLog方法中将传播行为设置为Propagation.REQUIRES_NEW的话,addLog会开启新的事务,因此提交成功,addUser会回滚。