spring事务管理方式

来源:互联网 发布:历年三大产业 数据 编辑:程序博客网 时间:2024/06/15 08:39
今天总结下spring对于事务的支持。我只会把每一步的步骤都写清楚,如果有些需要着重说的,我会以注解的方式写在代码里。spring的事务支持主要分为两大类:    1. 编程式事务            通过编码的方式来实现事务的管理(很少用).    2.声明式事务            又分为三种。                1. 服务层代理类的方式(很少用)                2. aop的方式实现(最常用)                3. 注解方式实现(常用)

准备工作

我们的场景为转账,从AAA帐号转200元到BBB的帐号,我们在AAA转出200元后,使代码发生异常(1/0),如果数据中没有发生AAA钱转出(因为发生了异常,事务回滚。)则事务生效。[idea创建maven工程](http://blog.csdn.net/baibinboss/article/details/62897695)在pom.xml文件中加入我们的依赖,清单如下:
pom.xml内容<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0"         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <groupId>com.baibin</groupId>    <artifactId>transaction</artifactId>    <version>1.0-SNAPSHOT</version>    <dependencies>        <!--aop支持,spring的aop支持是依赖aop联盟的-->        <dependency>            <groupId>org.aspectj</groupId>            <artifactId>aspectjweaver</artifactId>            <version>1.8.9</version>        </dependency>        <!--阿里开源的数据库链接池-->        <dependency>            <groupId>com.alibaba</groupId>            <artifactId>druid</artifactId>            <version>1.0.16</version>        </dependency>        <!--数据库驱动-->        <dependency>            <groupId>mysql</groupId>            <artifactId>mysql-connector-java</artifactId>            <version>5.1.38</version>        </dependency>        <!--测试-->        <dependency>            <groupId>junit</groupId>            <artifactId>junit</artifactId>            <version>4.12</version>        </dependency>        <!--spring上下文模块-->        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-context</artifactId>            <version>4.3.6.RELEASE</version>        </dependency>        <!--spring数据库操作-->        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-jdbc</artifactId>            <version>4.3.6.RELEASE</version>        </dependency>        <!--spring测试集成-->        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-test</artifactId>            <version>4.3.6.RELEASE</version>            <scope>test</scope>        </dependency>    </dependencies></project>
配置数据库链接信息jdbc.mysql.driver=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true&useUnicode=true&characterEncoding=UTF-8jdbc.username=rootjdbc.password=password
数据库建表语句CREATE TABLE `t_account` (  `id` varchar(32) NOT NULL DEFAULT '',  `name` varchar(32) DEFAULT NULL,/*帐号*/  `account` decimal(10,0) DEFAULT NULL,/*钱*/  PRIMARY KEY (`id`));INSERT INTO `t_account` VALUES ('1', 'AAA', '1000');INSERT INTO `t_account` VALUES ('2', 'BBB', '1000');INSERT INTO `t_account` VALUES ('3', 'CCC', '1000');

编程式事务

第一步:配置spring,配置文件内容如下:

<?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"       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">    <!--扫描配置的注解-->    <context:component-scan base-package="com.baibin.demo1"/>    <!--将所有的类路径下面的配置文件交给spring管理-->    <context:property-placeholder location="classpath*:*.properties"/>    <!--数据库操作-->    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">        <constructor-arg>            <ref bean="dataSource"></ref>        </constructor-arg>    </bean>    <!--数据源-->    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">        <property name="driverClassName" value="${jdbc.mysql.driver}"/>        <property name="url" value="${jdbc.url}"/>        <property name="username" value="${jdbc.username}"/>        <property name="password" value="${jdbc.password}"/>    </bean>    <!--事务管理器        这里因为我们是用最基础的方式来实现对数据库操作的,所以我们使用DataSourceTransactionManager    -->    <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"></property>    </bean></beans>

第二步:服务层代码:

package com.baibin.demo1.service;/** * 接口代码 */public interface TransferService {    /**     * 转账代码     * @param up 转出帐号     * @param down 转入帐号     * @param account 额度     */    void transferAccounts(final String up,final String down,final double account);}
package com.baibin.demo1.service;import com.baibin.demo1.dao.TransferDAO;import org.springframework.stereotype.Service;import org.springframework.transaction.TransactionStatus;import org.springframework.transaction.support.TransactionCallbackWithoutResult;import org.springframework.transaction.support.TransactionTemplate;import javax.annotation.Resource;/** * 实现类代码 */@Servicepublic class TransferServiceImpl implements TransferService {    @Resource    private TransferDAO transferDAO;    @Resource    private TransactionTemplate transactionTemplate;    /**     * 转账代码,因为需要在内部类中使用参数,所以需要写成final修饰的     *     * @param up      转出帐号     * @param down    转入帐号     * @param account 额度     */    public void transferAccounts(final String up, final String down, final double account) {        //转出钱,转入钱,他们应该一起成功,或者一起失败,我们用 1/0 来模拟发生意外。        transactionTemplate.execute(new TransactionCallbackWithoutResult() {            @Override            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {                //转出                transferDAO.expenditure(up, account);                //发生异常                int i = 1/0;                //转入                transferDAO.income(down, account);            }        });    }}

第三步:持久层代码

package com.baibin.demo1.dao;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.stereotype.Repository;/** * 持久层 */@Repositorypublic class TransferDAO {    @Autowired    private JdbcTemplate jdbcTemplate;    /**     * 进账     *     * @param down     * @param account     */    public void income(String down, double account) {        String sql = "UPDATE T_ACCOUNT SET ACCOUNT=ACCOUNT+? WHERE NAME=?";        jdbcTemplate.update(sql, account, down);    }    /**     * 转出     *     * @param up     * @param account     */    public void expenditure(String up, double account) {        String sql = "UPDATE T_ACCOUNT SET ACCOUNT=ACCOUNT-? WHERE NAME=?";        jdbcTemplate.update(sql, account, up);    }}

第四步:单元测试

package com.baibin.demo1.test;import com.baibin.demo1.service.TransferService;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import static org.junit.Assert.*;/** * 编程试事务,在使用所有注解前必须使用@RunWith(SpringJUnit4ClassRunner.class),让测试运行于spring测试环境 */@RunWith(value = SpringJUnit4ClassRunner.class)/** * 加载spring配置文件 */@ContextConfiguration(locations = {"classpath:beans1.xml"})public class TransferServiceImplTest {    @Autowired    private TransferService transferService;    @Test    public void transferAccounts() throws Exception {        /**         * AAA帐号转账200到BBB帐号         */        transferService.transferAccounts("AAA","BBB",200D);    }}
编程式事务需要我们自己来写代码,比较麻烦,一般很少用。

声明式事务之服务层代理类的方式

第一步:配置spring,配置文件内容如下:

<?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"       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">    <!--扫描配置的注解-->    <context:component-scan base-package="com.baibin.demo2"/>    <!--将所有的类路径下面的配置文件交给spring管理-->    <context:property-placeholder location="classpath*:*.properties"/>    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">        <constructor-arg>            <ref bean="dataSource"></ref>        </constructor-arg>    </bean>    <!--数据源-->    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">        <property name="driverClassName" value="${jdbc.mysql.driver}"/>        <property name="url" value="${jdbc.url}"/>        <property name="username" value="${jdbc.username}"/>        <property name="password" value="${jdbc.password}"/>    </bean>    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">        <property name="dataSource" ref="dataSource"/>    </bean>    <bean id="transferDAO" class="com.baibin.demo2.dao.TransferDAO">        <property name="jdbcTemplate" ref="jdbcTemplate"></property>    </bean>    <bean id="transferService" class="com.baibin.demo2.service.TransferServiceImpl">        <property name="transferDAO" ref="transferDAO"></property>    </bean>    <!--事务代理类,我们在使用服务层对象的时候不在直接注入transferService而是注入我们的代理类-->    <bean id="transferServiceProxy"          class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">        <!--为代理类注入事务管理器-->        <property name="transactionManager" ref="transactionManager"></property>        <!--代理的对象-->        <property name="target" ref="transferService"></property>        <!--事务配置-->        <property name="transactionAttributes">            <props>                <!--任意方法名                PROPAGATION_REQUIRED:需要一个事务,如果没有则创建新的事务-->                <prop key="*">PROPAGATION_REQUIRED</prop>            </props>        </property>    </bean></beans>

第二步:服务层代码:

package com.baibin.demo2.service;import com.baibin.demo1.service.TransferService;import com.baibin.demo2.dao.TransferDAO;/** * 服务层代码 */public class TransferServiceImpl implements TransferService {    private TransferDAO transferDAO;    public TransferDAO getTransferDAO() {        return transferDAO;    }    /**     * 需要set方法注入bean     * @param transferDAO     */    public void setTransferDAO(TransferDAO transferDAO) {        this.transferDAO = transferDAO;    }    public void transferAccounts(String up, String down, double account) {        transferDAO.expenditure(up, account);        /*手动触发异常*/        int i = 1/0;        transferDAO.income(down, account);    }}

第三步:持久层代码

package com.baibin.demo1.dao;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.stereotype.Repository;/** * 持久层 */@Repositorypublic class TransferDAO {    @Autowired    private JdbcTemplate jdbcTemplate;    /**     * 进账     *     * @param down     * @param account     */    public void income(String down, double account) {        String sql = "UPDATE T_ACCOUNT SET ACCOUNT=ACCOUNT+? WHERE NAME=?";        jdbcTemplate.update(sql, account, down);    }    /**     * 转出     *     * @param up     * @param account     */    public void expenditure(String up, double account) {        String sql = "UPDATE T_ACCOUNT SET ACCOUNT=ACCOUNT-? WHERE NAME=?";        jdbcTemplate.update(sql, account, up);    }}

第四步:单元测试

package com.baibin.demo2.test;import com.baibin.demo1.service.TransferService;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import javax.annotation.Resource;/** * 在使用所有注解前必须使用@RunWith(SpringJUnit4ClassRunner.class),让测试运行于spring测试环境 */@RunWith(value = SpringJUnit4ClassRunner.class)/** * 加载spring配置文件 */@ContextConfiguration(locations = {"classpath:beans2.xml"})public class TransferServiceImplTest {    /**     * 这里需要注意,我们不在直接注入TransferService  而是注入transferServiceProxy     */    @Resource(name = "transferServiceProxy")    private TransferService transferService;    @Test    public void transferAccounts() throws Exception {        transferService.transferAccounts("AAA","BBB",200);    }}
    代理类的方式呢,优点是对java代码没有了污染,缺点就是需要配置的工作太多了。

声明式事务之aop切面方式

第一步:配置spring,配置文件内容如下:

<?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:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"       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        http://www.springframework.org/schema/tx        http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">    <!--扫描配置的注解-->    <context:component-scan base-package="com.baibin.demo3"/>    <!--将所有的类路径下面的配置文件交给spring管理-->    <context:property-placeholder location="classpath*:*.properties"/>    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">        <property name="driverClassName" value="${jdbc.mysql.driver}"/>        <property name="url" value="${jdbc.url}"/>        <property name="username" value="${jdbc.username}"/>        <property name="password" value="${jdbc.password}"/>    </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>            <!--name里写事务管理的匹配模式,*是通配符,代表所有的方法 *Save代表Save名结束的方法-->            <tx:method name="*" propagation="REQUIRED"/>        </tx:attributes>    </tx:advice>    <!--配置aop切入点-->    <aop:config>        <!--aop切入的规则-->        <aop:pointcut id="allMethod" expression="execution(* com.baibin.demo3..*.*(..))"></aop:pointcut>        <!--指向事务通知-->        <aop:advisor advice-ref="txAdvice" pointcut-ref="allMethod"></aop:advisor>    </aop:config></beans>

第二步:服务层代码:

package com.baibin.demo3.service;import com.baibin.demo1.service.TransferService;import com.baibin.demo3.dao.TransferDAO;import org.springframework.stereotype.Service;import javax.annotation.Resource;/** * 服务层代码 */@Servicepublic class TransferServiceImpl implements TransferService {    @Resource    private TransferDAO transferDAO;    public void transferAccounts(String up, String down, double account) {        transferDAO.expenditure(up, account);        /*手动触发异常*/        int i = 1 / 0;        transferDAO.income(down, account);    }}

第三步:持久层代码

package com.baibin.demo3.dao;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.stereotype.Repository;import javax.annotation.Resource;/** * Created by ibm on 2017/3/22. */@Repositorypublic class TransferDAO {    @Resource    private JdbcTemplate jdbcTemplate;    /**     * 进账     *     * @param down     * @param account     */    public void income(String down, double account) {        String sql = "UPDATE T_ACCOUNT SET ACCOUNT=ACCOUNT+? WHERE NAME=?";        jdbcTemplate.update(sql, account, down);    }    /**     * 转出     *     * @param up     * @param account     */    public void expenditure(String up, double account) {        String sql = "UPDATE T_ACCOUNT SET ACCOUNT=ACCOUNT-? WHERE NAME=?";        jdbcTemplate.update(sql, account, up);    }}

第四步:单元测试

package com.baibin.demo3.test;import com.baibin.demo1.service.TransferService;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import javax.annotation.Resource;/** * 在使用所有注解前必须使用@RunWith(SpringJUnit4ClassRunner.class),让测试运行于spring测试环境 */@RunWith(value = SpringJUnit4ClassRunner.class)/** * 加载spring配置文件 */@ContextConfiguration(locations = "classpath:beans3.xml")public class TransferServiceImplTest {    @Resource    private TransferService transferService;    @Test    public void transferAccounts() throws Exception {        transferService.transferAccounts("AAA","BBB",200D);    }}
    aop的方式我个人觉得是最佳的方式,一个项目配置一个就可以完成所有的事务管理了(强烈建议)。

声明式事务之注解方式

第一步:配置spring,配置文件内容如下:

<?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:tx="http://www.springframework.org/schema/tx"       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        http://www.springframework.org/schema/tx        http://www.springframework.org/schema/tx/spring-tx.xsd">    <!--扫描配置的注解-->    <context:component-scan base-package="com.baibin.demo4"/>    <!--将所有的类路径下面的配置文件交给spring管理-->    <context:property-placeholder location="classpath*:*.properties"/>    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">        <property name="driverClassName" value="${jdbc.mysql.driver}"/>        <property name="url" value="${jdbc.url}"/>        <property name="username" value="${jdbc.username}"/>        <property name="password" value="${jdbc.password}"/>    </bean>    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">        <property name="dataSource" ref="dataSource"/>    </bean>    <!--这里需要启动注解解析,需要设置事务管理器,如果不写则会默认为transactionManager-->    <tx:annotation-driven transaction-manager="transactionManager"/></beans>

第二步:服务层代码:

package com.baibin.demo4.service;import com.baibin.demo1.service.TransferService;import com.baibin.demo4.dao.TransferDAO;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Propagation;import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource;/** * 服务层代码 */@Servicepublic class TransferServiceImpl implements TransferService {    @Resource    private TransferDAO transferDAO;    /**     * 这里需要使用 @Transactional注解,propagation是指传播方式,具体的自己可以查下,这次就不在博客里赘述了。     * @param up 转出帐号     * @param down 转入帐号     * @param account 额度     */    @Transactional(propagation = Propagation.REQUIRED)    public void transferAccounts(String up, String down, double account) {        transferDAO.expenditure(up, account);        transferDAO.income(down, account);    }}

第三步:持久层代码

package com.baibin.demo4.dao;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.stereotype.Repository;import javax.annotation.Resource;/** * Created by ibm on 2017/3/22. */@Repositorypublic class TransferDAO {    @Resource    private JdbcTemplate jdbcTemplate;    /**     * 进账     *     * @param down     * @param account     */    public void income(String down, double account) {        String sql = "UPDATE T_ACCOUNT SET ACCOUNT=ACCOUNT+? WHERE NAME=?";        jdbcTemplate.update(sql, account, down);    }    /**     * 转出     *     * @param up     * @param account     */    public void expenditure(String up, double account) {        String sql = "UPDATE T_ACCOUNT SET ACCOUNT=ACCOUNT-? WHERE NAME=?";        jdbcTemplate.update(sql, account, up);    }}

第四步:单元测试

package com.baibin.demo4.test;import com.baibin.demo1.service.TransferService;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import javax.annotation.Resource;/** * 在使用所有注解前必须使用@RunWith(SpringJUnit4ClassRunner.class),让测试运行于spring测试环境 */@RunWith(value = SpringJUnit4ClassRunner.class)/** * 加载spring配置文件 */@ContextConfiguration(locations = "classpath:beans4.xml")public class TransferServiceImplTest {    @Resource    private TransferService transferService;    @Test    public void transferAccounts() throws Exception {        transferService.transferAccounts("AAA", "BBB", 200D);    }}
    注解的方式也比较不错,不过需要在每个类上面都写,还是建议大家使用aop的方式。

总结:

    今天写了下java的spring提供的事务解决方案,我写的代码比较缀余,为的是大家可以拷过去就能用,事务还有一些隔离级别,传播方式相关的内容,下次和大家一起分享。
0 0
原创粉丝点击