Skip to content

事务

参考: https://blog.csdn.net/u013517797/article/details/82926410

https://blog.csdn.net/lsziri/article/details/80656600

https://blog.csdn.net/tianyaleixiaowu/article/details/73123242

概念

事务: 如果一个包含多个步骤的业务操作被事务管理, 那么这些操作要么同时成功, 要么同时失败.

事务的四大特性

  • 原子性: 是不可分隔的最小操作单位, 要么同时成功, 要么同时失败
  • 持久性: 当事务提交或回滚后, 数据库会持久化的保存数据
  • 隔离性: 多个事务之间会相互独立
  • 一致性: 事务操作前后, 数据总量不变

事务的隔离级别

问题:

  • 脏读: 一个事务读取到另一个事务没有提交的数据
  • 不可重复读(虚读): 在同一个事务中, 两次读取到的数据不一样
  • 幻读: 一个事务操作数据表中所有记录, 另一个事务添加了一条数据, 则第一个事务查询不到自己的修改

隔离级别:

1.** read uncommitted:读未提交**, 表示一个事务可以读取到另一个事务还未提交的数据

* 产生的问题:脏读、不可重复读、幻读

2. read committed:读已提交 (Oracle), 表示一个事务只能读取到另一个事务已经提交的数据

* 产生的问题:不可重复读、幻读

3.** repeatable read:可重复读 (MySQL默认)**, 表示一个事务在整个过程中可以重复执行某个查询, 并且每次返回的记录都相同, 即使在多次查询之间有新增的数据满足该查询, 这些新增的记录也会被忽略

* 产生的问题:幻读

4.** serializable:串行化,** 所有的事务依次逐个执行.

* 可以解决所有的问题

  • 注意:隔离级别从小到大安全性越来越高,但是效率越来越低

* 数据库查询隔离级别:

* select @@tx_isolation;

* 数据库设置隔离级别:

* set global transaction isolation level  级别字符串;

事务的传播行为

  • REQUIRED :如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
  • SUPPORTS :如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  • MANDATORY :如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
  • REQUIRES_NEW :创建一个新的事务,如果当前存在事务,则把当前事务挂起。
  • NOT_SUPPORTED :以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  • NEVER :以非事务方式运行,如果当前存在事务,则抛出异常。
  • NESTED :如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 REQUIRED 。

1745302737222


声明式事务

使用

在启动类开启@EnableTransactionManagement, 然后在类或方法上注解@Transactional即可

可以通过propagation以及isolation指定传播范围以及隔离级别

示例:

java
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT)

注解失效场景

  • @Transactional 注解只能应用到 public 可见度的方法上, 其余失效
  • 默认情况下,Spring会对unchecked异常进行事务回滚;如果是checked异常则不回滚

补充:

unchecked Exception(非受检异常==运行期异常): NPE, ClassCastException, OutOfIndexException...

checked Exception(受检异常==编译器异常): IOException, TimeoutException, SQLException, ClassNotFoundException...

  • 如果在同一个类中,一个非@Transaction的方法调用有@Transaction的方法不会生效,因为不走代理
  • 不同服务,非**@Transaction的方法调@Transaction的方法,里层有事务,但外层没有事务,也需注意**

例:

1.throw new Exception-->不生效, 因为只回滚RuntimeException下面的异常, 其余类型需要手动配置@Transactional(rollbackFor = Exception.class)

2.Mysql--mylsam引擎不支持事务

3.注解修饰的方法try--catch后事务不生效, 无异常抛出


编程式事务

使用

transactionTemplate使用

java
  @Autowired
  private TransactionTemplate transactionTemplate; 

@Override
  public Integer insert() {
    Integer i = transactionTemplate.execute(new TransactionCallback<Integer>() {
      @Override
      public Integer doInTransaction(TransactionStatus transactionStatus) {
        try {
          Integer i = userMapper.insert();
          // 除0错误演示
          System.out.println(1 / 0);
          return i;
        } catch (Exception e) {
          transactionStatus.setRollbackOnly();
          return 0;
        }
      }
    });
    return i;
  }
------------------上面例子用Lambda表达式优化下
  @Override
  public Integer insert() {
    return transactionTemplate.execute(t -> {
      try {
        return userMapper.insert();
      } catch (Exception e) {
        t.setRollbackOnly();
      }
      return 0;
    });
  }
------------------    
 @Override
  public Integer insert() {
    transactionTemplate.execute(new TransactionCallbackWithoutResult() {
      @Override
      protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
        try {
          userMapper.insert();
          // 除0错误用以演示
          System.out.println(1 / 0);
        } catch (Exception e) {
          transactionStatus.setRollbackOnly();
        }
      }
    });

    return null;
  }

PlatformTransactionManager****使用

java
 @Autowired
  private PlatformTransactionManager transactionManager;

 @Override
  public Integer insert() {
    DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
    definition.setPropagationBehavior(Propagation.REQUIRED.value());
    definition.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
    definition.setTimeout(-1);
    TransactionStatus status = transactionManager.getTransaction(definition);

    try {
      Integer i = userMapper.insert();
      // 除0错误演示
      System.out.println(1 / 0);
      this.transactionManager.commit(status);
      return i;
    } catch (Exception var8) {
      this.transactionManager.rollback(status);
      throw var8;
    }
  }

参考学习

巨益事务类

java
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.greatonce.core.database;

import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.support.DefaultTransactionDefinition;

public class ManualTransactionTemplate {
  private PlatformTransactionManager transactionManager;

  public ManualTransactionTemplate(PlatformTransactionManager transactionManager) {
    this.transactionManager = transactionManager;
  }

  public void execute(ManualTransactionTemplate.TransactionRunnable runnable) {
    this.execute(Propagation.REQUIRED, Isolation.DEFAULT, -1, runnable);
  }

  public void execute(Propagation propagation, ManualTransactionTemplate.TransactionRunnable runnable) {
    this.execute(propagation, Isolation.DEFAULT, -1, runnable);
  }

  public void execute(Propagation propagation, Isolation isolation, int timeout, ManualTransactionTemplate.TransactionRunnable runnable) {
    DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
    definition.setPropagationBehavior(propagation.value());
    definition.setIsolationLevel(isolation.value());
    definition.setTimeout(timeout);
    TransactionStatus status = this.transactionManager.getTransaction(definition);

    try {
      runnable.run();
      this.transactionManager.commit(status);
    } catch (Exception var8) {
      this.transactionManager.rollback(status);
      throw var8;
    }
  }

  public <T> T executeWithResult(ManualTransactionTemplate.TransactionCallable<T> callable) {
    return this.executeWithResult(Propagation.REQUIRED, Isolation.DEFAULT, -1, callable);
  }

  public <T> T executeWithResult(Propagation propagation, ManualTransactionTemplate.TransactionCallable<T> callable) {
    return this.executeWithResult(propagation, Isolation.DEFAULT, -1, callable);
  }

  public <T> T executeWithResult(Propagation propagation, Isolation isolation, int timeout, ManualTransactionTemplate.TransactionCallable<T> callable) {
    DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
    definition.setPropagationBehavior(propagation.value());
    definition.setIsolationLevel(isolation.value());
    definition.setTimeout(timeout);
    TransactionStatus status = this.transactionManager.getTransaction(definition);

    try {
      T t = callable.call();
      this.transactionManager.commit(status);
      return t;
    } catch (Exception var8) {
      this.transactionManager.rollback(status);
      throw var8;
    }
  }

  @FunctionalInterface
  public interface TransactionCallable<V> {
    V call();
  }

  @FunctionalInterface
  public interface TransactionRunnable {
    void run();
  }
}

使用

java
getTransactionTemplate().execute(() -> {
        b2bStockOutOrderDetailService.batchModify(outOrderDetails);
        stockOccupancyService.batchCreate(occupancies);
        updateOrder.setAuditedTime(LocalDateTime.now());
        update(updateOrder);
      });
---------------
int count = getTransactionTemplate().executeWithResult(() -> insertBatch(collection));

事务实测

同一个类中两个方法的调用

情况一: 实测安全!

java
  @Transactional
  public void method1() {
    method2();
  }

  @Transactional
  public void method2() {
  }

情况二: 实测安全!

java
  @Transactional
  public void method1() { 
    method2();
  }

  private void method2() {
  }

情况三: 实测安全!

java
  @Transactional
  public void method1() {
    method2();
  }

  public void method2() {
  }

情况四: 不安全!

java
  public void method1() {
    method2();
  }

  @Transactional
  public void method2() {
  }