美文网首页
Java中事务

Java中事务

作者: 一个半吊子工程师 | 来源:发表于2020-05-13 16:28 被阅读0次

    参考:https://www.cnblogs.com/lmyupupblogs/p/10248099.html

    1.什么是JAVA事务?

    通常的观念认为,事务仅与数据库相关。

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

    原子性(atomicity)
    一致性(consistency)
    隔离性 (isolation)
    持久性(durability)

    的缩写。

    事务的原子性:表示事务执行过程中的任何失败都将导致事务所做的任何修改失效。
    事务的一致性:表示当事务执行失败时,所有被该事务影响的数据都应该恢复到事务执行前的状态。
    事务的隔离性:表示在事务执行过程中对数据的修改,在事务提交之前对其他事务不可见。
    事务的持久性:表示已提交的数据在事务执行失败时,数据的状态都应该正确。

    通俗的理解,事务是一组原子操作单元,从数据库角度说,就是一组SQL指令,要么全部执行成功,若因为某个原因其中一条指令执行有错误,则撤销先前执行过的所有指令。更简答的说就是:要么全部执行成功,要么撤销不执行

    既然事务的概念从数据库而来,那Java事务是什么?之间有什么联系?
    实际上,一个Java应用系统,如果要操作数据库,则通过JDBC来实现的。增加、修改、删除都是通过相应方法间接来实现的,事务的控制也相应转移到Java程序代码中。因此,数据库操作的事务习惯上就称为Java事务。

    2. 为什么需要Java事务?

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

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

    3.Java事务的类型

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

    1.JDBC事务
    JDBC 事务是用 Connection 对象控制的。JDBC Connection 接口( java.sql.Connection )提供了两种事务模式:自动提交 和 手工提交

    java.sql.Connection 提供了以下控制事务的方法:
    1 public void setAutoCommit(boolean)
    2 public boolean getAutoCommit()
    3 public void commit()
    4 public void rollback()

    传统JDBC操作流程:

    1).获取JDBC连接
    2).声明SQL
    3).预编译SQL
    4).执行SQL
    5).处理结果集
    6).释放结果集
    7).释放Statement
    8).提交事务
    9).处理异常并回滚事务
    10).释放JDBC连接

    JDBC优缺点:

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

    JDBC为使用Java进行数据库的事务操作提供了最基本的支持。
    通过JDBC事务,我们可以将多个SQL语句放到同一个事务中,保证其ACID特性。
    JDBC事务的主要优点就是API比较简单,可以实现最基本的事务操作,性能也相对较好。
    但是,JDBC事务有一个局限:

    一个 JDBC 事务不能跨越多个数据库!所以,如果涉及到多数据库的操作或者分布式场景,JDBC事务就无能为力了。

    2.JTA(Java Transaction API)事务

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

    1)JTA的构成

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

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

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

    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,商业对象等等。

    3)JTA编程的基本步骤

    a、首先配置JTA ,建立相应的数据源

    b、建立事务:通过创建UserTransaction类的实例来开始一个事务。代码如下:

    Context ctx = new InitialContext(p) ;
    
    UserTransaction trans = (UserTransaction) ctx.lookup("javax. Transaction.UserTransaction")
    

    c、开始事务:代码为 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() ;

    JTA的优缺点:

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

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

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

    3.容器事务

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


    image

    Spring事务管理涉及的接口及其联系:
    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;  
    }
    

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

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
         <property name="dataSource" ref="dataSource" />
    </bean>
    

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

    2)、Hibernate事务

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

    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
         <property name="sessionFactory" ref="sessionFactory" />
    </bean>
    

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

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

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
         <property name="sessionFactory" ref="sessionFactory" />
    </bean>
    

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

    三种Java事务差异?

    1、JDBC事务控制的局限性在一个数据库连接内,但是其使用简单。
    2、JTA事务的功能强大,事务可以跨越多个数据库或多个DAO,使用也比较复杂。
    3、容器事务,主要指的是J2EE应用服务器提供的事务管理,局限于EJB应用使用。

    五、总结

    Java事务控制是构建J2EE应用不可缺少的一部分,合理选择应用何种事务对整个应用系统来说至关重要。一般说来,在单个JDBC 连接连接的情况下可以选择JDBC事务,在跨多个连接或者数据库情况下,需要选择使用JTA事务,如果用到了EJB,则可以考虑使用EJB容器事务。

    事务(Transaction):是并发控制的单元,是用户定义的一个操作序列。这些操作要么都做,要么都不做,是一个不可分割的工作单位。通过事务,sql server 能将逻辑相关的一组操作绑定在一起,以便服务器 保持数据的完整性。事务通常是以begin transaction开始,以commitrollback结束。Commint表示提交,即提交事务的所有操作。具体地说就是将事务中所有对数据的更新
    写回到磁盘上的物理数据库中去,事务正常结束。Rollback表示回滚,即在事务运行的过程中发生了某种故障,事务不能继续进行,系统将事务中对数据库的所有已完成的操作全部撤消,滚回到事务开始的状态。

    自动提交事务:每条单独的语句都是一个事务。每个语句后都隐含一个commit。 (默认)
    显式事务:以begin transaction显示开始,以commitrollback结束。

    隐式事务:当连接以隐式事务模式进行操作时,sql server数据库引擎实例将在提交或回滚当前事务后自动启动新事务。无须描述事物的开始,只需提交或回滚每个事务。但每个事务仍以commit或rollback显式结束。连接将隐性事务模式设置为打开之后,当数据库引擎实例首次执行下列任何语句时,都会自动启动一个隐式事务:alter tableinsertcreateopendeleterevokedropselectfetchtruncate tablegrantupdate在发出commitrollback语句之前,该事务将一直保持有效。在第一个事务被提交或回滚之后,下次当连接执行以上任何语句时,数据库引擎实例都将自动启动一个新事务。该实例将不断地生成隐性事务链,直到隐性事务模式关闭为止。

    Java JDBC事务机制

    首先,我们来看看现有JDBC操作会给我们打来什么重大问题,比如有一个业务:当我们修改一个信息后再去查询这个信息,看是这是一个简单的业务,实现起来也非常容易,但当这个业务放在多线程高并发的平台下,问题自然就出现了,比如当我们执行了一个修改后,在执行查询之前有一个线程也执行了修改语句,这是我们再执行查询,看到的信息就有可能与我们修改的不同,为了解决这一问题,我们必须引入JDBC事务机制,其实代码实现上很简单,一下给出一个原理实现例子供大家参考:

     private Connection conn = null;  
      private PreparedStatement ps = null;  
      try {   
          conn.setAutoCommit(false);  //将自动提交设置为false 
          ps.executeUpdate("修改SQL"); //执行修改操作 
          ps.executeQuery("查询SQL");  //执行查询操作 
          conn.commit();      //当两个操作成功后手动提交  
     } catch (Exception e) { 
         conn.rollback();    //一旦其中一个操作出错都将回滚,使两个操作都不成功 
         e.printStackTrace(); 
     }
    

    与事务相关的理论

    1.事务(Transaction)的四个属性(ACID)

    原子性(Atomic) 对数据的修改要么全部执行,要么全部不执行。
    一致性(Consistent) 在事务执行前后,数据状态保持一致性。
    隔离性(Isolated) 一个事务的处理不能影响另一个事务的处理。
    持续性(Durable) 事务处理结束,其效果在数据库中持久化。

    2.事务并发处理可能引起的问题

    脏读(dirty read):一个事务读取了另一个事务尚未提交的数据,
    不可重复读(non-repeatable read) :一个事务的操作导致另一个事务前后两次读取到不同的数据
    幻读(phantom read) :一个事务的操作导致另一个事务前后两次查询的结果数据量不同。

    举例:

    事务A、B并发执行时,当A事务update后,B事务select读取到A尚未提交的数据,此时A事务rollback,则B读到的数据是无效的”脏”数据。
    当B事务select读取数据后,A事务update操作更改B事务select到的数据,此时B事务再次读去该数据,发现前后两次的数据不一样。
    当B事务select读取数据后,A事务insert或delete了一条满足A事务的select条件的记录,此时B事务再次select,发现查询到前次不存在的记录(“幻影”),或者前次的某个记录不见了。

    JDBC的事务支持

    JDBC对事务的支持体现在三个方面:

    1.自动提交模式(Auto-commit mode)
    Connection提供了一个auto-commit的属性来指定事务何时结束。
    a.当auto-commit为true时,当每个独立SQL操作的执行完毕,事务立即自动提交,也就是说每个SQL操作都是一个事务。一个独立SQL操作什么时候算执行完毕。

    1.JDBC规范是这样规定的:对数据操作语言(DML,如insert,update,delete)和数据定义语言(如create,drop),语句一执行完就视为执行完毕。 2.对select语句,当与它关联的ResultSet对象关闭时,视为执行完毕。 3.对存储过程或其他返回多个结果的语句,当与它关联的所有ResultSet对象全部关闭,所有update count(update,delete等语句操作影响的行数)和output parameter(存储过程的输出参数)都已经获取之后,视为执行完毕。

    b. 当auto-commit为false时,每个事务都必须显示调用commit方法进行提交,或者显示调用rollback方法进行回滚。auto-commit默认为true。


    2.事务隔离级别(Transaction Isolation Levels)
    JDBC提供了5种不同的事务隔离级别,在Connection中进行了定义。
    JDBC定义了五种事务隔离级别:

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

    3.保存点(SavePoint)

    JDBC定义了SavePoint接口,提供在一个更细粒度的事务控制机制。当设置了一个保存点后,可以rollback到该保存点处的状态,而不是rollback整个事务。

    Connection接口的setSavepointreleaseSavepoint方法可以设置和释放保存点。
    JDBC规范虽然定义了事务的以上支持行为,但是各个JDBC驱动,数据库厂商对事务的支持程度可能各不相同。如果在程序中任意设置,可能得不到想要的效果。为此,JDBC提供了DatabaseMetaData接口,提供了一系列JDBC特性支持情况的获取方法。比如,通过DatabaseMetaData.supportsTransactionIsolationLevel方法可以判断对事务隔离级别的支持情况,通过DatabaseMetaData.supportsSavepoints方法可以判断对保存点的支持情况。

    事务的属性可同通过注解方式或配置文件配置:

    注解方式:

    @Transactional只能被应用到public方法上,对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能.
    默认情况下,一个有事务方法, 遇到RuntimeException 时会回滚 . 遇到 受检查的异常 是不会回滚 的. 要想所有异常都回滚,要加上
    @Transactional( rollbackFor={Exception.class,其它异常})

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

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

    <tx:advice id="advice" transaction-manager="txManager">
              <tx:attributes>
                <!-- tx:method的属性:
                    * name 是必须的,表示与事务属性关联的方法名(业务方法名),对切入点进行细化。通配符 
                         (*)可以用来指定一批关联到相同的事务属性的方法。
                          如:'get*'、'handle*'、'on*Event'等等.
                    * propagation:不是必须的,默认值是REQUIRED表示事务传播行为,
                      包括REQUIRED,SUPPORTS,MANDATORY,REQUIRES_NEW,NOT_SUPPORTED,NEVER,NESTED
                    * isolation:不是必须的 默认值DEFAULT ,表示事务隔离级别(数据库的隔离级别)
                    * timeout:不是必须的 默认值-1(永不超时),表示事务超时的时间(以秒为单位)
                    * read-only:不是必须的 默认值false不是只读的表示事务是否只读?
                    * rollback-for: 不是必须的表示将被触发进行回滚的 Exception(s);以逗号分开。
                       如:'com.foo.MyBusinessException,ServletException'
                    * no-rollback-for:不是必须的表示不被触发进行回滚的 Exception(s),以逗号分开。                                        
                       如:'com.foo.MyBusinessException,ServletException'   
                    任何 RuntimeException 将触发事务回滚,但是任何 checked Exception 将不触发事务回滚                     
                -->
                 <tx:method name="save*" propagation="REQUIRED" isolation="DEFAULT" read-only="false"/>
                 <tx:method name="update*" propagation="REQUIRED" isolation="DEFAULT" read-only="false"/>
                 <tx:method name="delete*" propagation="REQUIRED" isolation="DEFAULT" read-only="false"  rollback-for="Exception"/>
                 <!-- 其他的方法之只读的 -->
                 <tx:method name="*" read-only="true"/>
              </tx:attributes>
    </tx:advice>
    

    相关文章

      网友评论

          本文标题:Java中事务

          本文链接:https://www.haomeiwen.com/subject/oqannhtx.html