@Transactional注解只能应用到public可见度的方法上,可以被应用于接口定义和接口方法,方法会覆盖类上面声明的事务。
例如用户新增需要插入用户表、用户与岗位关联表、用户与角色关联表,如果插入成功,那么一起成功,如果中间有一条出现异常,那么回滚之前的所有操作, 这样可以防止出现脏数据,就可以使用事务让它实现回退。只需在方法或类添加@Transactional注解即可。
@Transactional
public int insertUser(User user)
{
// 新增用户信息
int rows = userMapper.insertUser(user);
// 新增用户岗位关联
insertUserPost(user);
// 新增用户与角色管理
insertUserRole(user);
return rows;
}
常见坑点1:遇到检查异常时,事务开启,也无法回滚。 例如下面这段代码,用户依旧增加成功,并没有因为后面遇到检查异常而回滚!!
@Transactional
public int insertUser(User user) throws Exception
{
// 新增用户信息
int rows = userMapper.insertUser(user);
// 新增用户岗位关联
insertUserPost(user);
// 新增用户与角色管理
insertUserRole(user);
// 模拟抛出SQLException异常
boolean flag = true;
if (flag)
{
throw new SQLException("发生异常了..");
}
return rows;
}
原因分析:因为Spring的默认的事务规则是遇到运行异常(RuntimeException)和程序错误(Error)才会回滚。如果想针对非检查异常进行事务回滚,可以在@Transactional 注解里使用 rollbackFor 属性明确指定异常。
常见坑点2: 在业务层捕捉异常后,发现事务不生效。在业务层手工捕捉并处理了异常。
例如:下面这段代码直接导致用户新增的事务回滚没有生效。
@Transactional
public int insertUser(User user) throws Exception
{
// 新增用户信息
int rows = userMapper.insertUser(user);
// 新增用户岗位关联
insertUserPost(user);
// 新增用户与角色管理
insertUserRole(user);
// 模拟抛出SQLException异常
boolean flag = true;
if (flag)
{
try
{
// 谨慎:尽量不要在业务层捕捉异常并处理
throw new SQLException("发生异常了..");
}
catch (Exception e)
{
e.printStackTrace();
}
}
return rows;
}
推荐做法:在业务层统一抛出异常,然后在控制层统一处理。
@Transactional
public int insertUser(User user) throws Exception
{
// 新增用户信息
int rows = userMapper.insertUser(user);
// 新增用户岗位关联
insertUserPost(user);
// 新增用户与角色管理
insertUserRole(user);
// 模拟抛出SQLException异常
boolean flag = true;
if (flag)
{
throw new RuntimeException("发生异常了..");
}
return rows;
}