• 事务原理及应用 ys


    一、核心概念

        1、概念

      数据库事务:数据库事务( transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。

      事务概念扩展:事务概念来源于数据库事务,扩展为事务是一个由有限操作集合组成的逻辑单元,包括文件系统,消息队列,一组不可分割的方法操作等。

         事务操作的目的:

       ① 数据一致,指事务提交时保证事务内的所有操作都成功完成,并且更改永久生效;事务回滚时,保证能够恢复到事务执行之前的状态。

       ② 操作隔离,指多个同时执行的事务之间应该相互独立,互不影响。

     2、事务的四个特性:ACID

    • 原子性(Atomicity):事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。
    • 一致性(Consistency):一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不应该被破坏。
    • 隔离性(Isolation):可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。
    • 持久性(Durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中。

      并发事务带来的问题

    • 丢失更新(Lost Update): 当两个或多个事务选择同一行,最初的事务修改的值,会被后面的事务修改的值覆盖。
    • 脏读(Dirty Reads): 当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提 交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
    • 不可重复读(NonRepeatable Reads): 一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现和以前读出的数据不一致。
    • 幻读(Phantom Reads): 一个事务按照相同的查询条件重新读取以前查询过的数据,却发现其他事务插入了满足其查询条件的新数据。

      为了解决上述提到的事务并发问题,数据库提供一定的事务隔离机制来解决这个问题。MySQL的InnoDB引擎提供四种隔离级别(即ACID中的隔离性)

    • 读未提交(READ UNCOMMITTED),能解决幻读问题问题
    • 读已提交(READ COMMITTED),能解决不可重复读取、幻读问题;
    • 可重复读(REPEATABLE READ)(默认),能解决脏读、不可重复读、幻读问题;
    • 串行化(SERIALIZABLE)

      InnoDB默认的隔离级别是REPEATABLE READ,其可避免脏读和不可重复读,但不能避免幻读。

     3、传播行为(Spring针对方法嵌套调用时事务的创建行为定义了七种事务传播机制)

      事务的第一个方面是传播行为(propagation behavior)。当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。Spring定义了七种传播行为:

    传播行为含义

    PROPAGATION_REQUIRED

    表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务

    PROPAGATION_SUPPORTS

    表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行

    PROPAGATION_MANDATORY

    表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常

    PROPAGATION_REQUIRED_NEW

    表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager

    PROPAGATION_NOT_SUPPORTED

    表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager

    PROPAGATION_NEVER

    表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常

    PROPAGATION_NESTED

    表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。注意各厂商对这种传播行为的支持是有所差异的。可以参考资源管理器的文档来确认它们是否支持嵌套事务

        4、事务执行原理(以mysql innodb引擎为例)

      1). redo log

      redo log 即重做日志,是用来实现事务的持久性。该日志文件由两部分组成:重做日志缓冲(redo log buffer)以及重做日志文件(redo log),前者是在内存中,后者在磁盘中。当事务提交之后会把所有修改信息都会存到该日志中, 用于在刷新脏页到磁盘时,发生错误时, 进行数据恢复使用。

    start transaction;
    select balance from bank where name="Tom";
    -- 生成 重做日志 balance=8000
    update bank set balance = balance - 2000;
    -- 生成 重做日志 account=2000
    update finance set account = account + 2000;
    commit;

     执行流程如图所示: 

      mysql 为了提升性能不会把每次的修改都实时同步到磁盘,而是会先存到Buffer Pool(缓冲池)里 头,把这个当作缓存来用。然后使用后台线程将缓存池刷新到磁盘。 当在执行刷新时,宕机或者断电,可能会丢失部分数据。所以引入了redo log来记录已成功提交事务的修改信息,并且在事务提交时会把redo log持久化到磁盘,系统重启之后在读取redo log恢复最新数据。 简单来说 , redo log是用来恢复数据的用于保障,已提交事务的持久化特性 ;

    2). undo log

     undo log 即回滚日志,用于记录数据被修改前的信息。他正好跟前面所说的重做日志所记录的相 反,重做日志记录数据被修改后的信息。undo log主要记录的是数据的逻辑变化,为了在发生错误时回滚之前的操作,需要将之前的操作都记录下来,然后在发生错误时才可以回滚。

        undo log 记录事务修改之前版本的数据信息,因此假如由于系统错误或者rollback操作而回滚的话 可以根据undo log的信息来进行回滚到没被修改前的状态。

    5、spring事务原理

     纯JDBC操作数据库事务步骤:

    1.     获取连接 Connection con = DriverManager.getConnection()
    2.     开启事务con.setAutoCommit(true/false);
    3.     执行CRUD
    4.     提交事务/回滚事务 con.commit() / con.rollback();
    5.     关闭连接 conn.close();

      使用Spring的事务管理功能后,步骤 2 和 4 的代码,而是由Spirng 自动完成。以注解方式为例,配置文件开启注解驱动,在相关的类和方法上通过注解@Transactional标识。Spring 在启动的时候会去解析生成相关的bean,这时候会查看拥有相关注解的类和方法,并且为这些类和方法生成代理,并根据@Transaction的相关参数进行相关配置注入,这样就在代理中增加了事务代码逻辑(开启正常提交事务,异常回滚事务)。而实质实现事务功能是通过数据库的事务处理(见上文,4、事务执行原理)。

    二、应用场景

    1、JDBC事务

    在JDBC中处理事务,都是通过Connection完成的。同一事务中所有的操作,都在使用同一个Connection对象。

    Connection的三个方法与事务有关:

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

    JDBC处理事务的代码格式:

    try{
         con.setAutoCommit(false);//开启事务
         ......
         con.commit();//try的最后提交事务      
    } catch() {
        con.rollback();//回滚事务
    }

    eg:

    import cn.itcast.jdbc.JdbcUtils;
    import org.junit.Test;
    import java.sql.Connection;
    import java.sql.SQLException;
    
    public class Demo1 {
        /*
        * 演示转账方法
        * 所有对Connect的操作都在Service层进行的处理
        * 把所有connection的操作隐藏起来,这需要使用自定义的小工具(day19_1)
        * */
        public void transferAccounts(String from,String to,double money) {
            //对事务的操作
            Connection con = null;
            try{
                con = JdbcUtils.getConnection();
                con.setAutoCommit(false);
                AccountDao dao = new AccountDao();
                dao.updateBalance(con,from,-money);//给from减去相应金额
                if (true){
                    throw new RuntimeException("不好意思,转账失败");
                }
                dao.updateBalance(con,to,+money);//给to加上相应金额
                //提交事务
                con.commit();
    
            } catch (Exception e) {
                try {
                    con.rollback();
                } catch (SQLException e1) {
                    e.printStackTrace();
                }
                throw new RuntimeException(e);
            }
        }
        @Test
        public void fun1() {
            transferAccounts("zs","ls",100);
        }
    }

    补充:

      JDBC事务优缺点:JDBC为使用Java进行数据库的事务操作提供了最基本的支持。通过JDBC事务,我们可以将多个SQL语句放到同一个事务中,保证其ACID特性。但是,一个 JDBC 事务不能跨越多个数据库,不支持多数据库的操作或分布式场景。

    2、JTA事务

      JTA(Java Transaction API)是一种高层的,与实现无关的,与协议无关的API,应用程序和应用服务器可以使用JTA来访问事务。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映射
    补充:

      JTA提供了分布式事务的解决方案,严格的ACID。但是JTA实现复杂,通常情况下,JTA UserTransaction需要从JNDI获取,如果使用JTA,就需要同时使用JTA和JNDI。

    3、容器事务:主要指的是J2EE应用服务器提供的事务管理,如在Spring、Hibernate等框架中都有各自的事务管理功能,表现形式不同,但都是在JAVA事务管理的基础上实现的。

      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;  
        } 

      

    所以Spring事务管理的一个优点就是为不同的事务API提供一致的编程模型,如JTA、JDBC、Hibernate、JPA。

        Spring事务抽象的核心类图

      

       部分Spring包含的对PlatformTransactionManager的实现类如下图所示:

      

     AbstractPlatformTransactionManager抽象类实现了Spring事务的标准流程,其子类DataSourceTransactionManager是我们使用较多的JDBC单数据源事务管理器,而JtaTransactionManager是JTA(Java Transaction API)规范的实现类,另外两个则分别是JavaEE容器WebLogic和WebSphere的JTA事务管理器的具体实现。

      spring事务核心逻辑

      事务拦截器TransactionInterceptorinvoke方法中,通过调用父类TransactionAspectSupportinvokeWithinTransaction方法进行事务处理,该方法支持声明式事务和编程式事务。

    // TransactionInterceptor.class
    @Override
    public Object invoke(final MethodInvocation invocation) throws Throwable {
        // 获取targetClass
        ...
    
        // Adapt to TransactionAspectSupport's invokeWithinTransaction...
        return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
            @Override
            public Object proceedWithInvocation() throws Throwable {
                // 实际执行目标方法
                return invocation.proceed();
            }
        });
    }
    
    // TransactionInterceptor父类TransactionAspectSupport.class
    protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
            throws Throwable {
    
        // If the transaction attribute is null, the method is non-transactional.
        // 查询目标方法事务属性、确定事务管理器、构造连接点标识(用于确认事务名称)
        final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
        final PlatformTransactionManager tm = determineTransactionManager(txAttr);
        final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
    
        if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
            // 事务获取
            TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
            Object retVal = null;
            try {
                // 通过回调执行目标方法
                retVal = invocation.proceedWithInvocation();
            }
            catch (Throwable ex) {
                // 目标方法执行抛出异常,根据异常类型执行事务提交或者回滚操作
                completeTransactionAfterThrowing(txInfo, ex);
                throw ex;
            }
            finally {
                // 清理当前线程事务信息
                cleanupTransactionInfo(txInfo);
            }
            // 目标方法执行成功,提交事务
            commitTransactionAfterReturning(txInfo);
            return retVal;
        } else {
            // 带回调的事务执行处理,一般用于编程式事务
            ...
        }
    }

    TransactionAspectSupport

    //TransactionAspectSupport.class
    protected TransactionInfo createTransactionIfNecessary(
            PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIdentification) {
        ...
        TransactionStatus status = null;
        if (txAttr != null) {
            if (tm != null) {
                // 获取事务
                status = tm.getTransaction(txAttr);
                ...
    }
    
    protected void commitTransactionAfterReturning(TransactionInfo txInfo) {
        if (txInfo != null && txInfo.hasTransaction()) {
            ...
            // 提交事务
            txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
        }
    }
    
    protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
        if (txInfo != null && txInfo.hasTransaction()) {
            ...
            if (txInfo.transactionAttribute.rollbackOn(ex)) {
                try {
                    // 异常类型为回滚异常,执行事务回滚
                    txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
                }
                ...
            } else {
                try {
                    // 异常类型为非回滚异常,仍然执行事务提交
                    txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
                }
                ...
    }
    
    protected final class TransactionInfo {
        private final PlatformTransactionManager transactionManager;
        ...

        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来管理事务,而后者是通过DataSource获取到的。通过调用连接的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>

         4)Java原生API事务

      如果没有使用以上所述的事务管理,或者是跨越了多个事务管理源(比如两个或者是多个不同的数据源),就需要使用JtaTransactionManager:

        <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
            <property name="transactionManagerName" value="java:TransactionManager" />
        </bean>

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

    三、Spring事务应用---编程式事务和声明式事务

      区别:Spring提供了对编程式事务和声明式事务的支持,编程式事务允许用户在代码中精确定义事务的边界,而声明式事务(基于AOP)有助于用户将操作与事务规则进行解耦(对原有代码无侵入)。 

    (1)编程式事务

        Spring提供两种方式的编程式事务管理,分别是:使用TransactionTemplate和直接使用PlatformTransactionManager。

     1.1)使用TransactionTemplate

      采用TransactionTemplate和采用其他Spring模板,如JdbcTempalte和HibernateTemplate是一样的方法。它使用回调方法,把应用程序从处理取得和释放资源中解脱出来。如同其他模板,TransactionTemplate是线程安全的。代码片段:

        TransactionTemplate tt = new TransactionTemplate(); // 新建一个TransactionTemplate
        Object result = tt.execute(
            new TransactionCallback(){  
                public Object doTransaction(TransactionStatus status){  
                    updateOperation();  
                    return resultOfUpdateOperation();  
                }  
        }); // 执行execute方法进行事务管理

    使用TransactionCallback()可以返回一个值。如果使用TransactionCallbackWithoutResult则没有返回值。

        1.2)使用PlatformTransactionManager

        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(); //定义一个某个框架平台的TransactionManager,如JDBC、Hibernate
        dataSourceTransactionManager.setDataSource(this.getJdbcTemplate().getDataSource()); // 设置数据源
        DefaultTransactionDefinition transDef = new DefaultTransactionDefinition(); // 定义事务属性
        transDef.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED); // 设置传播行为属性
        TransactionStatus status = dataSourceTransactionManager.getTransaction(transDef); // 获得事务状态
        try {
            // 数据库操作
            dataSourceTransactionManager.commit(status);// 提交
        } catch (Exception e) {
            dataSourceTransactionManager.rollback(status);// 回滚
        }

    (2)声明式事务

        ①声明式事务原理

      声明式事务的实现就是通过环绕增强的方式,在目标方法执行之前开启事务,在目标方法执行之后提交或者回滚事务,事务拦截器的继承关系图可以体现这一点:

      

     ②用法:根据代理机制的不同,总结了五种Spring事务的配置方式,配置文件如下:

        1)每个Bean都有一个代理

    <?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:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
               http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
               http://www.springframework.org/schema/context
               http://www.springframework.org/schema/context/spring-context-2.5.xsd
               http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
    
        <bean id="sessionFactory" 
                class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 
            <property name="configLocation" value="classpath:hibernate.cfg.xml" /> 
            <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
        </bean> 
    
        <!-- 定义事务管理器(声明式的事务) --> 
        <bean id="transactionManager"
            class="org.springframework.orm.hibernate3.HibernateTransactionManager">
            <property name="sessionFactory" ref="sessionFactory" />
        </bean>
    
        <!-- 配置DAO -->
        <bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl">
            <property name="sessionFactory" ref="sessionFactory" />
        </bean>
    
        <bean id="userDao" 
            class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> 
               <!-- 配置事务管理器 --> 
               <property name="transactionManager" ref="transactionManager" />    
            <property name="target" ref="userDaoTarget" /> 
             <property name="proxyInterfaces" value="com.bluesky.spring.dao.GeneratorDao" />
            <!-- 配置事务属性 --> 
            <property name="transactionAttributes"> 
                <props> 
                    <prop key="*">PROPAGATION_REQUIRED</prop>
                </props> 
            </property> 
        </bean> 
    </beans>

       2)所有Bean共享一个代理基类

       3)使用拦截器

    <?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:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
               http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
               http://www.springframework.org/schema/context
               http://www.springframework.org/schema/context/spring-context-2.5.xsd
               http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
    
        <bean id="sessionFactory" 
                class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 
            <property name="configLocation" value="classpath:hibernate.cfg.xml" /> 
            <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
        </bean> 
    
        <!-- 定义事务管理器(声明式的事务) --> 
        <bean id="transactionManager"
            class="org.springframework.orm.hibernate3.HibernateTransactionManager">
            <property name="sessionFactory" ref="sessionFactory" />
        </bean> 
    
        <bean id="transactionInterceptor" 
            class="org.springframework.transaction.interceptor.TransactionInterceptor"> 
            <property name="transactionManager" ref="transactionManager" /> 
            <!-- 配置事务属性 --> 
            <property name="transactionAttributes"> 
                <props> 
                    <prop key="*">PROPAGATION_REQUIRED</prop> 
                </props> 
            </property> 
        </bean>
    
        <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> 
            <property name="beanNames"> 
                <list> 
                    <value>*Dao</value>
                </list> 
            </property> 
            <property name="interceptorNames"> 
                <list> 
                    <value>transactionInterceptor</value> 
                </list> 
            </property> 
        </bean> 
    
        <!-- 配置DAO -->
        <bean id="userDao" class="com.bluesky.spring.dao.UserDaoImpl">
            <property name="sessionFactory" ref="sessionFactory" />
        </bean>
    </beans>

        4)使用tx标签配置的拦截器

        5)全注解

    <?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:context="http://www.springframework.org/schema/context"
        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.5.xsd
               http://www.springframework.org/schema/context
               http://www.springframework.org/schema/context/spring-context-2.5.xsd
               http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
               http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
    
        <context:annotation-config />
        <context:component-scan base-package="com.bluesky" />
    
        <tx:annotation-driven transaction-manager="transactionManager"/>
    
        <bean id="sessionFactory" 
                class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 
            <property name="configLocation" value="classpath:hibernate.cfg.xml" /> 
            <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
        </bean> 
    
        <!-- 定义事务管理器(声明式的事务) --> 
        <bean id="transactionManager"
            class="org.springframework.orm.hibernate3.HibernateTransactionManager">
            <property name="sessionFactory" ref="sessionFactory" />
        </bean>
    
    </beans>

    此时在DAO上需加上@Transactional注解,如下:

    import java.util.List;
    import org.hibernate.SessionFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
    import org.springframework.stereotype.Component;
    
    @Transactional
    @Component("userDao")
    public class UserDaoImpl extends HibernateDaoSupport implements UserDao {
    
        public List<User> listUsers() {
            return this.getSession().createQuery("from User").list();
        }  
    }

    eg:

    数据库表:

    book(isbn, book_name, price) 
    account(username, balance) 
    book_stock(isbn, stock)

    xml配置:

    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    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-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
    
        <import resource="applicationContext-db.xml" />
    
        <context:component-scan
            base-package="com.springinaction.transaction">
        </context:component-scan>
    
        <tx:annotation-driven transaction-manager="txManager"/>
    
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource" />
        </bean>
    
    </beans>

    BookShopServiceImpl

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Isolation;
    import org.springframework.transaction.annotation.Propagation;
    import org.springframework.transaction.annotation.Transactional;
    
    @Service("bookShopService")
    public class BookShopServiceImpl implements BookShopService {
    
        @Autowired
        private BookShopDao bookShopDao;
    
        /**
         * 1.添加事务注解
         * 使用propagation 指定事务的传播行为,即当前的事务方法被另外一个事务方法调用时如何使用事务。
         * 默认取值为REQUIRED,即使用调用方法的事务
         * REQUIRES_NEW:使用自己的事务,调用的事务方法的事务被挂起。
         *
         * 2.使用isolation 指定事务的隔离级别,最常用的取值为READ_COMMITTED
         * 3.默认情况下 Spring 的声明式事务对所有的运行时异常进行回滚,也可以通过对应的属性进行设置。通常情况下,默认值即可。
         * 4.使用readOnly 指定事务是否为只读。 表示这个事务只读取数据但不更新数据,这样可以帮助数据库引擎优化事务。若真的是一个只读取数据库值得方法,应设置readOnly=true
         * 5.使用timeOut 指定强制回滚之前事务可以占用的时间。
         */
        @Transactional(propagation=Propagation.REQUIRES_NEW,
                isolation=Isolation.READ_COMMITTED,
                noRollbackFor={UserAccountException.class},
                readOnly=true, timeout=3)
        @Override
        public void purchase(String username, String isbn) {
            //1.获取书的单价
            int price = bookShopDao.findBookPriceByIsbn(isbn);
            //2.更新书的库存
            bookShopDao.updateBookStock(isbn);
            //3.更新用户余额
            bookShopDao.updateUserAccount(username, price);
        }
    }

      备注:和spring boot集成使用方式参考 https://zhuanlan.zhihu.com/p/227922586 

     四、分布式事务

      1、5种分布式事务解决方案原理 https://developer.51cto.com/art/201907/600249.htm

      2、分布式事务框架-Seata

      (1)简介

        Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。

      

      3、ShardingSphere 集成了 SEATA作为分布式事务解决方案

        Apache ShardingSphere ,是一套开源的分布式数据库解决方案组成的生态圈,包括由 JDBC、Proxy 和 Sidecar(规划中)。TCC 和 Saga 是两种常见分布式事务实现方案, 主张开发者自行实现对数据库的反向操作,来达到数据在回滚时仍能够保证最终一致性。 SEATA 实现了 SQL 反向操作的自动生成,可以使柔性事务不再必须由开发者介入才能使用。ShardingSphere 集成了 SEATA 作为柔性事务的使用方案。

     

     感谢阅读,借鉴了不少大佬资料,如需转载,请注明出处,谢谢!https://www.cnblogs.com/huyangshu-fs/p/15600093.html

  • 相关阅读:
    Python基础(三) 基本数据类型②:列表
    Python基础(一) Python3环境搭建
    Python基础(二) 基本数据类型①:整数型、布尔型、字符串
    win7下花生壳无法访问的问题
    Java中使用Hhibernate遇到一个错误:No Dialect mapping for JDBC type:1
    ASP.NET MVC2 实验总结
    js 格式化时间
    web service
    初学。。ASP.NET
    在.NET中使用XML的基本方法
  • 原文地址:https://www.cnblogs.com/huyangshu-fs/p/15600093.html
Copyright © 2020-2023  润新知