Spring-tx

0 阅读4分钟

maven 项目搭建

添加事务注解

  1. 配置事务管理器(数据库相关的配置)
@EnableTransactionManagement // 开启事务注解支持

....

/**
 * 装配事务管理实现对象
 * @param dataSource
 * @return
 */
@Bean
public TransactionManager transactionManager(DataSource dataSource){
    // 内部要进行事务的操作,要基于连接池
    DataSourceTransactionManager  dataSourceTransactionManager = new DataSourceTransactionManager();
    dataSourceTransactionManager.setDataSource(dataSource);
    return dataSourceTransactionManager;
}
  1. 使用声明事务注解 @Transactional
// 类中所有的方法都开启事务
@Transactional
@Service
public class StudentService {
    @Autowired
    private StudentDao studentDao;

    // 因为有修改操作,所以不启动只读模式
    @Transactional(readOnly = false)
    public void changeInfo(){
        studentDao.updateAgeById(101,2);
        System.out.println("-----------");
        studentDao.updateNameById("test2",2);
    }

    // 只是查询,开启 只读模式 模式,加快数据查询速度
    @Transactional(readOnly = true)
    public void getStudentInfo(){
        
    }
}

超时时间

  • 事务在执行过程中,有可能因为遇到某些问题,导致程序卡住,从而长时间占用数据库资源。而长时间占用资源,大概率是因为程序运行出现了问题(可能是Java程序或MySQL数据库或网络连接等等)。此时这个很可能出问题的程序应该被回滚,撤销它已做的操作,事务结束,把资源让出来,让其他正常程序可以执行。概括来说就是一句话:超时回滚,释放资源。
  • 默认值是 -1 ,表示永远不超时。
// 因为有修改操作,所以不启动只读模式
@Transactional(readOnly = false, timeout = 3)
public void changeInfo(){
    studentDao.updateAgeById(101,2);
    System.out.println("-----------");
    studentDao.updateNameById("test2",2);
}

事务异常

  • 默认只针对运行时异常回滚,编译时异常不回滚。
@Service
public class StudentService {
    @Autowired
    private StudentDao studentDao;

    /**
     * timeout设置事务超时时间,单位秒! 默认: -1 永不超时,不限制事务时间!
     * rollbackFor = 指定哪些异常才会回滚,默认是 RuntimeException and Error 异常方可回滚!
     * noRollbackFor = 指定哪些异常不会回滚, 默认没有指定,如果指定,应该在rollbackFor的范围内!
     */
    @Transactional(readOnly = false,timeout = 3)
    public void changeInfo() throws FileNotFoundException {
        studentDao.updateAgeById(100,1);
        //主动抛出一个 IO异常 , 发现不会回滚,因为不在rollbackFor的默认范围内! 
        new FileInputStream("xxxx");
        studentDao.updateNameById("test1",1);
    }
}
  • 设置回滚异常(rollbackFor属性:指定哪些异常类才会回滚,默认是 RuntimeException and Error 异常方可回滚!)
/**
 * timeout设置事务超时时间,单位秒! 默认: -1 永不超时,不限制事务时间!
 * rollbackFor = 指定哪些异常才会回滚,默认是 RuntimeException and Error 异常方可回滚!
 * noRollbackFor = 指定哪些异常不会回滚, 默认没有指定,如果指定,应该在rollbackFor的范围内!
 */
@Transactional(readOnly = false,timeout = 3,rollbackFor = Exception.class)
public void changeInfo() throws FileNotFoundException {
    studentDao.updateAgeById(100,1);
    //主动抛出一个检查异常,测试! 发现不会回滚,因为不在rollbackFor的默认范围内! 
    new FileInputStream("xxxx");
    studentDao.updateNameById("test1",1);
}
  • 设置不回滚的异常
    noRollbackFor 属性:在rollbackFor的范围内,指定哪些异常不会回滚
@Service
public class StudentService {
    @Autowired
    private StudentDao studentDao;
    /**
     * timeout设置事务超时时间,单位秒! 默认: -1 永不超时,不限制事务时间!
     * rollbackFor = 指定哪些异常才会回滚,默认是 RuntimeException and Error 异常方可回滚!
     * noRollbackFor = 指定哪些异常不会回滚, 默认没有指定,如果指定,应该在rollbackFor的范围内!
     */
    @Transactional(readOnly = false,timeout = 3,rollbackFor = Exception.class,noRollbackFor = FileNotFoundException.class)
    public void changeInfo() throws FileNotFoundException {
        studentDao.updateAgeById(100,1);
        //主动抛出一个检查异常,测试! 发现不会回滚,因为不在rollbackFor的默认范围内!
        new FileInputStream("xxxx");
        studentDao.updateNameById("test1",1);
    }
}

事务隔离级别

@Service
public class StudentService {
    @Autowired
    private StudentDao studentDao;
    /**
     * isolation = 设置事务的隔离级别,mysql默认是repeatable read!
     */
    @Transactional(readOnly = false,isolation = Isolation.REPEATABLE_READ)
    public void changeInfo() throws FileNotFoundException {
        studentDao.updateAgeById(100,1);
        studentDao.updateNameById("test1",1);
    }
}

事务传播行为

image.png

  • 声明两个业务方法
@Service
public class StudentService {
    @Autowired
    private StudentDao studentDao;
    /**
     * 声明两个独立修改数据库的事务业务方法
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void changeAge(){
        studentDao.updateAgeById(99,1);
    }
    @Transactional(propagation = Propagation.REQUIRED)
    public void changeName(){
        studentDao.updateNameById("test2",1);
        int i = 1/0;
    }
}
  • 声明一个整合业务方法
@Service
public class TopService {
    @Autowired
    private StudentService studentService;
    @Transactional
    public void  topService(){
        studentService.changeAge();
        studentService.changeName();
    }
}

因为设置了 propagation = Propagation.REQUIRED,所以 topServicechangeAgechangeName 都在同一个事务中了,现在执行 changeAge 正常,但是 changeName 异常了,所以事务回滚,changeAge 修改的值也回滚了。

  • propagation 属性的可选值
名称含义
REQUIRED 默认值如果父方法有事务,就加入,如果没有就新建自己独立!
REQUIRES_NEW不管父方法是否有事务,我都新建事务,都是独立的!

tips: 在同一个类中,对于@Transactional注解的方法调用,事务传播行为不会生效。这是因为Spring框架中使用代理模式实现了事务机制,在同一个类中的方法调用并不经过代理,而是通过对象的方法调用,因此@Transactional注解的设置不会被代理捕获,也就不会产生任何事务传播行为的效果。