spring 事务 以及 数据库事务

事务的认识

我们在实际业务场景中,经常会遇到数据频繁修改读取的问题。在同一时刻,不同的业务逻辑对同一个表数据进行修改,这种冲突很可能造成数据不可挽回的错乱,所以我们需要用事务来对数据进行管理。
事务的概念
事务必须服从ACID原则。ACID指的是原子性(atomicity)、一致性(consistency)、隔离性(isolation)和持久性(durability)。
通俗理解,事务其实就是一系列指令的集合。
  1. 原子性:操作这些指令时,要么全部执行成功,要么全部不执行。只要其中一个指令执行失败,所有的指令都执行失败,数据进行回滚,回到执行指令前的数据状态。
  2. 原子性:操作这些指令时,要么全部执行成功,要么全部不执行。只要其中一个指令执行失败,所有的指令都执行失败,数据进行回滚,回到执行指令前的数据状态。
  3. 隔离性:在该事务执行的过程中,无论发生的任何数据的改变都应该只存在于该事务之中,对外界不存在任何影响。只有在事务确定正确提交之后,才会显示该事务对数据的改变。其他事务才能获取到这些改变后的数据。
  4. 持久性:当事务正确完成后,它对于数据的改变是永久性的。
事务的基本原理

Spring事务的本质其实就是数据库对事务的支持使用JDBC的事务管理机制,就是利用java.sql.Connection对象完成对事务的提交,那在没有Spring帮我们管理事务之前,我们要怎么做。

Connection conn = DriverManager.getConnection();
try {  
    conn.setAutoCommit(false);  //将自动提交设置为false                         
    //执行CRUD操作 
    conn.commit();      //当两个操作成功后手动提交  
} catch (Exception e) {  
    conn.rollback();    //一旦其中一个操作出错都将回滚,所有操作都不成功
    e.printStackTrace();  
} finally {
    conn.colse();
}
事务的并发

多用户访问数据库是常见的场景,这就是所谓的事务的并发。事务并发所可能存在的问题:
1.脏读:一个事务读到另一个事务未提交的更新数据。
2.不可重复读:一个事务两次读同一行数据,可是这两次读到的数据不一样。
3.幻读:一个事务执行两次查询,但第二次查询比第一次查询多出了一些数据行。
4.丢失更新:撤消一个事务时,把其它事务已提交的更新的数据覆盖了。

可以在java.sql.Connection中看到JDBC定义了五种事务隔离级别来解决这些并发导致的问题:

/**
 * A constant indicating that transactions are not supported. 
 */
int TRANSACTION_NONE         = 0;  //JDBC   驱动不支持事务 
/**
 * A constant indicating that
 * dirty reads, non-repeatable reads and phantom reads can occur.
 * This level allows a row changed by one transaction to be read
 * by another transaction before any changes in that row have been
 * committed (a "dirty read").  If any of the changes are rolled back, 
 * the second transaction will have retrieved an invalid row.
 */
int TRANSACTION_READ_UNCOMMITTED = 1; //允许脏读、不可重复读和幻读。
/**
 * A constant indicating that
 * dirty reads are prevented; non-repeatable reads and phantom
 * reads can occur.  This level only prohibits a transaction
 * from reading a row with uncommitted changes in it.
 */
int TRANSACTION_READ_COMMITTED   = 2; //禁止脏读,但允许不可重复读和幻读。 
/**
 * A constant indicating that
 * dirty reads and non-repeatable reads are prevented; phantom
 * reads can occur.  This level prohibits a transaction from
 * reading a row with uncommitted changes in it, and it also
 * prohibits the situation where one transaction reads a row,
 * a second transaction alters the row, and the first transaction
 * rereads the row, getting different values the second time
 * (a "non-repeatable read").
 */
int TRANSACTION_REPEATABLE_READ  = 4; //禁止脏读和不可重复读,单运行幻读。 
/**
 * A constant indicating that
 * dirty reads, non-repeatable reads and phantom reads are prevented.
 * This level includes the prohibitions in
 * <code>TRANSACTION_REPEATABLE_READ</code> and further prohibits the 
 * situation where one transaction reads all rows that satisfy
 * a <code>WHERE</code> condition, a second transaction inserts a row that
 * satisfies that <code>WHERE</code> condition, and the first transaction
 * rereads for the same condition, retrieving the additional
 * "phantom" row in the second read.
 */
int TRANSACTION_SERIALIZABLE     = 8; //禁止脏读、不可重复读和幻读。

这几个常量就是
TRANSACTION_NONE JDBC 驱动不支持事务
TRANSACTION_READ_UNCOMMITTED 允许脏读、不可重复读和幻读。
TRANSACTION_READ_COMMITTED 禁止脏读,但允许不可重复读和幻读。
TRANSACTION_REPEATABLE_READ 禁止脏读和不可重复读,单运行幻读。
TRANSACTION_SERIALIZABLE 禁止脏读、不可重复读和幻读。

隔离级别越高,意味着数据库事务并发执行性能越差,能处理的操作就越少。
通过conn.setTransactionLevel去设置你需要的隔离级别。

JDBC规范虽然定义了事务的以上支持行为,但是各个JDBC驱动,数据库厂商对事务的支持程度可能各不相同。

出于性能的考虑我们一般设置TRANSACTION_READ_COMMITTED就差不多了,剩下的通过使用数据库的锁来帮我们处理别的。

事务大致了解之后 那么spring事务又是怎么处理的呢?

有了Spring,我们再也无需要去处理获得连接、关闭连接、事务提交和回滚等这些操作,使得我们把更多的精力放在处理业务上。事实上Spring并不直接管理事务,而是提供了多种事务管理器。他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。

spring 事务

首先 spring支持 编程式事务管理 和 声明式事务管理 两种方式。

编程式事务管理
编程式事务管理编程式事务使用TransactionTemplate或者直接使用底层的
PlatformTransactionManager。对于编程式事务管理,
spring推荐使用TransactionTemplate。
声明式事务管理
声明式事务声明式事务是建立在AOP之上的。其本质是对方法前后进行拦截,
然后在目标方法开始之前创建或者加入一个事务,
在执行完目标方法之后根据执行情况提交或者回滚事务。
声明式事务最大的优点就是不需要通过编程的方式管理事务,
这样就不需要在业务逻辑代码中掺杂事务管理的代码,
只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),
便可以将事务规则应用到业务逻辑中。

显然声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式。声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就可以获得完全的事务支持。和编程式事务相比,声明式事务唯一不足地方是,它的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。但是即便有这样的需求,也存在很多变通的方法,比如,可以将需要进行事务管理的代码块独立为方法等等。声明式事务管理也有两种常用的方式,一种是基于tx和aop名字空间的xml配置文件,另一种就是基于@Transactional注解。显然基于注解的方式更简单易用,更清爽。

spring方式
开启事务注解标记@Transactional

Spring在jdbc中提供了一个事务管理组件DataSourceTransactionManager

<!-- 配置事务管理组件 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource”> 
</bean>
<!-- 开启事务注解标记@Transactional -->
<tx:annotation-driven transaction-manager=“txManager" />  

配置上面的信息后,Spring在初始化包含Transactional注解的类时,会自动生成这些类的代理,并放置再容器中,以便备用。

spring boot
spring boot中打开事务的几种方式:

1、自动装载
spring-boot-autoconfigure jar中 spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration
@TransactionAutoConfiguration->EnableTransactionManagement
2、手功启动事务管理 @EnableTransactionManagemen
@EnableTransactionManagemen ->TransactionManagementConfigurationSelector->ProxyTransactionManagementConfiguration->TransactionInterceptor

具体实现:
::Transactional 实现事务管理是通过TransactionInterceptor拦截器工作的。::
主要关注两点:
::Spring 会调用TransactionInterceptor在目标方法执行前后进行拦截::
::获取数据库连接是通过当前线程获取的,同一线程获取的连接是同一个::

数据库事务

数据库事务的概念:
用一句话简单的说明:数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作(对数据库的相关增删改查的操作),要么完全地执行,要么完全地不执行。
事务的隔离级别

隔离级别一共有四种:读未提交、读已提交、可重复读、串行化。这四种隔离级别分别可以解决的不同的问题:

  1. 丢失修改
    A和B两个事物同事修改同一个数据,A修改的提交在B提交之后,导致B好像没有修改,丢失修改。

  2. 脏读
    B事务修改了一个数据并未提交,A事物读取了这个数据,然后B事务回滚了,最后A又读取了一次,两次读取的数据不一致,称为脏读。

  3. 不可重复读
    A事务读取了一个数据后,B事务修改了这个数据,A事务又读取了这个数据,两次读取的数据也不一致,称为不可重复读。

  4. 幻读
    A事务更新了某个字段(范围是整个数据表的)(以id=1为条件的),B事务又插入了一条新的记录,导致A事务认为自己没有完全更新过来,就像出现幻觉一样。
    针对这几种错误分别设置不同的隔离级别来解决:

    第一种丢失修改一般使用加锁锁来解决,因此串行化可以解决,并且串行化可以解决上面出现的所有问题。
    第二种问题脏读是因为读取其他事物未提交的数据,因为设置读已提交隔离级别可以解决这个问题。但不可解决不可重复读和幻读的问题。
    第三种问题不可能重复读,是因为B事物的修改影响了A事务的读取数据,设置可重复读隔离级别,使得B事务修改数据和A事务读取数据互不影响,隔离开来,从而解决这个问题,同时解决 脏读问题
    第四种幻读问题,是因为A事务更新完数据后,B事务又插入了新的数据,设置串行化隔离级别可解决,并且这种隔离级别解决上面所有的问题。除了串行化,多版本并发控制(MVCC,Multiversion Concurrency Control)机制也可以解决该问题。

数据库事务与spring事务的区别
数据库事务和spring事务 本质上其实是同一个概念,spring的事务是对数据库的事务的封装,
最后本质的实现还是在数据库,假如数据库不支持事务的话,spring的事务是没有作用的.
数据库的事务说简单就只有开启,回滚和关闭,spring对数据库事务的包装,
原理就是拿一个数据连接,根据spring的事务配置,操作这个数据连接对数据库进行事务开启,
回滚或关闭操作.但是spring除了实现这些,还配合spring的传播行为对事务进行了更广泛的管理.
其实这里还有个重要的点,那就是事务中涉及的隔离级别,
以及spring如何对数据库的隔离级别进行封装.事务与隔离级别放在一起理解会更好些.
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值