欢迎来到农夫的博客

半亩方塘一鉴开, 天光云影共徘徊。 问渠哪得清如许? 为有源头活水来!

事务的概念

一、事务                    

  事务是访问数据库的一个操作序列,数据库应用系统通过事务集来完成对数据库的存取。事物的正确执行是的数据库从一个状态转换成另一个状态。

  事务必须服从ISO/IEC所指定的ACID原则。

  • 原子性(atomicity)。即不可分割性,事务要么全部被执行,要么就全部不被执行。如果事务的所有子事务全部被提交成功(所有的数据库操作被提交),则数据库的状态发生转换;如果子事务执行失败,则其他的子事务的数据库操作被回滚,即数据库回到事务执行的状态之前,不会发生状态转换。
  • 一致性或可串性(consistency)。事务的执行使得数据库从一种正确状态转换成另一种正确状态。 如果事务启动时数据是一致的,那么当这个事物成功后数据库的数据也必须是一致的。  
  • 隔离性(isolation)。在事务提交之前,不允许把该事务对数据的任何改变提供给任何其他事务。
  • 持久性(durability)。事务正确提交后,其结果将永久保存在数据库中,即使在事务提交后有了其他故障,事务的处理结果也会得到保存。
  • 注意:银行存取款中, 事务的执行过程中,必须允许暂时的不一致性,因为无论是A账户取出操作在前还是B账户存入操作在前,这两个操作必然有一个先后顺序,两个操作之间就会产生不一致。于是乎,在原子性和一致性的双重作用之下,事务就能够正确、有效地执行,实现响应的逻辑功能。

    所以说原子性和一致性是有区别的,但是两者又是互补的,不能隔离开来。

  • COMMIT 和 ROLLBACK 在大多数情况下,通过执行COMMIT或者ROLLBACK语句来终止事务。 当执行COMMIT语句时,自从事务启动以来对数据库所做的一切操作就会成为永久--即所有的改变被写入磁盘。         当执行ROLLBACK语句时,自从事务启动以来对数据库所做的一切操作都被撤销,且数据库返回到事务开始之前所处的状态。 不管哪种情况,数据库在事务完成时都保证能回到一致状态。

二、数据系统的两种事务模式    

  • 自动提交模式:每个SQL语句都是一个独立的事务,当数据库系统执行完一个SQL语句后,会自动提交事务。
  • 手动提交模式:必须由数据库客户端程序显示指定事务开始边界和结束边界。

  MySQL中数据库表常用的分为3种类型:INNODB,BDB和MyISAM,INNODB和BDB属于事务安全类表。

三、隔离级别          

  对于访问数据库相同数据的多个事务,如果没有采取必要的隔离机制,就会导致并发问题,这些并发问题可归纳为以下几类

  1. 更新遗失:两个事务都同时更新一行数据,一个事务对数据的更新把另一个事务对数据的更新覆盖了。这是因为系统没有执行任何的锁操作,因此并发事务并没有被隔离开来。
  2. 脏读:一个事务读取到另一个事务未提交的更新数据。因为没有最终提交,有可能是不正确的数据。
  3. 不可重复读:一个事务读到另一个事务已提交的更新数据。另一个事务提交前后的数据不一致。
  4. 第二类丢失更新:这是不可重复读中的特例,一个事务覆盖另一个事务已提交的更新数据。
  5. 幻读:一个事务读到另一个事务已提交的新插入的数据(事务1读取记录时事务2增加了记录并提交,事务1再次读取时可以看到事务2新增的记录;)。

  隔离机制(从高到低)如下:

  • 可串行化(Serializable)读加共享锁,写加排它锁。 读取事务可以并发,写事务之间是互斥的,必须一个一个的执行事务。
  • 可重复读(Repeatable Read)读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。避免了不可重复读取和脏读,但是有时可能出现幻读。这可以通过“共享读锁”和“排他写锁”实现。
  • 读已提交数据(Read Committed)读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。该隔离级别避免了脏读,但是却可能出现不可重复读。事务A事先读取了数据,事务B紧接了更新了数据,并提交了事务,而事务A再次读取该数据时,数据已经发生了改变。
  • 读未提交数据(Read Uncommitted)如果一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。避免了更新丢失,却可能出现脏读。也就是说事务B读取到了事务A未提交的数据

隔离机制对应产生的问题(Y引起问题):

隔离机制/并发问题 LU丢失更新 DR脏读 NRR非重复读 SLU二类丢失更新 PR幻读
未提交读(Read Uncomitted)  N  Y  Y  Y  Y
提交读(Read Commited)  N     N  Y  Y  Y
重复读(Repeatable Read)  N  N  N  N  Y
串行化(Serializable)  N  N  N  N  N

 

 

 

 

 

四、Spring 事务管理和实现    

这里简单的介绍一下声明式,使用注解方式的事务管理和基于AspectJ的xml配置方式。

  <!-- 事务管理 对mybatis操作数据的事务控制,spring使用jdbc的事务控制类 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <!-- 管理事务的两种方式 -->
    <!-- 1、基于注释的事务,当注释中发现@Transactional时,使用id为“transactionManager”的事务管理器  -->
    <!-- 如果没有设置transaction-manager的值,则spring以缺省默认的事务管理器来处理事务,默认事务管理器为第一个加载的事务管理器 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
    
    <!-- 2、使用切面实现事务 通知 ,也可以不配置切面管理事务,可以使用annotation方式-->
    <!-- <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            传播行为
            <tx:method name="save*" propagation="REQUIRED"/>
            <tx:method name="delete*" propagation="REQUIRED"/>
            <tx:method name="insert*" propagation="REQUIRED"/>
            <tx:method name="update*" propagation="REQUIRED"/>
            <tx:method name="find*" propagation="SUPPORTS"/>
            <tx:method name="get*" propagation="SUPPORTS"/>
            <tx:method name="select**" propagation="SUPPORTS"/>
        </tx:attributes>
    </tx:advice>

使用单元测试进行测试事务

@Transactional
    @Test
    public void getShopUserById(){
        ShopUser shopUser = new ShopUser();
        shopUser.setUserid(6);
        shopUser.setUsername("zhangsan");
        shopUser.setPassword("333");
        shopUser.setCreateTime(Long.parseLong(DateUtil.getString(new Date(), DateUtil.YMDHMS)));
        try {
            shopUserService.insertSelective(shopUser);
            int i = 2/0;
        } catch (Exception e) {
            e.printStackTrace();
            logger.info("ShopControllerTest" + e);
        }

 

 

 

转载: http://www.cnblogs.com/kristain/articles/2038397.html

posted on 2016-09-29 09:33  allTime  阅读(248)  评论(0编辑  收藏  举报

导航