数据库事务,Spring事务

事务

什么是事务?通俗来讲就是一组操作。通过事务控制可以极大的避免出现逻辑处理失败导致的脏数据等问题。我们经常听到数据库事务,Spring事务,redis事务,分布式事务等。都是什么意思呢?

先来说说事务,事务有四个特征:原子性,一致性,隔离性,持久性,简称ACID。

-- 原子性:要么全部成功,要么全部失败回滚。

-- 一致性:一个事物执行前和执行后都必须处于一致性状态(转账)

-- 隔离性:多个用户并发访问数据库,操作同一张表,数据库为每个用户开启的事务,不能被其他事务所干扰,多个并发事务之间相互隔离。

-- 持久性:一旦事务提交,那么数据的改变是永久性的,发生故障也不会丢失提交的事务操作。

 

事务最重要的两个特性是传播级别隔离级别,传播级别定义的是事务的控制范围,事务隔离级别定义的是数据库在读写方面的控制范围。

 

不进行事务控制可能会导致的问题:

脏读:一个事务在处理过程中读了另一个未提交的事务的数据。

不可重复读:对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的值。这是由于查询间隔时,被另一个事务修改并提交了数据。

幻读:事务非独立执行时发生的一种现象。幻读是针对批量数据整体。当一个事务对批量数据进行修改时,另一个事务新增了数据,会产生还有数据没有修改成功的幻觉。

 

数据库事务4种隔离级别

 一个数据库声称支持事务,就必须具备ACID特性。Mysql数据库提供了4种隔离级别,级别越高效率越低。

1. Serializable 串行化:可避免脏读,不可重复读,幻读。采用锁表的方式,类似于java多线程里的锁。效率低,别的操作只有等他执行完成。

2. Repeatable read 可重复读:可避免脏读,不可重复读。mysql默认级别。

3. Read Committed 读已提交:可以避免脏读

4. Read Uncommitted 读未提交:无法保证脏读,不可重复读,幻读。

-- oracle只支持串行化和读已提交,默认读已提交。

设置数据库的隔离级别一定要是再开启事务之前,JDBC也是在调用setAutoCommit( false )之前。Mysql隔离级别设置只对当前窗口有效,即JDBC中只对一个connection有效。

数据库底层的事务提交和回滚是通过 binlog 或 redolog 来实现的。

 

Spring事务5种隔离级别

Isolation_default:默认隔离级别,使用数据库的默认隔离级别

其余四个与数据库隔离级别对应。

 

Spring事务

spring事务本质就是对数据库事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。纯JDBC操作的数据库,想要用到事务,有以下几个步骤:

  1. con.setAutoCommit(false); //取消自动提交
  2. 执行CRUD操作... ...
  3. con.commit() / rollback();
  4. con.close();

使用spring管理事务后,可以不用写1,3两个步骤,而是由spring自动完成。那么spring是如何在CRUD之前操作事务的呢?

以注解方式为例:配置文件开启注解驱动,在相关的类和方法上通过注解@Transactional 标识。spring在启动时回去解析生成相关的bean,会查看拥有相关注解的类和方法,并为这些类和方法生成代理,并根据@Transactional 的相关参数进行相关配置注入,这样就在代理中为我们把相关的事务处理掉了。

 

事务传播级别

PROPAGATION_REQUIRED:默认的事务传播级别,如果上下文中已经存在事务,那么就加入到事务中执行,如果当前上下文中不存在事务,则新建事务执行。

PROPAGATION_REQUIRES_NEW:每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。是两个独立事务,内层事务不会影响到外层事务的提交和回滚。反之亦然。

PROPAGATION_NESTED:嵌套级别事务。该传播级别特征是,如果上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务。父事务回滚,子事务也会回滚。子事务回滚,父事务之前的操作不会回滚,有一个savePoint

PROPAGATION_MANDATORY:该级别的事务要求上下文中必须要存在事务,否则就会抛出异常!配置该方式的传播级别是有效的控制上下文调用代码遗漏添加事务控制的保证手段。比如一段代码不能单独被调用执行,但是一旦被调用,就必须有事务包含的情况,就可以使用这个传播级别。

PROPAGATION_SUPPORTS:如果上下文存在事务,则支持事务加入事务,如果没有事务,则使用非事务的方式执行。

PROPAGATION_NOT_SUPPORTED:上下文中存在事务,则挂起事务,执行当前逻辑,结束后恢复上下文的事务

PROPAGATION_NEVER:要求上下文中不能存在事务,一旦有事务,就抛出runtime异常

嵌套级别的事务:

嵌套是子事务套在父事务中执行,子事务是父事务的一部分,在进入子事务之前,父事务建立一个回滚点,叫save point,然后执行子事务,这个子事务的执行也算是父事务的一部分,然后子事务执行结束,父事务继续执行。重点就在于那个save point。看几个问题就明了了:

如果子事务回滚,会发生什么?

父事务会回滚到进入子事务前建立的save point,然后尝试其他的事务或者其他的业务逻辑,父事务之前的操作不会受到影响,更不会自动回滚。

如果父事务回滚,会发生什么?

父事务回滚,子事务也会跟着回滚!为什么呢,因为父事务结束之前,子事务是不会提交的,我们说子事务是父事务的一部分,正是这个道理。那么:

事务的提交,是什么情况?

是父事务先提交,然后子事务提交,还是子事务先提交,父事务再提交?答案是第二种情况,还是那句话,子事务是父事务的一部分,由父事务统一提交。

 

Spring事务内部调用的一个坑

默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰

@Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。

如果我们在接口上标注@Transactional注解,会留下这样的隐患:因为注解不能被继承,所以业务接口中标注的@Transactional注解不会被业务实现类继承。所以可能会出现不启动事务的情况。所以,Spring建议我们将@Transaction注解在实现类上。

虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。

 

Spring事务管理

https://blog.csdn.net/donggua3694857/article/details/69858827

Spring事务管理的核心接口是PlatformTransactionManager ,事务管理器接口通过 getTransaction(TransactionDefinition def)方法根据指定的传播行为返回当前活动的事务或创建一个新的事务

TransactionDefinition 类中定义了它自己的传播行为和隔离级别 (常量)

调用PlatformTransactionManager接口的getTransaction()的方法得到的是TransactionStatus接口的一个实现

 

配置事务管理器

配置文件中配置事务管理器,配置了事务管理器后,事务当然还是得我们自己去操作,Spring提供了两种事务管理的方式:编程式事务管理和声明式事务管理

  1. 编程式事务管理我们可以通过PlatformTransactionManager实现来进行事务管理,同样的Spring也为我们提供了模板类TransactionTemplate进行事务管理,TransactionTemplate帮我们封装了许多代码,节省了我们的工作。
  2. 声明式事务管理有两种常用的方式,一种是基于tx和aop命名空间的xml配置文件,需要配置通知和切面切点。一种是基于@Transactional注解,随着Spring和Java的版本越来越高,大家越趋向于使用注解的方式。注解只需要在配置文件中开启对注解事务管理的支持<tx:annotation-driven>

 

Spring配置声明式事务

http://lzh166.iteye.com/blog/1134146

Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource、TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分。 DataSource、TransactionManager这两部分只是会根据数据访问方式有所变化。比如使用Hibernate进行数据访问 时,DataSource实际为SessionFactory,TransactionManager的实现为 HibernateTransactionManager。

根据代理机制的不同,Spring事务的配置又有几种不同的方式:

第一种方式:每个Bean都有一个代理

第二种方式:所有Bean共享一个代理基类

第三种方式:使用拦截器

第四种方式:使用tx标签配置的拦截器

第五种方式:全注解

 

posted @ 2018-04-14 11:19  污力豆豆  阅读(438)  评论(0编辑  收藏  举报