Java 事务

1、什么是事务?

事务(Transaction),一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。

事务通常由高级数据库操纵语言或编程语言(如SQL,C++或Java)书写的用户程序的执行所引起,并用形如begin transactionend transaction语句(或函数调用)来界定。事务由事务开始(begin transaction)和事务结束(end transaction)之间执行的全体操作组成。

2、为什么要事务?

事务是为解决数据安全操作提出的,事务控制实际上就是控制数据的安全访问。

用一个简单例子说明:银行转帐业务,账户A要将自己账户上的1000元转到B账户下面,A账户余额首先要减去1000元,然后B账户要增加1000元。假如在中间网络出现了问题,A账户减去1000元已经结束,B因为网络中断而操作失败,那么整个业务失败,必须做出控制,要求A账户转帐业务撤销。这才能保证业务的正确性,完成这个操走就需要事务,将A账户资金减少和B账户资金增加放到同一个事务里,要么全部执行成功,要么全部撤销,这样就保证了数据的安全性。

3、事务的4个特性(ACID)

  • 原子性(atomicity):事务是数据库的逻辑工作单位,而且是必须是原子工作单位,对于其数据修改,要么全部执行,要么全部不执行。

  • 一致性(consistency):事务在完成时,必须是所有的数据都保持一致状态。在相关数据库中,所有规则都必须应用于事务的修改,以保持所有数据的完整性。(实例:转账,两个账户余额相加,值不变。)

  • 隔离性(isolation):一个事务的执行不能被其他事务所影响。

  • 持久性(durability):一个事务一旦提交,事物的操作便永久性的保存在DB中。即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。

4、Java有几种类型的事务?

Java事务的类型有三种:JDBC事务、JTA(Java Transaction API)事务、容器事务。

4.1 JDBC事务

在JDBC中处理事务,都是通过Connection完成的。同一事务中所有的操作,都在使用同一个Connection对象。JDBC事务默认是开启的,并且是默认提交。

JDBC Connection 接口提供了两种事务模式:自动提交和手工提交

JDBC中的事务java.sql.Connection 的三个方法与事务有关:

  • setAutoCommit(boolean):设置是否为自动提交事务,如果true(默认值为true)表示自动提交,也就是每条执行的SQL语句都是一个单独的事务,如果设置为false,需要手动提交事务。
  • commit():提交结束事务。
  • rollback():回滚结束事务。

4.1.1 传统JDBC操作流程:

  • 获取JDBC连接
  • 声明SQL
  • 预编译SQL
  • 执行SQL
  • 处理结果集
  • 释放结果集
  • 释放Statement
  • 提交事务
  • 处理异常并回滚事务
  • 释放JDBC连接

4.1.2 JDBC优缺点:

  • 冗长、重复
  • 显示事务控制
  • 每个步骤不可获取
  • 显示处理受检查异常

JDBC为使用Java进行数据库的事务操作提供了最基本的支持。通过JDBC事务,我们可以将多个SQL语句放到同一个事务中,保证其ACID特性。JDBC事务的主要优点就是API比较简单,可以实现最基本的事务操作,性能也相对较好。但是,JDBC事务有一个局限:一个 JDBC 事务不能跨越多个数据库!所以,如果涉及到多数据库的操作或者分布式场景,JDBC事务就无能为力了。

4.2 JTA事务(Java Transaction API)

JTA(Java Transaction API)提供了跨数据库连接(或其他JTA资源)的事务管理能力。JTA事务管理则由JTA容器实现,J2ee框架中事务管理器与应用程序,资源管理器,以及应用服务器之间的事务通讯。

4.2.1 JTA的构成

a、高层应用事务界定接口,供事务客户界定事务边界的

b、X/Open XA协议(资源之间的一种标准化的接口)的标准Java映射,它可以使事务性的资源管理器参与由外部事务管理器控制的事务中

c、高层事务管理器接口,允许应用程序服务器为其管理的应用程序界定事务的边界

4.2.2 JTA的主要接口:位于javax.transaction包中

a、UserTransaction接口:让应用程序得以控制事务的开始、挂起、提交、回滚等。由Java客户端程序或EJB调用。

b、TransactionManager 接口:用于应用服务器管理事务状态

c、Transaction接口:用于执行相关事务操作

d、XAResource接口:用于在分布式事务环境下,协调事务管理器和资源管理器的工作

e、Xid接口:为事务标识符的Java映射

注:前3个接口位于Java EE版的类库 javaee.jar 中,Java SE中没有提供!UserTransaction是编程常用的接口,JTA只提供了接口,没有具体的实现。

JTS(Java Transaction Service)是服务OTS的JTA的实现。简单的说JTS实现了JTA接口,并且符合OTS的规范。

JTA的事务周期可横跨多个JDBC Connection生命周期,对众多Connection进行调度,实现其事务性要求。

JTA可以处理任何提供符合XA接口的资源。包括:JDBC连接,数据库,JMS,商业对象等等。

4.2.3 JTA编程的基本步骤

//a、首先配置JTA ,建立相应的数据源
//b、建立事务:通过创建UserTransaction类的实例来开始一个事务。代码如下:
    Context ctx = new InitialContext(p) ;

    UserTransaction trans = (UserTransaction) ctx.lookup("javax. Transaction.UserTransaction")
//c1、开始事务:
trans.begin() ;

//d、找出数据源:从Weblogic Server上找到数据源:
DataSource ds = (DataSource) ctx.lookup(“mysqldb") ;

//e、建立数据库连接:
Connection mycon = ds.getConnection() ;

//f、执行SQL操作:
stmt.executeUpdate(sqlS);

g、完成事务:
trans.commit(); / trans.rollback();

h、关闭连接:
mycon.close() ;

4.2.4 优缺点:

JTA的优点很明显,就是提供了分布式事务的解决方案,严格的ACID。但是,标准的JTA方式的事务管理在日常开发中并不常用。

JTA的缺点是实现复杂,通常情况下,JTA UserTransaction需要从JNDI获取。这意味着,如果我们使用JTA,就需要同时使用JTA和JNDI。

JTA本身就是个笨重的API,通常JTA只能在应用服务器环境下使用,因此使用JTA会限制代码的复用性。

4.3 Spring容器事务

Spring事务管理的实现有许多细节,如果对整个接口框架有个大体了解会非常有利于我们理解事务,下面通过讲解Spring的事务接口来了解Spring实现事务的具体策略。

Spring事务管理涉及的接口及其联系:

20180919164820400.png

Spring并不直接管理事务,而是提供了多种事务管理器,他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。 Spring事务管理器的接口是org.springframework.transaction.PlatformTransactionManager,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。

Public interface PlatformTransactionManager{  
       // 由TransactionDefinition得到TransactionStatus对象
       TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
    // 提交
       Void commit(TransactionStatus status) throws TransactionException;  
       // 回滚
       Void rollback(TransactionStatus status) throws TransactionException;  
}

4.3.1 Spring JDBC事务

如果应用程序中直接使用JDBC来进行持久化,DataSourceTransactionManager会为你处理事务边界。为了使用 DataSourceTransactionManager,你需要使用如下的XML将其装配到应用程序的上下文定义中:


     

实际上,DataSourceTransactionManager是通过调用java.sql.Connection来管理事务。通过调用连接的commit()方法来提交事务,同样,事务失败则通过调用rollback()方法进行回滚。

4.3.2 Hibernate事务

如果应用程序的持久化是通过Hibernate实现的,那么你需要使用HibernateTransactionManager。对于Hibernate3,需要在Spring上下文定义中添加如下的声明:


     

sessionFactory属性需要装配一个Hibernate的session工厂,HibernateTransactionManager的实现细节是它将事务管理的职责委托给org.hibernate.Transaction对象,而后者是从Hibernate Session中获取到的。当事务成功完成时,HibernateTransactionManager将会调用Transaction对象的commit()方法,反之,将会调用rollback()方法。

4.3.3 Java持久化API事务(JPA)

Hibernate多年来一直是事实上的Java持久化标准,但是现在Java持久化API作为真正的Java持久化标准进入大家的视野。如果你计划使用JPA的话,那你需要使用Spring的JpaTransactionManager来处理事务。你需要在Spring中这样配置JpaTransactionManager:


     

JpaTransactionManager只需要装配一个JPA实体管理工厂(javax.persistence.EntityManagerFactory接口的任意实现)。JpaTransactionManager将与由工厂所产生的JPA EntityManager合作来构建事务。

  • 基本的事务属性的定义:

事务管理器接口PlatformTransactionManager通过getTransaction(TransactionDefinition definition)方法来得到事务,这个方法里面的参数是TransactionDefinition类,这个类就定义了一些基本的事务属性。

事务属性可以理解成事务的一些基本配置,描述了事务策略如何应用到方法上。 事务属性包含了5个方面:传播行为、隔离规则、回滚规则、事务超时、是否只读

TransactionDefinition:

public interface TransactionDefinition {
    /** 返回事务传播行为 
        当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。
        例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。
*/
    int getPropagationBehavior();

    /** 返回事务的隔离级别,事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据 
        隔离级别定义了一个事务可能受其他并发事务影响的程度。
        在实际开发过程中,我们绝大部分的事务都是有并发情况。多个事务并发运行,经常会操作相同的数据来完成各自的任务。
        在这种情况下可能会导致以下的问题:脏读、不可重复读、幻读
        不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表
        因此我们需要将事务与事务之间隔离。根据隔离的方式来避免事务并发状态下脏读、不可重复读、幻读的产生。Spring中定义了五种隔离规则。
*/
    int getIsolationLevel();

    /** 事务超时时间,事务必须在多少秒之内完成 
        为了使应用程序很好地运行,事务不能运行太长的时间。
        因为事务可能涉及对后端数据库的锁定,也会占用数据库资源。
        事务超时就是事务的一个定时器,在特定时间内事务如果没有执行完毕,那么就会自动回滚,而不是一直等待其结束。 
*/
    int getTimeout();

    /** 事务是否只读,事务管理器能够根据这个返回值进行优化,确保事务是只读的 
        如果在一个事务中所有关于数据库的操作都是只读的,也就是说,这些操作只读取数据库中的数据,而并不更新数据, 这个时候我们应该给该事务设置只读属性,这样可以帮助数据库引擎优化事务。提升效率。
*/
    boolean isReadOnly();

    /**  事务名字 */
    @Nullable
    String getName();
}
  • 7种传播行为
    1、PROPAGATION_REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
    2、PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
    3、PROPAGATION_MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
    4、PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
    5、PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事-0 务,就把当前事务挂起。
    6、PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
    7、PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
    虽然有7种,但是常用的就第一种REQUIRED和第四种REQUIRES_NEW

  • 五个隔离级别
    1、ISOLATION_DEFAULT:默认
    这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.
    另外四个与JDBC的隔离级别相对应;
    2、ISOLATION_READ_UNCOMMITTED:最低 -- 产生脏读,不可重复读和幻读
    充许另外一个事务可以看到这个事务未提交的数据。
    当前级别写数据只会锁住相应的行
    3、ISOLATION_READ_COMMITTED:产生不可重复读和幻读
    允许读取并发事务已经提交的数据,保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。
    4、ISOLATION_REPEATABLE_READ:产生幻读。
    它除了保证一个事务不能读取另一个事务未提交的数据外,对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改。
    如果检索条件有索引(包括主键索引)的时候,默认加锁方式是next-key锁;如果检索条件没有索引,更新数据时会锁住整张表。一个间隙被事务加了锁,其他事务是不能在这个间隙插入记录的,这样可以防止幻读。
    5、ISOLATION_SERIALIZABLE:最高 -- 都避免了
    这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。
    完全服从ACID的隔离级别,也是最慢的事务隔离级别,因为它通常是通过完全锁定(读写都锁定)事务相关的数据库表来实现的
    ISOLATION_SERIALIZABLE 隔离规则类型在开发中很少用到。举个很简单的例子。咱们使用了ISOLATION_SERIALIZABLE规则。A,B两个事务操作同一个数据表并发过来了。A先执行。A事务这个时候会把表给锁住,B事务执行的时候直接报错。

隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也就越大。

  • 回滚规则
    事务回滚规则定义了哪些异常会导致事务回滚而哪些不会。
    默认情况下,只有未检查异常(RuntimeException和Error类型的异常)会导致事务回滚。
    而在遇到检查型异常时不会回滚。 但是你可以声明事务在遇到特定的检查型异常时像遇到运行期异常那样回滚。
    同样,你还可以声明事务遇到特定的异常不回滚,即使这些异常是运行期异常。

  • 名词解释:
    脏读(Dirty reads)—— 事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据。
    不可重复读(Nonrepeatable read)—— 事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果不一致。
    幻读(Phantom read)—— 系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。

不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表

事务的属性可同通过注解方式或配置文件配置:
1、注解方式:
@Transactional只能被应用到public方法上,对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能.
默认情况下,一个有事务方法, 遇到RuntimeException 时会回滚 . 遇到 受检查的异常 是不会回滚 的. 要想所有异常都回滚,要加上 @Transactional( rollbackFor={Exception.class,其它异常})

@Transactional(
    readOnly = false, //读写事务
    timeout = -1 ,     //事务的超时时间,-1为无限制
    noRollbackFor = ArithmeticException.class, //遇到指定的异常不回滚
    isolation = Isolation.DEFAULT, //事务的隔离级别,此处使用后端数据库的默认隔离级别
    propagation = Propagation.REQUIRED //事务的传播行为
)

2、配置文件( aop拦截器方式):


          
            
             
             
             
             
             
          

参考文档:

  • Java中的事务及使用
  • Spring事务管理的四种方式(以银行转账为例)
  • Java模拟银行转账(操作事务)

你可能感兴趣的:(Java 事务)