Spring事务----@Transactional浅谈

1,035 阅读3分钟

概述

Spring使用@Transactional 注解提供事务管理的快捷手段

常用参数

readOnly

@Transactional(readOnly = true)

readOnly属性用来设置当前事务是否为只读事务(默认 false)

当事务被设置成只读事务时,增删改操作会导致报错

org.springframework.dao.TransientDataAccessResourceException: 
### Error updating database.  Cause: java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed

rollbackFor

@Transactional(rollbackFor = Exception.class)

设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚

@Transactional默认只会回滚RuntimeException,如果发生非运行时异常不会回滚

propagation

用来设置事务的传播行为

常用的基本就这两个

@Transactional(propagation=Propagation.REQUIRED) :如果有事务, 那么加入事务, 没有的话新建一个(默认情况)
@Transactional(propagation=Propagation.REQUIRES_NEW) :不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务

下面通过四个案例说明这两个事务的区别

case1:

@Transactional
public void Example1(User user) {    
 userDao.createUser(user);   
 propagationService.required();
}
@Transactional
public void required() {
    throw new NullPointerException("抛出异常1");
}
@Test
public void Example1() 
{
    User user = new User();
    user.setUsername("vito1");
    transactionService.Example1(user);
}

会回滚

case2:

@Transactional
public void Example2(User user) {
    userDao.createUser(user);
    try {
        propagationService.required();
    } catch (Exception e) {
        e.printStackTrace();
    }
}
@Transactional
public void required() {
    throw new NullPointerException("抛出异常1");
}
@Test
public void Example2() 
{
    User user = new User();
    user.setUsername("vito2");
    transactionService.Example1(user);
}

也会回滚,而且还会多报个错

org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only 

case3:

@Transactional
public void Example3(User user) {
    userDao.createUser(user);
    propagationService.requiresNew();
}
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void requiredNew() {
    throw new NullPointerException("抛出异常2");
}
@Test
public void Example3() 
{
    User user = new User();
    user.setUsername("vito3");
    transactionService.Example3(user);
}

也会回滚,但不会报case2的错

case4:

@Transactional
public void Example4(User user) {
    userDao.createUser(user);
    try {
        propagationService.requiresNew();
    } catch (Exception e) {
        e.printStackTrace();
    }
}
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void requiredNew() {
    throw new NullPointerException("抛出异常2");
}
@Test
public void Example4() 
{
    User user = new User();
    user.setUsername("vito4");
    transactionService.Example4(user);
}

测试通过,并且数据成功插入

isolation

事务隔离级别,一般选择默认,即和数据库事务隔离级别一样

ISOLATION_DEFAULT  默认的隔离级别,使用数据库默认的事务隔离级别. 

另外四个与 JDBC 的隔离级别相对应

ISOLATION_READ_UNCOMMITTED 这是事务最低的隔离级别,它充许别外一个事务可以看到这个事务未提交的数据。 这种隔离级别会产生脏读,不可重复读和幻像读。

 ISOLATION_READ_COMMITTED 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。 这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。

 ISOLATION_REPEATABLE_READ 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。 它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生 (不可重复读)。

 ISOLATION_SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。 除了防止脏读,不可重复读外,还避免了幻像读。 


本文相关代码:github.com/kjlist/Bazo…

Tips

一个没有加 @Transactional 注解的方法,去调用一个加了 @Transactional 的方法,不会产生事务

reason:@Transactional底层使用的是AOP技术,利用了动态代理技术。

@Transactional 注解只能应用到 public 方法上