java事务的详细讲解

发布时间:2021-09-16 20:28:50 作者:chen
来源:亿速云 阅读:206

这篇文章主要介绍“java事务的详细讲解”,在日常操作中,相信很多人在java事务的详细讲解问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”java事务的详细讲解”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

事务概念

我们要理解下事务概念: 什么是事务呢?事务是并发控制的单位,是用户定义的一个操作序列。有四个特性(ACID):

以上是书面解释,简单来说就是把你的操作统一化,要么所有操作都成功,要么就都不成功,如果执行中有某一项操作失败,其之前所有的操作都回滚到未执行这一系列操作之前的状态。

脏读、不可重复读、幻读

先理解这三种由于并发访问导致的数据读取问题,再理解事务隔离级别就简单多了。

脏读

A事务读取B事务尚未提交的数据,此时如果B事务发生错误并执行回滚操作,那么A事务读取到的数据就是脏数据。就好像原本的数据比较干净、纯粹,此时由于B事务更改了它,这个数据变得不再纯粹。这个时候A事务立即读取了这个脏数据,但事务B良心发现,又用回滚把数据恢复成原来干净、纯粹的样子,而事务A却什么都不知道,最终结果就是事务A读取了此次的脏数据,称为脏读。

这种情况常发生于转账与取款操作中

java事务的详细讲解

不可重复读(前后多次读取,数据内容不一致)

事务A在执行读取操作,由整个事务A比较大,前后读取同一条数据需要经历很长的时间 。而在事务A第一次读取数据,比如此时读取了小明的年龄为20岁,事务B执行更改操作,将小明的年龄更改为30岁,此时事务A第二次读取到小明的年龄时,发现其年龄是30岁,和之前的数据不一样了,也就是数据不重复了,系统不可以读取到重复的数据,成为不可重复读。

java事务的详细讲解

幻读(前后多次读取,数据总量不一致)

事务A在执行读取操作,需要两次统计数据的总量,前一次查询数据总量后,此时事务B执行了新增数据的操作并提交后,这个时候事务A读取的数据总量和之前统计的不一样,就像产生了幻觉一样,平白无故的多了几条数据,成为幻读。

java事务的详细讲解

小总结:不可重复读和幻读到底有什么区别?

(1) 不可重复读是读取了其他事务更改的数据,针对update操作

解决:使用行级锁,锁定该行,事务A多次读取操作完成后才释放该锁,这个时候才允许其他事务更改刚才的数据。

(2) 幻读是读取了其他事务新增的数据,针对insert和delete操作

解决:使用表级锁,锁定整张表,事务A多次读取数据总量之后才释放该锁,这个时候才允许其他事务新增数据。

这时候再理解事务隔离级别就简单多了呢。

数据库事务的隔离级别

SQL 标准定义的四种隔离级别被 ANSI(美国国家标准学会)和 ISO/IEC(国际标准)采用,每种级别对事务的处理能力会有不同程度的影响。事务是一系列的动作,它们综合在一起才是一个完整的工作单元,这些动作必须全部完成,如果有一个失败的话,那么事务就会回滚到最开始的状态,仿佛什么都没发生过一样。

数据库事务的隔离级别有4个,由低到高依次为Read uncommitted 、Read committed 、Repeatable read 、Serializable ,这四个级别可以逐个解决脏读 、不可重复读 、幻读 这几类问题。

java事务的详细讲解

DEFAULT

默认值,表示使用底层数据库的默认隔离级别。大部分数据库为READ_COMMITTED(MySql默认REPEATABLE_READ)

READ UNCOMMITTED(读未提交)

该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读和不可重复读,因此很少使用该隔离级别。

READ_COMMITTED (读提交)

该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。

REPEATABLE_READ (可重复读)

该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读。

SERIALIZABLE (串行化)

所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。 在该隔离级别下事务都是串行顺序执行的,MySQL 数据库的 InnoDB 引擎会给读操作隐式加一把读共享锁,从而避免了脏读、不可重读复读和幻读问题。

MVCC(多版本并发控制)

mysql中,默认的事务隔离级别是可重复读(repeatable-read),为了解决不可重复读,innodb采用了MVCC(多版本并发控制)来解决这一问题。 MVCC是利用在每条数据后面加了隐藏的两列(创建版本号和删除版本号),每个事务在开始的时候都会有一个递增的版本号,用来和查询到的每行记录的版本号进行比较。 MYSQL MVCC

Spring事务传播行为

先来介绍下Spring事务传播行为的使用方法:

@Transactional(propagation=Propagation.REQUIRED)
public void test() {
        //todo something
}

注解@Transactional 通过使用 propagation 属性设置,例如:@Transactional(propagation = Propagation.REQUIRED)

它的propagation属性取值有以下几种:

public enum Propagation {

    REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),

    SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),

    MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),

    REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),

    NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),

    NEVER(TransactionDefinition.PROPAGATION_NEVER),

    NESTED(TransactionDefinition.PROPAGATION_NESTED);

}

事务传播行为:

Spring 事务的两种实现

Spring 支持“编程式事务”管理和“声明式事务”管理两种方式:

1编程式事务: 编程式事务使用 TransactionTemplate 或者直接使用底层的 PlatformTransactionManager 实现事务。 对于编程式事务 Spring 比较推荐使用 TransactionTemplate 来对事务进行管理。

2声明式事务: 声明式事务是建立在 AOP 之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况“提交”或者“回滚”事务。

两种事务管理间的区别
Spring 编程式事务

一般来说编程式事务有两种方法可以实现: 模板事务的方式(TransactionTemplate)平台事务管理器方式(PlatformTransactionManager)

例:

其中 TransactionTemplate 的 execute 能接受两种类型参数执行事务,分别为:

 TransactionCallback<Object>(): 执行事务且可以返回一个值。
 TransactionCallbackWithoutResult(): 执行事务没有返回值。

下面是使用 TransactionTemplate 的实例:

@Service
public class TransactionExample {
  /** 1、获取 TransactionTemplate 对象 **/
  @Autowired
  private TransactionTemplate transactionTemplate;
  
  public void addUser() {
      // 2、使用 TransactionCallback 或者 TransactionCallbackWithoutResult 执行事务
      transactionTemplate.execute(new TransactionCallbackWithoutResult() {
          @Override
          public void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
              try {
                  // 3、执行业务代码(这里进行模拟,执行多个数据库操作方法)
                  userMapper.delete(1);
                  userMapper.delete(2);
              } catch (Exception e) {
                  // 4、发生异常,进行回滚
                  transactionStatus.setRollbackOnly();
              }
          }
      });
  }
  
}

使用事务管理器方式实现事务步骤:

@Service
public class TransactionExample {
    
    /** 1、获取 PlatformTransactionManager 对象 **/
    @Autowired
    private PlatformTransactionManager platformTransactionManager;

    public void addUser() {
        // 2、获取默认事务定义
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        // 设置事务传播行为
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        // 3、根据事务定义对象设置的属性,获取事务状态
        TransactionStatus status = platformTransactionManager.getTransaction(def);
        try {
            // 4、执行业务代码(这里进行模拟,执行多个数据库操作方法)
            userMapper.delete(1);
            userMapper.delete(2);
            // 5、事务进行提交
            platformTransactionManager.commit(status);
        } catch(Exception e){
            // 5、事务进行回滚
            platformTransactionManager.rollback(status);
        }
    }
    
}
Spring 声明式事务

声明式事务(declarative transaction management)顾名思义就是使用声明的方式来处理事务。该方式是基于 Spring AOP 实现的,将具体业务逻辑和事务处理解耦,其本质是在执行方法前后进行拦截,在方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

常用的声明式事务使用方法

常用的声明式事务使用方法有

两种方法,由于近几年 SpringBoot 的流行,提供很方便的自动化配置,致使 XML 方式已经逐渐淘汰,比较推荐使用注解的方式

@Transactional 的作用范围

注解 @Transactional 不仅仅可以添加在方法上面,还可以添加到类级别上,当注解放在类级别时,表示所有该类的公共方法都配置相同的事务属性信息。如果类级别配置了 @transactional,方法级别也配置了 @transactional,应用程序会以方法级别的事务属性信息来管理事务,换言之,方法级别的事务属性信息会覆盖类级别的相关配置。

@Transactional 注解中可配置参数
示例
@Transactional(propagation=Propagation.REQUIRED)
public void test() {
        //todo something
}

注意: 一般而言,不推荐将 @Transaction 配置到类上,因为这样很可能使后来的维护人员必须强制使用事务。

使用事务时需要注意的点

@Transactional(rollbackFor=Exception.class)
@Transactional(rollbackFor=Exception.class)
public void test() {
    try {
         //业务代码
    } catch (Exception e) {
        // TODO: handle exception
    }
   //主动捕捉异常导致框架无法捕获,从而导致事物失效
}

到此,关于“java事务的详细讲解”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!

推荐阅读:
  1. MVCC详细讲解
  2. Spring 事务 属性 详细

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

java

上一篇:如何使用Tomcat Native提升Tomcat IO效率

下一篇:React的核心原理和用法

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》