[小白进阶日记]Spring 事务管理

Spring 事务管理

提到数据库肯定有事务一谈,那么什么是事务呢?

数据库事务( transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。事务由事务开始与事务结束之间执行的全部数据库操作组成。 --百度百科

事务的性质

事务必须要遵守ACID 也就是遵守原子性、一致性、隔离性、持久性

  • 原子性(Atomicity):事务中的全部操作在数据库中是不可分割的,要么全部完成,要么全部不执行
  • 一致性(Consistency):几个并行执行的事务,其执行结果必须与按某一顺序串执行的结果相一致(数据库运行中出现故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是 不一致的状态)
  • 隔离性(Isolation):事务的执行不受其他事务的干扰,事务执行的中间结果对其他事务必须是透明的(
    即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。 )
  • 持久性(Durability):对于任意已提交事务,系统必须保证该事务对数据库的改变不被丢失,即使数据库出现故障。
在了解了事务的性质之后我们开始了解一下并发事务可能会导致的问题

在多个事务处理同一个数据时,如果没有采取有效的隔离机制,那么并发处理数据的时候就会带来数据不一致的问题

  1. 第一种丢失更新(撤销一个事务时,把其他事务提交更新的数据覆盖)
  1. 脏读 (一个事务处理过程中,读取到了另一个没有提交事务中的数据,假如没有提交的事务进行了回退,那么正常事务中读取到的数据是无效的)
  1. 幻读/虚读 (一个事务执行两次完全相同的查询时,第二次结果集与第一次的结果集不同)
  1. 不可重复度 (一个事务执行两次完全相同查询时,第二次结果与第一次结果不同)
  1. 第二种丢失更新 (不可重复读的特殊情况 两个事务同时对一条数据进行修修改,并提交,第一个事务或者第二个事务做的改变丢失)
脏读、幻读/虚读、不可重复读的区别:

PS:前提 描述中涉及到的事务都是操作的同一个(组)数据 !!

  1. 不可重复读与脏读的区别是:
  • 不可重复读是在事务发生时另一个事务对数据进行了修改并提交导致读取到的数据不可信。(第二事务已经提交)
  • 脏读是事务发生时读取到了另一个没有提交的数据导致数据不可信。(第二事务未提交)
  1. 不可重复读和幻读/虚读的区别是:
  • 不可重复读查询的是同一条数据。(针对单条记录)
  • 幻读/虚读查询的是一批数据。(针对多条记录)
既然有了事务会产生的问题那么我们有必要来了解一下事务的隔离级别
事务的隔离级别有4种 从小到大:
1.Read uncommitted (最低级别,什么都不保证)
    读取  未提交
    如名,就是一个事务可以读取到另一个事务没有提交的数据 (说白了就是 脏读)
    那么如何解决这个问题呢?  Read committed 读取提交 就可以解决脏读现象
2.Read committed (可以避免脏读的发生) **orcale 默认级别  大部分数据库默认的级别**
    读取 提交
    如名,就是一个事务要等另一个事务提交之后才能读取数据
    那么 举个例子  A 拿着卡 去买东西 当A正在付款的时候(A事务开启) B把卡中的钱全部转走(B事务开启并提交了),当收费系统进行扣款时检测A卡中余额发现卡中没钱了。
    这个就是 读提交,如果有事务对数据进行更新时,读提交需要等待这个更新操作事务提交之后才能读取数据,在上面例子中 出现了一个事务范围 查询两个相同的查询缺返回了不同的数据,这就是不可重复读。
    那么如何解决这个问题呢?Repeatable read  可重复读!!
3. Repeatable read(可以避免脏读,不可重复读的发生)   **Mysql 默认**
    可重复读(重复读) 
    就是在读取事务开启时,不在允许修改操作
    But  这个 可重复读 对应的是Update操作  那么 如果新插入一条数据还是会出现幻读现象
    那么如何解决这个问题呢? Serializable 序列化
4. Serializable(可以避免脏读、幻读、不可重复读的发生)
    serializable 是最高的事务隔离级别,在这个级别下,事务串行化顺序执行,直接避免了脏读、幻读、不可重复读,但是这个级别的效率极其低下,一般并不使用。        
那么了解了事务和事务的隔离级别我们一起来看一下Spring中是怎么实现事务管理的

Spring 中实现事务管理的高层抽象接口主要有三个

  • PlatformTransactionManager  事务管理器
    
  • TransactionDefinition 事务定义信息(隔离解绑、传播行为、超时时间、是否只读)
    
  • TransactionStatus 事务运行状态
    
事务管理器 PlatformTransactionManager

Spring为不同的持久层框架提供了不同的PlatformTransactionManager接口的实现

  • 使用Spring JDBC 或者iBatis 时需要实现的是org.springframework.jdbc.datasource.DataSourceTransactionManager
  • 使用Hibernate3.0 时 需要实现的是 org.springframework.orm.hibernate3.HibernateTransactionManager
  • 使用JPA时 需要实现的是 org.springframework.orm.jpa.JpaTransactionManager
  • 使用Jdo时 org.springframework.jdo.JdoTransactionManager
  • 等等等等。。。。。。
事务定义信息 TransactionDefinition
事务隔离级别(四种)
  • Default 使用数据库默认的级别
  • READ_UNCOMMITED 允许读取没有提交事务的数据
  • READ_COMMITED 允许在并发事务已经提交之后读取,可防止脏读
  • REPEATABLE_READ 对相同字段读取结果是一致的,可以防止脏读、不可重复读
  • SERIALIZABLE 完全服从ACID 的隔离级别,性能是最低的,隔离级别是最高的
事务传播行为(七种)
  1. PROPAGATION_REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务 中,加入到这个事务中。 常用
  2. PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。 pass 开事务 你不给我事务执行?
  3. PROPAGATION_MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。 pass 抛异常让我程序死啊!
  4. PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。 pass 有事务了 把这个事务挂起来?在新建一个???玩我呢?
  5. PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前 事务挂起。 pass 我可以安安静静的用一下事务嘛
  6. PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。 pass 我要用事务啊!!
  7. PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则 执行与PROPAGATION_REQUIRED类似的操作。 OK
事务的运行状态(五种)
  1. 活动状态
    事务在执行时的状态叫活动状态。
  2. 部分提交状态
    事务中最后一条语句被执行后的状态叫部分提交状态。
  3. 失败状态
    事务不能正常执行的状态叫失败状态。
  4. 提交状态
    事务在部分提交后,将往硬盘上写入数据,当最后一条信息写入后的状态叫提交状态。进入提交状态的事务就成功完成了。
  5. 中止状态
    事务回滚并且数据库已经恢复到事务开始执行前的状态叫中止状态。
那么我们开始使用XML进行声明事务(基于tx/aop)

编写XML配置文件

<?xml version="1.0" encoding="UTF-8"?><beans 
xmlns="http://www.springframework.org/schema/beans"       
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       
xmlns:context="http://www.springframework.org/schema/context"       
xmlns:aop="http://www.springframework.org/schema/aop"       
xmlns:tx="http://www.springframework.org/schema/tx"       
xsi:schemaLocation="http://www.springframework.org/schema/beans        
http://www.springframework.org/schema/beans/spring-beans.xsd        http://www.springframework.org/schema/context        
http://www.springframework.org/schema/context/spring-context.xsd        http://www.springframework.org/schema/aop        
http://www.springframework.org/schema/aop/spring-aop.xsd        http://www.springframework.org/schema/tx        
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--读取JDBC配置文件-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <!--dataSource-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="${jdbc.url}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="username" value="${jdbc.username}"></property>
    </bean>
    <!--声明JDBCTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    进行事务控制 事务管理器 事务定义信息 事务状态信息
    事务管理器:配置
    -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
        <!--tx配置-->
   <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!--事务定义信息-->
            <tx:method name="transfer*" read-only="false"/>
                        <!--name 配置 拦截的方法名 *是通配符   propagation 是事务的传播行为  isolation 是隔离级别  read-only 是否只读(默认false  不是只读) timeout 超时时间 -1是永不超时  然而设置任何 都是永不超时  -->
            <tx:method name="update*" propagation="REQUIRED" isolation="DEFAULT" read-only="false" timeout="-1"/>
            <tx:method name="find*" read-only="true"/>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="save*" read-only="false"/>
            <tx:method name="add*" read-only="false"/>
            <tx:method name="*" read-only="true"/>
        </tx:attributes>
    </tx:advice>
    <!--aop配置-->
    <aop:config>
                                            <!--execuion 表达式  (*.包名..*(..))  意思是 包下所有类中所有方法-->
        <aop:pointcut expression="execution(* com.baidu.service..*(..))" id="poi1"/>
        <!-- advice-ref 使用哪个 tx配置       printcut-ref 拦截些类中方法-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="poi1"/>
    </aop:config>
</beans>
那么我们开始使用注解形式进行事务声明

编写XML配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">
          <!--扫描-->
    <context:component-scan base-package="com.baidu"></context:component-scan>
    <!--开启注解-->
    <context:annotation-config/>
    <!--读取JDBC配置文件-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <!--dataSource-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="${jdbc.url}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="username" value="${jdbc.username}"></property>
    </bean>
    <!--声明JDBCTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--
    进行事务控制 事务管理器 事务定义信息 事务状态信息
    事务管理器:配置
    -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--开启注解的事务配置-->
    <tx:annotation-driven/>
</beans>

在编写方法的时候就可以在方法上加上:
@Transactional(propagation = 事务的传播行为,isolation = 数据库的隔离级别,timeout = 超时时间,readOnly = 是否只读)
也可以在类上编写,这样整个类的事务设置全部都是类上Transactional 设置的属性

bye~

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值