Java事务小结

来源:互联网 发布:知乎折叠 编辑:程序博客网 时间:2024/05/17 04:19

1.事务的简单理解
  一组由多个操作形成的工作单元,该工作单元的所有操作要么全部执行,要么全部取消.
  在对数据库进行操纵时候需要考虑事务,事务一般交给应用程序来设置,最终在数据库端执行.
  所以一般要求数据库服务器支持事务处理.

2.事务类型

   在java中一般分为两种:全局事务,本地事务
  全局事务采用JTA控制,可以控制多个数据源.
  本地事务即JDBC事务,由Connection控制,不能跨数据源.

  java.sql.Connection 提供了以下控制事务的方法:

 

3.事务以及Spring事务管理
  J2ee开发中常用Spring来控制事务. 一般采用声明式事务管理器,基于Aop实现
  3.1 事务的四个特性(ACDI)
    Atomic(原子性)
    一个事务被看成一个工作单元,要么全部成功,要么全部失败.
    Consistency(一致性)
    要求事务必须确保数据库的状态保持一致,这就是说事务开始时,数据库的状态是一致的;在事务结束时,数据库的状态也必须是一致的
    Isolation(隔离性)
    要求系统必须保证事务不受其他并发执行的事务的影响.
    Durability(持久性)
    事务一旦成功,在系统中的变化时持久的.

  3.2 事务的传播属性

      PROPAGATION_REQUIRED       -- 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
      PROPAGATION_SUPPORTS       -- 支持当前事务,如果当前没有事务,就以非事务方式执行。
      PROPAGATION_MANDATORY      -- 支持当前事务,如果当前没有事务,就抛出异常。
      PROPAGATION_REQUIRES_NEW   -- 新建事务,如果当前存在事务,把当前事务挂起。
      PROPAGATION_NOT_SUPPORTED  -- 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
      PROPAGATION_NEVER  -- 以非事务方式执行,如果当前存在事务,则抛出异常。
      PROPAGATION_NESTED -- 如果当前存在事务,则在嵌套事务内执行。
                            如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。
      前六个策略类似于EJB CMT,第七个(PROPAGATION_NESTED)是Spring所提供的一个特殊变量。
      它要求事务管理器或者使用JDBC 3.0 Savepoint API提供嵌套事务行为(如Spring的DataSourceTransactionManager)

      常见情形一:
       

      当程序调用ServiceA.methodA()前Spring容器会根据事物的传播属性来判断是否需要开启事务

      比如目前是PROPAGATION_REQUIRED(假设目前没有事务),则调用ServiceA.methodA()前Spring会开启一个事务.
      常见情形二:
     

      Spring容器会根据ServiceA的事物传播属性PROPAGATION_REQUIRED(假设目前没有事务)在调用ServiceA.methodA()前开启一个事务,当执行到ServiceB.methodB(),因为目前已经在一个事务中,ServiceB的事物传播属性为PROPAGATION_REQUIRED,所以spring不会再开启事务.
      什么时候会新起一个事务呢?当ServiceB的事务传播属性设为为PROPAGATION_REQUIRES_NEW,spring会把当前事务挂起,重新开启新的事务.

   3.3 事务的隔离级别以及并发控制(来自网上)
   事务隔离级别属于处理多事务并发的问题
    1 Serializable:最严格的级别,事务串行执行,资源消耗最大;
    2 REPEATABLE READ:读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。避免了“脏读取”和“不可重复读取”的情况,但是带来了更多的性能损失。
    3 READ COMMITTED:大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”。该级别适用于大多数系统。
    4 Read Uncommitted:最低的事务隔离级别,保证了读取过程中不会读取到非法数据。
  并行可以提高数据库的吞吐量和效率,但是并不是所有的并发事务都可以并发运行,这需要查看数据库教材的可串行化条件判断

  并发中可能发生的3种情况
    1 Dirty reads(脏读):一个事务读取了另一个未提交的并行事务写的数据。也就是说,比如事务A的未提交(还依然缓存)的数据被事务B读走,如果事务A失败回滚,会导致事务B所读取的的数据是错误的。
    2 non-repeatable reads(不可重复读,同一事务多次读取不一致):一个事务重新读取前面读取过的数据,发现该数据已经被另一个已提交的事务修改过。比如事务A中两处读取数据-total-的值。在第一读的时候,total是100,然后事务B就把total的数据改成200,事务A再读一次,结果就发现,total竟然就变成200了,造成事务A数据混乱。
    3 phantom reads(幻读):一个事务重新执行一个查询,返回一套符合查询条件的行,发现这些行因为其他最近提交的事务而发生了改变。这个和non-repeatable reads相似,也是同一个事务中多次读不一致的问题。但是phantom reads所要读的数据的不一致却不是他所要读的数据集改变,而是他的条件数据集改变。比如Select account.id where account.name="ppgogo*",第一次读去了6个符合条件的id,第二次读取的时候,由于事务b把一个帐号的名字由"dd"改成"ppgogo1",结果取出来了7个数据。

   隔离级别总结:

                                    Dirty reads          non-repeatable reads            phantom reads
   Serializable                     不会                    不会                                           不会
   REPEATABLE READ          不会                    不会                                            会
   READ COMMITTED           不会                    会                                               会
   Read Uncommitted          会                       会                                              会
 
  常见的并发控制策略(from:深入浅出hibernate)
    1.悲观锁
       悲观锁大多情况利用数据库的锁机制实现(利用for update子句),以保证操作操作最大程度的独占性,随之而来的是数据库性能的大开销,特别是对长事务而言,这样开销无法承受.
      ex.select * from account where name = "Erica" for update
      这条Sql锁定了account表中所有符合检索条件(name="Erica")的记录.本次事务提交前(事务提交时会释放过程中的锁),外界无法修改这些记录.
     2.乐观锁
      大多基于数据版本(Version)记录机制实现.既在数据库表中增加一个"version"字段来实现.
      读取数据时,将此版本号一同读出,之后更新,将此版本号加1.此时,将提交的数据的版本数据与数据库对应记录的当前版本信息比较,如果提交的数据版本号大于数据当前版本号, 则给予更新,否则认为是过期数据.
  
一些有帮助的参考
1.http://www.pgsqldb.org/pgsqldoc-7.4/transaction-iso.html
2.http://msdn.microsoft.com/zh-cn/library/ms189122.aspx
3.http://yuquan-nana.javaeye.com/blog/295639
原创粉丝点击