使用

使用 SpringBoot 建立一个测试项目.

sql

  1. create database test;
  2. -- auto-generated definition
  3. create table test_transaction
  4. (
  5. id int auto_increment
  6. primary key,
  7. name varchar(16) default '' not null,
  8. email varchar(24) default '' not null,
  9. birth timestamp default CURRENT_TIMESTAMP not null
  10. on update CURRENT_TIMESTAMP
  11. )
  12. engine = InnoDB;

DataSource

  1. /**
  2. * @author chenshun00@gmail.com
  3. * @since 2019/5/4
  4. */
  5. @Configuration
  6. @EnableTransactionManagement(proxyTargetClass = true) // 开启事务
  7. public class DataSourceConfig implements EnvironmentAware {
  8. private Environment environment;
  9. @Bean(initMethod = "init", destroyMethod = "close")
  10. public DataSource dataSource() {
  11. DruidDataSource druidDataSource = new DruidDataSource();
  12. druidDataSource.setUsername(this.environment.getProperty("spring.datasource.username"));
  13. druidDataSource.setPassword(this.environment.getProperty("spring.datasource.password"));
  14. druidDataSource.setUrl(this.environment.getProperty("spring.datasource.url"));
  15. druidDataSource.setDriverClassName(this.environment.getProperty("spring.datasource.driver-class-name"));
  16. druidDataSource.setDbType(this.environment.getProperty("spring.datasource.type"));
  17. druidDataSource.setInitialSize(Integer.parseInt(this.environment.getProperty("spring.datasource.initialSize")));
  18. druidDataSource.setMaxActive(Integer.parseInt(this.environment.getProperty("spring.datasource.maxActive")));
  19. druidDataSource.setMaxWait(Integer.parseInt(this.environment.getProperty("spring.datasource.maxWait")));
  20. druidDataSource.setValidationQuery(this.environment.getProperty("spring.datasource.validationQuery"));
  21. druidDataSource.setTestWhileIdle(Boolean.parseBoolean(this.environment.getProperty("spring.datasource.testWhileIdle")));
  22. druidDataSource.setTestOnBorrow(Boolean.parseBoolean(this.environment.getProperty("spring.datasource.testOnBorrow")));
  23. druidDataSource.setTestOnReturn(Boolean.parseBoolean(this.environment.getProperty("spring.datasource.testOnReturn")));
  24. return druidDataSource;
  25. }
  26. @Override
  27. public void setEnvironment(Environment environment) {
  28. this.environment = environment;
  29. }
  30. }

例子

SQL语句使用 wireshark 抓取. 源代码地址: https://github.com/chenshun00/mysql-transaction/

  • checked异常回滚

    • NullPointException
    • RuntimeException…

      1. set autocommit = 0
      2. INSERT INTO test_transaction ( name,email,birth ) VALUES ( 'chenshun00','chenshun00@gmail.com','2019-05-04 14:20:37.209')
      3. rollback
      4. SET autocommit=1
      5. @Transactional
      6. public void testUncheckedExceptionRollback() {
      7. testTransactionDao.addTestTransaction(build());
      8. throw new NullPointerException("测试checked异常会滚");
      9. }
  • unchecked异常不回滚

    • SQLException
    • other exception…

      1. set autocommit = 0
      2. INSERT INTO test_transaction ( name,email,birth ) VALUES ( 'chenshun00','chenshun00@gmail.com','2019-05-04 14:20:37.209' )
      3. SET autocommit=1
      4. @Transactional
      5. public void testCheckedException() throws SQLException {
      6. testTransactionDao.addTestTransaction(build());
      7. throw new SQLException("ggg");
      8. }
  • 指定异常回滚

    @Transactional(rollbackFor = {SQLException.class})

  1. set autocommit = 0
  2. INSERT INTO test_transaction ( name,email,birth ) VALUES ( 'chenshun00','chenshun00@gmail.com','2019-05-04 14:20:37.209' )
  3. rollback
  4. SET autocommit=1
  5. @Transactional(rollbackFor = {SQLException.class})
  6. public void testCheckedExceptionRollback() throws SQLException {
  7. testTransactionDao.addTestTransaction(build());
  8. throw new SQLException("ggg");
  9. }
  • 嵌套方法(无异常)

    1. --nest method 共插入2条记录 sql
    2. SET autocommit=0
    3. select @@session.tx_read_only
    4. INSERT INTO test_transaction ( name,email,birth ) VALUES ( 'chenshun00','chenshun00@gmail.com','2019-05-04 14:47:00.572' )
    5. select @@session.tx_read_only
    6. INSERT INTO test_transaction ( name,email,birth ) VALUES ( 'chenshun00','chenshun00@gmail.com','2019-05-04 14:47:00.635' )
    7. commit
    8. SET autocommit=1
    9. @Transactional
    10. public void testNestMethod() {
    11. //insert
    12. testTransactionDao.addTestTransaction(build());
    13. this.subMethod();
    14. }
    15. private void subMethod() {
    16. testTransactionDao.addTestTransaction(build());
    17. }
  • 嵌套方法带checked异常

    1. --子方法出现运行运行时异常,回滚
    2. SET autocommit=0
    3. select @@session.tx_read_only
    4. SELECT @@session.tx_isolation
    5. INSERT INTO test_transaction ( name,email,birth ) VALUES ( 'chenshun00','chenshun00@gmail.com','2019-05-04 14:57:47.05' )
    6. INSERT INTO test_transaction ( name,email,birth ) VALUES ( 'chenshun00','chenshun00@gmail.com','2019-05-04 14:57:47.141' )
    7. rollback
    8. SET autocommit=1
    9. @Transactional
    10. public void testNestMethodRuntimeException() {
    11. testTransactionDao.addTestTransaction(build());
    12. this.subExceptionMethod();
    13. }
    14. private void subExceptionMethod() {
    15. testTransactionDao.addTestTransaction(build());
    16. throw new NullPointerException("运行时异常测试");
    17. }
  • 嵌套方法带unchecked异常.

    1. set autocommit = 1
    2. select @@session.tx_read_only
    3. SELECT @@session.tx_isolation
    4. set autocommit = 0
    5. insert sql语句
    6. insert sql语句
    7. commit
    8. set autocommit = 1
    9. @Transactional
    10. public void testNestMethodUncheckException() throws SQLException {
    11. testTransactionDao.addTestTransaction(build());
    12. this.subUncheckExceptionMethod();
    13. }
    14. private void subUncheckExceptionMethod() throws SQLException {
    15. testTransactionDao.addTestTransaction(build());
    16. throw new SQLException("运行时异常测试");
    17. }
  • 嵌套方法指定异常回滚

    1. set autocommit = 1
    2. select @@session.tx_read_only
    3. SELECT @@session.tx_isolation
    4. set autocommit = 0
    5. insert sql语句
    6. insert sql语句
    7. rollback
    8. set autocommit = 1
    9. @Transactional(rollbackFor = {SQLException.class})
    10. public void testNestMethodUncheckExceptionRollback() throws SQLException {
    11. testTransactionDao.addTestTransaction(build());
    12. this.subUncheckExceptionMethodRollback();
    13. }
    14. private void subUncheckExceptionMethodRollback() throws SQLException {
    15. testTransactionDao.addTestTransaction(build());
    16. throw new SQLException("运行时异常测试");
    17. }
  • this 调用无事务

    1. /**
    2. * 测试调用未开启事务,直接是一条sql一个事务提交
    3. * 没有事务原因:没有Transactional注解,没有进入proxy代理当中,没有进入代码当中就进入不了事务,
    4. * this调用还是没有事务,这里如果要产生事务就需要获取当前(this)对象当proxy对象,再进行方法调用会产生事务
    5. */
    6. public void testMainMethodNoTransactionRuntimeException() {
    7. this.subMethodTransactionRuntimeException();
    8. }
    9. @Transactional
    10. public void subMethodTransactionRuntimeException() {
    11. testTransactionDao.addTestTransaction(build());
    12. throw new NullPointerException("11");
    13. }
  • this 调用带事务

    1. /**
    2. * proxy代理调用产生事务
    3. * 需要配置 @EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
    4. */
    5. public void testProxyObjectGetTransaction() throws SQLException {
    6. ((TestTransactionService) AopContext.currentProxy()).testProxyObjectGetTransactionSubMethod();
    7. }
    8. @Transactional(rollbackFor = {SQLException.class})
    9. public void testProxyObjectGetTransactionSubMethod() throws SQLException {
    10. testTransactionDao.addTestTransaction(build());
    11. throw new SQLException("11");
    12. }
  • 多Transactional注解

    1. /**
    2. * 事务1
    3. * <p>
    4. * set autocommit = 0
    5. * insert
    6. * insert
    7. * commit
    8. * set autocommit = 1
    9. */
    10. @Transactional
    11. public void firstTransaction() {
    12. testTransactionDao.addTestTransaction(build());
    13. this.secondTransaction();
    14. }
    15. /**
    16. * 事务2
    17. */
    18. @Transactional
    19. public void secondTransaction() {
    20. testTransactionDao.addTestTransaction(build());
    21. }

总结

自己多试试,抓包看本质.