`
xinklabi
  • 浏览: 1591813 次
  • 性别: Icon_minigender_1
  • 来自: 吉林
文章分类
社区版块
存档分类
最新评论

事务相关内容详解(Spring、java)

 
阅读更多

Spring的事务传播性与隔离级别

 

一、事务的四个特性

l  原子性:一个事务中所有对数据库的操作是一个不可分割的操作序列,要么全做,要么全部做。

l  一致性:数据不会因为事务的执行而遭到破坏。

l  隔离性:一个事务的执行,不受其他事务(进程)的干扰。既并发执行的个事务之间互不干扰。

l  持久性:一个事务一旦提交,它对数据库的改变将是永久的。

 

二、事务的实现方式

      实现方式共有两种:编码方式;声明式事务管理方式。

      基于AOP技术实现的声明式事务管理,实质就是:在方法执行前后进行拦截,然后在目标方法开始之前创建并加入事务,执行完目标方法后根据执行情况提交或回滚事务。

      声明式事务管理又有两种方式:一种是基于XML配置文件的方式;另一种是在业务方法上添加@Transactional注解,将事务规则应用到业务逻辑中(一般定义在service层)。

 

三、创建事务的时机

      是否需要创建事务,是由事务传播行为控制的。读数据不需要或只为其指定只读事务,而数据的插入、修改、删除就需要进行事务管理了,这是由事务的隔离级别控制的。

      事务具有7个传播级别和4个隔离级别,传播级别定义的是事务创建的时机,隔离级别定义的是对并发事务数据读取的控制。

 

四、事务的传播级别

      以下是事务的7种传播级别:

l  PROPAGATION_REQUIRED

默认的Spring事务传播级别,使用该级别的特点是:如果上下文中已经存在事务,那么就加入到事务中执行;如果当前上下文中不存在事务,则新建事务执行。所以这个级别通常能满足处理大多数的业务场景。

l  PROPAGATION_SUPPORTS

从字面意思就知道,supports,支持,该传播级别的特点是:如果上下文存在事务,则支持事务加入事务,如果没有事务,则使用非事务的方式执行。所以说,并非所有的包含在TransactionTemplate.execute方法中的代码都会有事务支持。这个通常是用来处理那些并非原子性的非核心业务逻辑操作。应用场景较少。

l  PROPAGATION_MANDATORY

该级别的事务要求上下文中必须要存在事务,否则就会抛出异常!配置该方式的传播级别是有效的控制上下文调用代码遗漏添加事务控制的保证手段。比如一段代码不能单独被调用执行,但是一旦被调用,就必须有事务包含的情况,就可以使用这个传播级别。

l  PROPAGATION_REQUIRES_NEW

从字面即可知道,new,每次都要一个新事务,该传播级别的特点是:每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。

这是一个很有用的传播级别,举一个应用场景:现在有一个发送100个红包的操作,在发送之前,要做一些系统的初始化、验证、数据记录操作,然后发送100封红包,然后再记录发送日志,发送日志要求100%的准确,如果日志不准确,那么整个父事务逻辑需要回滚。

怎么处理整个业务需求呢?就是通过这个PROPAGATION_REQUIRES_NEW级别的事务传播控制就可以完成。发送红包的子事务不会直接影响到父事务的提交和回滚。

l  PROPAGATION_NOT_SUPPORTED

这个也可以从字面得知,not supported,不支持,当前级别的特点是:若上下文中存在事务,则挂起事务,执行当前逻辑,结束后恢复上下文的事务。

这个级别有什么好处?可以帮助你将事务尽可能的缩小。我们知道一个事务越大,它存在的风险也就越多。所以在处理事务的过程中,要保证尽可能的缩小范围。比如一段代码,是每次逻辑操作都必须调用的,比如循环1000次的某个非核心业务逻辑操作。这样的代码如果包在事务中,势必造成事务太大,导致出现一些难以考虑周全的异常情况,所以事务的这个传播级别就派上用场了。用当前级别的事务模板包含起来就可以了。

l  PROPAGATION_NEVER

该事务更严格,上面一个事务传播级别只是不支持而已,有事务就挂起,而PROPAGATION_NEVER传播级别要求上下文中不能存在事务,一旦有事务,就抛出runtime异常,强制停止执行!这个级别上辈子跟事务有仇。

l  PROPAGATION_NESTED

从字面也可知道,nested,嵌套级别事务。该传播级别的特征是:如果上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务。

那么什么是嵌套事务呢?很多人都不理解,我看过一些博客,都是有些理解偏差。

嵌套是子事务嵌套在父事务中执行,子事务是父事务的一部分,在进入子事务之前,父事务建立一个回滚点,叫save point,然后执行子事务,这个子事务的执行也算是父事务的一部分,然后子事务执行结束,父事务继续执行。重点就在于那个save point。看几个问题就明了了:

(1)   如果子事务回滚,会发生什么?

父事务会回滚到进入子事务前建立的save point,然后尝试其他的事务或者其他的业务逻辑,父事务之前的操作不会受到影响,更不会自动回滚。

(2)   如果父事务回滚,会发生什么?

父事务回滚,子事务也会跟着回滚!为什么呢,因为父事务结束之前,子事务是不会提交的,我们说子事务是父事务的一部分,正是这个道理。

(3)   事务的提交,是什么情况?

是父事务先提交,然后子事务提交,还是子事务先提交,父事务再提交?答案是第二种情况,还是那句话,子事务是父事务的一部分,由父事务统一提交。

现在你再体会一下这个”嵌套“,是不是有那么点意思了?

以上是事务的7个传播级别,在日常应用中,通常可以满足各种业务需求,但是除了传播级别,在读取数据库的过程中,如果两个事务并发执行,那么彼此之间的数据是如何影响的呢?这就需要了解一下事务的另一个特性:数据隔离级别。

 

五、事务的隔离级别

      数据隔离级别分为不同的4种:

l  SERIALIZABLE

最严格的级别,事务串行执行,资源消耗最大。

l  REPEATABLE_READ

保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。避免了“脏读取”和“不可重复读取”的情况,但是带来了更多的性能损失。

l  READ_COMMITTED

大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”。该级别适用于大多数系统。

l  READ_UNCOMMITTED

保证了读取过程中不会读取到非法数据。

 

      上面的解释其实每个定义都有一些拗口,其中涉及到几个术语:脏读、不可重复读、幻读。这里解释一下:

l  脏读(Dirty Reads)

所谓的脏读,其实就是读到了别的事务回滚前的脏数据。比如事务B执行过程中修改了数据X,在未提交前,事务A读取了X,而事务B却回滚了,这样事务A就形成了脏读。

l  不可重复读(Non-RepeatableReads)

不可重复读字面含义已经很明了了,比如事务A首先读取了一条数据,然后执行逻辑的时候,事务B将这条数据改变了,然后事务A再次读取的时候,发现数据不匹配了,就是所谓的不可重复读了。

l  幻读

小的时候数手指,第一次数十10个,第二次数是11个,怎么回事?产生幻觉了?

幻读也是这样子,事务A首先根据条件索引得到10条数据,然后事务B改变了数据库一条数据,导致也符合事务A当时的搜索条件,这样事务A再次搜索发现有11条数据了,就产生了幻读。

 

事务隔离级别对照关系表:

 

脏读

不可重复读

幻读

SERIALIZABLE

不会

不会

不会

REPEATABLE_READ

不会

不会

READ_COMMITTED

不会

READ_UNCOMMITTED

      所以最安全的,是Serializable,但是伴随而来也是高昂的性能开销。

      另外,事务常用的两个属性:① readonly,设置事务为只读以提升性能;② timeout,设置事务的超时时间,一般用于防止大事务的发生。

 

六、Spring管理声明式事务的配置

1. 基于XML配置文件的AOP和TX配置方式

      applicationContext.xml配置文件:

<?xml version="1.0" encoding="gbk"?>

<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    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-2.0.xsd

    http://www.springframework.org/schema/tx

    http://www.springframework.org/schema/tx/spring-tx-2.0.xsd

    http://www.springframework.org/schema/aop

    http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

 

    <bean id="dataSource"

        class="org.apache.commons.dbcp.BasicDataSource">

        <property name="driverClassName"

            value="com.microsoft.sqlserver.jdbc.SQLServerDriver">

        </property>

        <property name="url"

            value="jdbc:sqlserver://localhost:1500;databaseName=ssh">

        </property>

        <property name="username" value="sa"></property>

        <property name="password" value="sa"></property>

    </bean>

    <bean id="sessionFactory"

        class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

        <property name="dataSource">

            <ref bean="dataSource" />

        </property>

        <property name="hibernateProperties">

            <props>

                <prop key="hibernate.dialect">

                    org.hibernate.dialect.SQLServerDialect

                </prop>

            </props>

        </property>

        <property name="mappingResources">

            <list>

                <value>bank/entity/Account.hbm.xml</value>

            </list>

        </property>

    </bean>

  

    <bean id="AccountDAO" class="bank.dao.AccountDAO">

        <property name="sessionFactory">

            <ref bean="sessionFactory" />

        </property>

    </bean>

 

    <bean id="AccountManager" class="bank.biz.AccountManager">

        <property name="dao">

            <ref bean="AccountDAO" />

        </property>

    </bean>

  

    <bean name="/account" class="bank.action.AccountAction">

        <property name="accountManager">

            <ref bean="AccountManager" />

        </property>

    </bean>  

  

    <!--通用事务管理器-->

    <bean id="TransactionManager"

        class="org.springframework.orm.hibernate3.HibernateTransactionManager">

        <property name="sessionFactory" ref="sessionFactory" />

    </bean>

    <!--指定事务策略,声明一个通知,用以指出要管理哪些事务方法及如何管理-->

    <tx:advice id="txAdvice" transaction-manager="TransactionManager">

        <tx:attributes>

            <!-- 对get/load/search开头的方法要求只读事务 -->

            <tx:method name="find*" propagation="SUPPORTS"

                read-only="true" />

            <!-- 对其它方法要求事务 -->

            <tx:method name="*" propagation="REQUIRED" />

        </tx:attributes>

    </tx:advice>

  

    <!--声明一个config,用以将事务策略和业务类关联起来-->

    <aop:config>

        <!-- 添加事务支持,因为前面配置的transactionManager是专对Hibernate的事务管理器-->

        <aop:pointcut id="bizMethods" expression="execution(* bank.biz..*.*(..))" />

        <!-- 织入 -->

        <aop:advisor advice-ref="txAdvice" pointcut-ref="bizMethods" />

    </aop:config>          

</beans>

 

2. 基于anotation注解形式的事务管理

      applicationContext.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:p="http://www.springframework.org/schema/p"

       xmlns:tx="http://www.springframework.org/schema/tx"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

    http://www.springframework.org/schema/aop

    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd

    http://www.springframework.org/schema/tx

    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd ">

 

       <!-- 需要引入tx的命名空间 -->

       <tx:annotation-driven transaction-manager="transactionManager" />

      

       <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">

              <property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver">

              </property>

              <property name="url"

                     value="jdbc:sqlserver://localhost:1500;databaseName=ssh">

              </property>

              <property name="username" value="sa"></property>

              <property name="password" value="sa"></property>

       </bean>

      

       <bean id="sessionFactory"

              class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

              <property name="dataSource">

                     <ref bean="dataSource" />

              </property>

              <property name="hibernateProperties">

                     <props>

                            <prop key="hibernate.dialect">

                                   org.hibernate.dialect.SQLServerDialect

                            </prop>

                     </props>

              </property>

              <property name="mappingResources">

                     <list>

                            <value>bank/entity/Account.hbm.xml</value>

                     </list>

              </property>

       </bean>

      

       <bean id="tblUserDAO" class="com.angi.dao.TblUserDAO">

              <property name="sessionFactory">

                     <ref bean="sessionFactory" />

              </property>

       </bean>

       <bean id="tblUserService" class="com.angi.dao.service.TblUserService">

              <property name="tblUserDAO">

                     <ref bean="tblUserDAO" />

              </property>

       </bean>

      

       <!-- 声明一个 Hibernate3 的事务管理器供代理类自动管理事务用 -->

       <bean id="transactionManager"            class="org.springframework.orm.hibernate3.HibernateTransactionManager">

              <property name="sessionFactory">

                     <ref local="sessionFactory" />

              </property>

       </bean>

</beans>

 

Java代码片段:

    @Transactional

    public void doTransaction() {

        // step1 insert

        TblUser tblUser1 = new TblUser();

        tblUser1.setId(24);

        tblUser1.setUsername("Angi12");

        tblUser1.setPassword("Wang21");

        tblUserDAO.save(tblUser1);

        // step2 update

        TblUser tblUser2 = tblUserDAO.findById(2);

        tblUser2.setPassword(tblUser2.getPassword() + "a");

        tblUserDAO.update(tblUser2);

        // step3 insert

        TblUser tblUser = new TblUser();

        tblUser.setId(23);

        tblUser.setUsername("Angi");

        tblUser.setPassword("Wang");

        tblUserDAO.save(tblUser);

    }

 

七、简述Hibernate的SessionFactory和Session

l  SessionFactory对象

Hibernate中SessionFactory对象的创建代价很高,它是线程安全的对象,被设计成可以为所有的应用程序线程所共享。通常,SessionFactory会在应用程序启动时创建,一旦创建了SessionFactory将不会轻易关闭,只有当应用关闭时,SessionFactory才会关闭。

l  Session对象

Hibernate中Session对象是轻量级的,它是线程不安全的。对于单个业务进程、单个工作单元而言,Session只被使用一次。创建Session时,并不会立即打开与数据库之间的连接,Session只在需要进行数据库操作时,才会获取JDBC连接。因此,打开和关闭Session,并不会对性能造成很大的影响。甚至即使无法确定一个请求是否需要数据访问,也可以打开Session对象,因为如果不进行数据库访问,Session不会获取JDBC连接。

使用Spring管理Hibernate的事务,在每个DAO操作中使用SessionFactory.getCurrentSession()方法,该方法可以得到当前事务绑定的Session。同时当前的Session和关联的Hibernate事务被绑定到当前线程上,虽然Session不是线程安全的,但是通过这样的方式,每一个Session都处于单线程中,避免Session线程安全问题。

l  不通过Spring管理事务,开启事务的主动性

在SessionFactory.openSession()中,Hibernate会初始化数据库连接,与此同时,将其 AutoCommit设为关闭状态,这就是说,从SessionFactory获得Session,其自动提交属性就已经被关闭了,事务需要主动、显示的调用才能生效,下面的代码不会对事务性数据库产生任何效果:

        session = sessionFactory.openSession();

        session.save(user);

        session.close();

      如果要使得代码真正作用到数据库,必须显示的调用Transaction指令:

        session = sessionFactory.openSession();

        Transaction tx = session.beginTransaction();

        session.save(user);

        tx.commit();

        session.close();

 

八、Java的三种事务管理

l  JDBC事务

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

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

public void setAutoCommit(boolean)

public boolean getAutoCommit()

public void commit()

public void rollback()

使用 JDBC 事务界定时,您可以将多个 SQL 语句结合到一个事务中。

JDBC 事务的一个缺点是事务的范围局限于一个数据库连接。一个 JDBC 事务不能跨越多个数据库。

l  JTA(Java Transaction API)事务

JTA是一种高层的,与实现无关的,与协议无关的API,应用程序和应用服务器可以使用JTA来访问事务。

JTA允许应用程序执行分布式事务处理--在两个或多个网络计算机资源上访问并且更新数据,这些数据可以分布在多个数据库上。JDBC驱动程序的JTA支持极大地增强了数据访问能力。

如果计划用 JTA 界定事务,那么就需要有一个实现 javax.sql.XADataSource 、javax.sql.XAConnection 和 javax.sql.XAResource 接口的 JDBC 驱动程序。一个实现了这些接口的驱动程序将可以参与 JTA 事务。一个 XADataSource 对象就是一个 XAConnection 对象的工厂。 XAConnection s 是参与 JTA 事务的 JDBC 连接。

您将需要用应用服务器的管理工具设置XADataSource 。从应用服务器和 JDBC 驱动程序的文档中可以了解到相关的指导。

J2EE 应用程序用 JNDI 查询数据源。一旦应用程序找到了数据源对象,它就调用 javax.sql.DataSource.getConnection() 以获得到数据库的连接。

XA 连接与非 XA 连接不同。一定要记住 XA 连接参与了 JTA 事务。这意味着 XA 连接不支持 JDBC 的自动提交功能。同时,应用程序一定不要对 XA 连接调用 java.sql.Connection.commit() 或者java.sql.Connection.rollback() 。相反,应用程序应该使用UserTransaction.begin()、 UserTransaction.commit() 和 serTransaction.rollback() 。

l  容器事务

容器事务主要是J2EE应用服务器提供的,容器事务大多是基于JTA完成,这是一个基于JNDI的,相当复杂的API实现。相对编码实现JTA 事务管理,我们可以通过EJB容器提供的容器事务管理机制(CMT)完成同一个功能,这项功能由J2EE应用服务器提供。这使得我们可以简单的指定将哪个方法加入事务,一旦指定,容器将负责事务管理任务。这是我们常见的解决方式,因为通过这种方式我们可以将事务代码排除在逻辑编码之外,同时将所有困难交给 J2EE容器去解决。使用EJB CMT的另外一个好处就是程序员无需关心JTA API的编码,不过,理论上我们必须使用EJB。

 

三种事务差异:

1.   JDBC事务控制的局限性在一个数据库连接内,但是其使用简单。

2.   JTA事务的功能强大,事务可以跨越多个数据库或多个DAO,使用也比较复杂。

3.   容器事务,主要指的是J2EE应用服务器提供的事务管理,局限于EJB应用使用。

分享到:
评论

相关推荐

    Spring中事务的传播属性详解

    ### Spring中事务的传播属性详解 #### 一、引言 在使用Spring框架进行应用程序开发时,事务管理是一项非常重要的特性。Spring提供了两种事务管理方式:编程式事务管理和声明式事务管理。其中,声明式事务管理因其...

    spring几种事务配置详解【精】

    以上就是Spring框架中常见的事务配置方式及其相关概念,理解并熟练掌握这些知识对于开发高效、稳定的业务系统至关重要。通过选择合适的事务管理策略,我们可以确保数据的一致性,提高应用的可靠性。

    java事务处理详解

    Java事务处理详解 Java事务处理是指在Java应用程序中对事务的管理和控制。事务是指一系列的操作,Either all succeed or all fail。Java事务处理的目的是为了确保数据的一致性和完整性。 Spring是Java事务处理的...

    Spring事务详解

    在Java企业级应用开发中,Spring框架以其强大的功能和易用性成为了不可或缺的一部分。其中,Spring的事务管理是其核心特性之一,它...了解和熟练掌握Spring事务管理,对于提升Java企业级应用的稳定性和可靠性至关重要。

    spring事务详解

    Spring事务详解 Spring框架的事务管理功能是Java企业级开发中的重要组成部分,它将事务管理从具体的业务逻辑和数据访问逻辑中独立出来,实现了关注点分离。这种分离不仅降低了事务管理的复杂性,而且增强了代码的可...

    Hibernate缓存与spring事务详解

    **标题:“Hibernate缓存与Spring事务详解”** 在IT领域,尤其是Java开发中,Hibernate作为一款流行的ORM(对象关系映射)框架,极大地简化了数据库操作。而Spring框架则以其全面的功能,包括依赖注入、AOP(面向切...

    Spring事务优缺点及使用详解.docx

    【Spring 事务管理详解】 一、事务简介 事务是数据库操作的基本单位,它确保一组SQL语句要么全部成功,要么全部失败,以维护数据的一致性。在MySQL中,事务通常涉及INSERT、UPDATE、SELECT和DELETE等操作。当操作...

    java疯狂讲义,spring in action,leetcode详解

    "Spring in action"是一本详细介绍Spring框架的书籍,可能包含Spring的核心模块如依赖注入、AOP(面向切面编程)、MVC(模型-视图-控制器)架构、数据访问、Spring Boot、Spring Cloud等相关内容。Spring框架简化了...

    spring架构详解 spring架构详解

    Spring 框架是Java开发领域中广泛应用的开源框架,其设计理念和设计模式对于理解和创建高效、可维护的软件系统至关重要。Spring的核心架构由Core、Context和Beans三个主要组件构成,它们共同构建了Spring的基石,...

    spring事务配置详解

    Spring 事务配置是Spring 框架中的重要组成部分,它提供了对数据库操作事务性的管理,确保数据的一致性和完整性。Spring 提供了多种方式来配置事务管理,主要分为编程式事务管理和声明式事务管理。下面将详细介绍这...

    详解Spring配置事务的五种方式

    3. **基于Java的@Configuration注解**:通过Java配置类,我们可以使用`@EnableTransactionManagement`开启事务管理,并使用`@Transactional`注解在方法级别声明事务。同时,可以使用`@Bean`定义TransactionManager和...

    Java全栈课程之Spring详解

    《Spring详解——Java全栈开发的关键》 Spring框架是Java企业级应用开发的基石,它以其灵活、高效和模块化的特性赢得了广大开发者的一致好评。本篇将深入探讨Spring的核心概念,包括IoC(Inversion of Control)...

    多图详解Spring框架的设计理念与设计模式.pdf

    - **臃肿与复杂性**:传统的Java EE框架往往过于复杂,使得开发者在实现业务逻辑的同时还需处理大量的框架相关代码。 - **开发效率低下**:由于框架本身的复杂度,开发者需要花费大量时间去理解和调试代码,严重影响...

    主题:详解spring事务属性.doc

    Spring 声明式事务是Java开发中一种高效且便捷的事务管理方式,它通过AOP(面向切面编程)实现,将事务管理代码从业务逻辑中解耦出来。Spring 提供了`PlatformTransactionManager`接口作为事务管理的核心,它负责...

    JAVA SPRING 框架实例

    《JAVA SPRING框架实例开发源代码详解》 JAVA SPRING框架是Java领域中极为重要的一个开源项目,它以其强大的功能、易用性和灵活性,成为企业级应用开发的首选框架。本篇将深入探讨Spring框架的核心概念,并通过具体...

    spring高级编程详解

    ### Spring高级编程详解:深入理解声明式事务管理 #### 一、引言 在现代软件开发中,事务处理是确保数据一致性和完整性的重要手段之一。传统的事务管理方式往往需要开发者手动编写大量的代码来处理诸如获取数据库...

Global site tag (gtag.js) - Google Analytics