zoukankan      html  css  js  c++  java
  • Java Spring-事务管理

    2017-11-12 16:31:59

    Spring的事务管理分为两种:

    • 编程式的事务管理:手动编写代码
    • 声明式的事务管理:只需要配置就可以

    一、最初的环境搭建

    public interface AccountDAO {
        public void out(String to,Double money);
    
        public void in(String from, Double money);
    }
    
    public class AccountDAOImpl extends JdbcDaoSupport implements AccountDAO {
        @Override
        public void out(String from, Double money) {
            String sql =  "update account  set money = money - ? where name = ?";
            this.getJdbcTemplate().update(sql, money,from);
        }
    
        @Override
        public void in(String to, Double money) {
            String sql = "update account  set money = money + ? where name = ?";
            this.getJdbcTemplate().update(sql, money , to);
        }
    }
    
    public interface AccountService {
        public void transfer(String from, String to, Double money);
    }
    
    public class AccountServiceImpl implements AccountService {
        @Resource(name = "accountDao")
        private AccountDAO accountDAO;
    
        @Override
        public void transfer(String from, String to, Double money) {
            accountDAO.out(from,money);
            accountDAO.in(to,money);
        }
    }
    
    // 测试
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:config4.xml")
    public class TestDemo {
        @Resource(name = "accountservice")
        private AccountService accountService;
    
        @Test
        public void demo(){
            accountService.transfer("aaa","bbb",100d);
        }
    }
    

    二、手动式的事务管理

    可以发现,在没有引入事务管理的时候,如果在转账的out和in之间出现了异常,那么就会导致转账的结果出错。所以我们需要引入事务管理技术。

    Spring提供了事务管理的模板(工具类),可以方便我们对事务进行管理。

    具体步骤:

    • 第一步:注册事务管理器
    • 第二步:注册事务模板类
    • 第三步:在业务层注入模板类
    • 第四步:在业务层代码上使用模板
    public class AccountServiceImpl implements AccountService {
        @Resource(name = "accountDao")
        private AccountDAO accountDAO;
    
        @Resource(name = "transactionTemplate")
        private TransactionTemplate transactionTemplate;
    
        @Override
        public void transfer(String from, String to, Double money) {
            transactionTemplate.execute(new TransactionCallbackWithoutResult() {
                @Override
                protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                    accountDAO.out(from,money);
                    accountDAO.in(to,money);
                }
            });
        }
    }
    

    XML的配置:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    
        <!--配置连接池-->
        <!--<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">-->
        <!--<property name="driverClassName" value="com.mysql.jdbc.Driver"/>-->
        <!--<property name="url" value="jdbc:mysql://localhost:3306/testdb"/>-->
        <!--<property name="username" value="host"/>-->
        <!--<property name="password" value="hy1102"/>-->
        <!--</bean>-->
    
        <!-- 配置DBCP连接池 -->
        <!--<bean id="datasource" class=" org.apache.commons.dbcp.BasicDataSource ">-->
        <!--<property name="driverClassName" value="com.mysql.jdbc.Driver"/>-->
        <!--<property name="url" value="jdbc:mysql://localhost:3306/testdb"/>-->
        <!--<property name="username" value="host"/>-->
        <!--<property name="password" value="hy1102"/>-->
        <!--</bean>-->
    
        <!-- 引入该属性文件 -->
        <!--<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">-->
        <!--<property name="location" value="classpath:jdbc.properties"/>-->
        <!--</bean>-->
    
        <!-- 使用 context 标签引入属性文件 -->
        <context:property-placeholder location="classpath:jdbc.properties"/>
    
        <!--  配置 c3p0 连接池 -->
        <bean id="datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="${jdbc.driver}"/>
            <property name="jdbcUrl" value="${jdbc.url}"/>
            <property name="user" value="${jdbc.user}"/>
            <property name="password" value="${jdbc.password}"/>
        </bean>
    
        <!-- 定义模板 -->
        <bean id="jdbctemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="datasource"/>
        </bean>
    
        <bean id="userdao" class="spring3.UserDao">
            <property name="jdbcTemplate" ref="jdbctemplate"/>
        </bean>
    
        <!--业务层-->
        <bean id="accountservice" class="spring4.AccountServiceImpl"/>
    
        <!--持久层-->
        <bean id="accountDao" class="spring4.AccountDAOImpl">
            <!--事实上可以直接注入连接池来创建模板-->
            <property name="dataSource" ref="datasource"/>
        </bean>
    
        <!--配置事务管理器-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <!-- 需要注入连接池,通过连接池获得连接 -->
            <property name="dataSource" ref="datasource"/>
        </bean>
    
        <!-- 事务管理的模板 -->
        <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
            <property name="transactionManager" ref="transactionManager"/>
        </bean>
    
    
    </beans>
    

    三、声明式的事务管理

    手动编码方式类似于对transfer方法进行增强,所以考虑代理Service对象。

    • 基于原始的TransactionProxyFactoryBean
    // 业务代码
    public class AccountServiceImpl implements AccountService {
        @Resource(name = "accountDao")
        private AccountDAO accountDAO;
    
        @Override
        public void transfer(String from, String to, Double money) {
            accountDAO.out(from,money);
            accountDAO.in(to,money);
        }
    }
    
    // 测试
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:config4.xml")
    public class TestDemo {
        @Resource(name = "accountServiceProxy")
        private AccountService accountService;
    
        @Test
        public void demo(){
            accountService.transfer("aaa","bbb",100d);
        }
    }
    

    XML的配置:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    
        <!--配置连接池-->
        <!--<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">-->
        <!--<property name="driverClassName" value="com.mysql.jdbc.Driver"/>-->
        <!--<property name="url" value="jdbc:mysql://localhost:3306/testdb"/>-->
        <!--<property name="username" value="host"/>-->
        <!--<property name="password" value="hy1102"/>-->
        <!--</bean>-->
    
        <!-- 配置DBCP连接池 -->
        <!--<bean id="datasource" class=" org.apache.commons.dbcp.BasicDataSource ">-->
        <!--<property name="driverClassName" value="com.mysql.jdbc.Driver"/>-->
        <!--<property name="url" value="jdbc:mysql://localhost:3306/testdb"/>-->
        <!--<property name="username" value="host"/>-->
        <!--<property name="password" value="hy1102"/>-->
        <!--</bean>-->
    
        <!-- 引入该属性文件 -->
        <!--<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">-->
        <!--<property name="location" value="classpath:jdbc.properties"/>-->
        <!--</bean>-->
    
        <!-- 使用 context 标签引入属性文件 -->
        <context:property-placeholder location="classpath:jdbc.properties"/>
    
        <!--  配置 c3p0 连接池 -->
        <bean id="datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="${jdbc.driver}"/>
            <property name="jdbcUrl" value="${jdbc.url}"/>
            <property name="user" value="${jdbc.user}"/>
            <property name="password" value="${jdbc.password}"/>
        </bean>
    
        <!-- 定义模板 -->
        <bean id="jdbctemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="datasource"/>
        </bean>
    
        <bean id="userdao" class="spring3.UserDao">
            <property name="jdbcTemplate" ref="jdbctemplate"/>
        </bean>
    
        <!--业务层-->
        <bean id="accountservice" class="spring4.AccountServiceImpl"/>
    
        <!--持久层-->
        <bean id="accountDao" class="spring4.AccountDAOImpl">
            <!--事实上可以直接注入连接池来创建模板-->
            <property name="dataSource" ref="datasource"/>
        </bean>
    
        <!--配置事务管理器-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <!-- 需要注入连接池,通过连接池获得连接 -->
            <property name="dataSource" ref="datasource"/>
        </bean>
    
        <!-- 事务管理的模板 -->
        <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
            <property name="transactionManager" ref="transactionManager"/>
        </bean>
    
        <!--  配置生成代理对象 -->
        <bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
            <!--  目标对象 -->
            <property name="target" ref="accountservice"/>
            <!--  注入事务管理器 -->
            <property name="transactionManager" ref="transactionManager"/>
            <!--  事务的属性设置 -->
            <property name="transactionAttributes">
                <props>
                    <prop key="transfer">PROPAGATION_REQUIRED</prop>
                </props>
            </property>
        </bean>
    
    </beans>
    

    prop格式:PROPAGATION,ISOLATION,readOnly,-Exception,+Exception
    * 顺序:传播行为、隔离级别、事务是否只读、发生哪些异常可以回滚事务(所有的异常都回滚)、发生了哪些异常不回

    <prop key="transfer">PROPAGATION_REQUIRED,readonly</prop>
    <prop key="transfer">PROPAGATION_REQUIRED,+java.lang.ArithmeticException</prop>
    
    • 基于切面自动代理

    上面的方法每次都要手动生成代理,显然是不太合适的,所以可以使用基于切面的自动代理。

    public interface AccountDAO {
        public void out(String to,Double money);
    
        public void in(String from, Double money);
    }
    
    public class AccountDAOImpl extends JdbcDaoSupport implements AccountDAO {
        @Override
        public void out(String from, Double money) {
            String sql =  "update account  set money = money - ? where name = ?";
            this.getJdbcTemplate().update(sql, money,from);
        }
    
        @Override
        public void in(String to, Double money) {
            String sql = "update account  set money = money + ? where name = ?";
            this.getJdbcTemplate().update(sql, money , to);
        }
    }
    
    public interface AccountService {
        public void transfer(String from, String to, Double money);
    }
    
    public class AccountServiceImpl implements AccountService {
        @Resource(name = "accountDao")
        private AccountDAO accountDAO;
    
        @Override
        public void transfer(String from, String to, Double money) {
            accountDAO.out(from,money);
            accountDAO.in(to,money);
        }
    }
    
    //测试
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:config5.xml")
    public class TestDemo {
        @Resource(name = "accountservice")
        private AccountService accountService;
    
        @Test
        public void demo(){
            accountService.transfer("aaa","bbb",100d);
        }
    }
    

    XML的配置:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:aop="http://www.springframework.org/schema/aop" xmlns="http://www.springframework.org/schema/beans"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
    
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!-- 使用 context 标签引入属性文件 -->
        <context:property-placeholder location="classpath:jdbc.properties"/>
    
        <!--  配置 c3p0 连接池 -->
        <bean id="datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="${jdbc.driver}"/>
            <property name="jdbcUrl" value="${jdbc.url}"/>
            <property name="user" value="${jdbc.user}"/>
            <property name="password" value="${jdbc.password}"/>
        </bean>
    
        <!-- 定义模板 -->
        <bean id="jdbctemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="datasource"/>
        </bean>
    
        <bean id="userdao" class="spring3.UserDao">
            <property name="jdbcTemplate" ref="jdbctemplate"/>
        </bean>
    
        <!--业务层-->
        <bean id="accountservice" class="spring4.AccountServiceImpl"/>
    
        <!--持久层-->
        <bean id="accountDao" class="spring4.AccountDAOImpl">
            <!--事实上可以直接注入连接池来创建模板-->
            <property name="dataSource" ref="datasource"/>
        </bean>
    
        <!--配置事务管理器-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <!-- 需要注入连接池,通过连接池获得连接 -->
            <property name="dataSource" ref="datasource"/>
        </bean>
    
        <!-- 定义一个增强 -->
        <tx:advice id="txAdvice" transaction-manager="transactionManager">
            <!-- 增强(事务)的属性的配置 -->
            <tx:attributes>
                <!--
                isolation:DEFAULT :事务的隔离级别.
                propagation :事务的传播行为.
                read-only :false.不是只读
                timeout :-1
                no-rollback-for :发生哪些异常不回滚
                rollback-for :发生哪些异常回滚事务
                -->
                <tx:method name="transfer" isolation="DEFAULT"/>
            </tx:attributes>
        </tx:advice>
    
        <!-- aop配置定义切面和切点的信息 -->
        <aop:config>
            <!-- 定义切点:哪些类的哪些方法应用增强 -->
            <aop:pointcut expression=" execution(* spring4.AccountService+.*(..)) " id="mypointcut"/>
            <!-- 定义切面: -->
            <aop:advisor advice-ref="txAdvice" pointcut-ref="mypointcut"/>
        </aop:config>
    
    </beans>
    
    • 基于注解的事务配置

    具体步骤:

    • 第一步:事务管理器
    • 第二步:注解事务
    • 第三步:在Service上使用注解

    * 注解中有属性值:

    * isolation

    * propagation

    * readOnly

    ...

    • 第四步:测试
    @Transactional
    public class AccountServiceImpl implements AccountService {
        @Resource(name = "accountDao")
        private AccountDAO accountDAO;
    
        @Override
        public void transfer(String from, String to, Double money) {
            accountDAO.out(from,money);
            // int i = 1/0;
            accountDAO.in(to,money);
        }
    }
    

    XML配置文件:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:aop="http://www.springframework.org/schema/aop" xmlns="http://www.springframework.org/schema/beans"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
    
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!-- 使用 context 标签引入属性文件 -->
        <context:property-placeholder location="classpath:jdbc.properties"/>
    
        <!--  配置 c3p0 连接池 -->
        <bean id="datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="${jdbc.driver}"/>
            <property name="jdbcUrl" value="${jdbc.url}"/>
            <property name="user" value="${jdbc.user}"/>
            <property name="password" value="${jdbc.password}"/>
        </bean>
    
        <!-- 定义模板 -->
        <bean id="jdbctemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="datasource"/>
        </bean>
    
        <bean id="userdao" class="spring3.UserDao">
            <property name="jdbcTemplate" ref="jdbctemplate"/>
        </bean>
    
        <!--业务层-->
        <bean id="accountservice" class="spring4.AccountServiceImpl"/>
    
        <!--持久层-->
        <bean id="accountDao" class="spring4.AccountDAOImpl">
            <!--事实上可以直接注入连接池来创建模板-->
            <property name="dataSource" ref="datasource"/>
        </bean>
    
        <!--配置事务管理器-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <!-- 需要注入连接池,通过连接池获得连接 -->
            <property name="dataSource" ref="datasource"/>
        </bean>
    
        <tx:annotation-driven transaction-manager="transactionManager"/>
    
    </beans>
    

    比较来看,最后的基于注解的方式是最容易,也是代码量最少的。

  • 相关阅读:
    【独立开发人员er Cocos2d-x实战 001】csb文件导出和载入
    Best Time to Buy and Sell Stock I &amp;&amp; II &amp;&amp; III
    cocos2d-x项目101次相遇-安装和环境搭建 -xcode
    使用 C# 开发智能手机软件:推箱子(十二)
    javascript实现掉落弹出层------Day29
    Android中onTouch与onClick事件的关系
    CSDN编程挑战——《交替字符串》
    【NPR】非真实感渲染实验室
    CSS自己主动换行、强制不换行、强制断行、超出显示省略号
    在Ubuntu 14.04安装和使用Docker
  • 原文地址:https://www.cnblogs.com/hyserendipity/p/7822391.html
Copyright © 2011-2022 走看看