什么是数据库事务

数据库事务是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。

  • 事务处理是一种机制,用来管理必须完成批执行的mysql操作,以保证数据库不包含不完整的操作结果。
  • 事务:指一组SQL语句;

Spring为什么支持数据库事务

对于一些业务网站而言,产品库的扣减、交易记录以及账户都必须是要么同时成功,要么同时失败。又例如在一些特殊场景下,如一个批处理,它将处理多个交易,但是在一些交易中发生了异常,这个时候就不能将所有的交易都回滚(否则,那些本能够正常处理的业务也无端地被回滚了)。

Spring数据库事务应用

通过@Transactional注解的相关配置,Spring就会知道在哪里启动事务机制,其约定流程如下图所示:

java spring数据库事务处理 spring的数据库事务_回滚

  • @Transactional的源码
package org.springframework.transaction.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
  // 通过bean name指定事务管理器
  @AliasFor("transactionManager")
  String value() default "";

  // 同value属性
  @AliasFor("value")
  String transactionManager() default "";

  // 指定传播行为
  Propagation propagation() default Propagation.REQUIRED;

  // 指定隔离级别
  Isolation isolation() default Isolation.DEFAULT;

  // 指定超时时间(单位:秒)
  int timeout() default -1;

  // 是否只读事务
  boolean readOnly() default false;

  // 方法在发生指定异常时回滚,默认是所有异常都回滚
  Class<? extends Throwable>[] rollbackFor() default {};

  // 方法在发生指定异常名称时回滚,默认是所有异常都回滚
  String[] rollbackForClassName() default {};

  // 方法在发生指定异常时不回滚,默认是所有异常都回滚
  Class<? extends Throwable>[] noRollbackFor() default {};

  // 方法在发生指定异常名称时不回滚,默认是所有异常都回滚
  String[] noRollbackForClassName() default {};
}
  • @Transactional应用
@Service
public class UserServiceImpl implements UserService {
  @Autowired
  private UserDao userDao = null;
  
  @Override
  @Transactional(isolation= Isolation.READ_COMMITTED, timeout = 1)
  public int insertUser(User user) {
    return userDao.insertUser(user);
  }
  
  @Override
  @Transactional(isolation= Isolation.READ_COMMITTED, timeout = 1)
  public User getUser(Long id) {
    return userDao.getUser(id);
  }
}
  • 隔离级别
    上面是简单的使用事务,下面是Spring事务机制中最重要的两个配置项,即隔离级别和传播行为。
    数据库丢失更新示例:(假设一种商品的库存数量还有100,每次都只抢购1件商品,则抢购中就可能出现下面的场景)

    对于这样一个事务回滚另外一个事务提交而引发的数据不一致的情况,称之为第一类丢失更新

    对于这样多个事务提交引发的丢失更新,称之为第二类丢失更新

mysql中的事务

  • mysql事务详解:(隔离级别 -> 读分类 -> 锁)
  • java spring数据库事务处理 spring的数据库事务_java spring数据库事务处理_02

  • 未提交读:允许脏读,也就是可能读取到其他会话中未提交事务修改的数据;
脏读:当一个会话的事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个会话的事务也访问这个数据,然后使用了这个数据;

java spring数据库事务处理 spring的数据库事务_java_03

  • 提交读:只能读取到已经提交读数据。Oracle等多数数据库默认都是该级别(不重复读);
提交读解决了脏读的现象:

java spring数据库事务处理 spring的数据库事务_java_04


提交读存在的问题 不可重复读:指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,则第一个事务两次读到的数据可能是不一样的,这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读;

java spring数据库事务处理 spring的数据库事务_数据_05

  • 可重复读:可重复读。在同一个事务内的查询都是事务开始时刻一致的。即同一个事务里面,读取同一行数据不会因为外部的事务修改了而改变,也就是同一个事务里面,所有的数据怎么读都是同一个状态的,不受外部影响,InnoDB默认级别;
    可重复读解决了提交读中不可重复读的问题:
可重复存在的问题 幻读:会话T1事务中执行一次查询,然后会话T2新插入一行记录,这行记录恰好可以满足T1所使用的查询条件。然后T1又使用相同的查询再次对表进行检索,但是此时却看到了事务T2刚才插入的新行。这个新行就称为"幻象",因为对T1来说这一行就像突然出现的一样

java spring数据库事务处理 spring的数据库事务_java_06

  • 串行读:完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞;
  • 不可重复读和幻读读区别:不可重复读重点在于update和delete,幻读重点在于insert

锁详解:…