实现spring事务的四种方式

Posted Bertha然

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了实现spring事务的四种方式相关的知识,希望对你有一定的参考价值。

用一个银行账号转钱的案例来说明spring事务的实现。
在转钱过程中,一个账号钱增加,另一个减少,那么当有异常产生时,就会出现钱转丢了的现象
一个减少了,而另一个没有增加,这个时候就需要把这两个行为绑定到一起,要么同时发生,要么都不发生
这就用到了事务,事务就是指在逻辑上的一组操作,这组操作要么全部成功,要么全部失败
实现spring事务的四种方式分别为:
(1)编程式事务管理:需要手动编写代码,在实际开发中很少使用
(2)声明式事务管理:
(2.1)基于TransactionProxyFactoryBean的方式,需要为每个进行事务管理的类做相应配置
(2.2)基于AspectJ的XML方式,不需要改动类,在XML文件中配置好即可
(2.3)基于注解的方式,配置简单,需要在业务层类中添加注解
(2.2)和(2.3)在开发中使用比较多,前者配置一目了然,可以在XML文件中得到所有信息,后者配置简单方便

需要做的一些准备工作:

 

1.在数据库中新建一张account数据表

SQL脚本:

CREATE TABLE `account` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `name` varchar(20) NOT NULL,

  `money` double DEFAULT NULL,

  PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

INSERT INTO `account` VALUES (\'1\', \'aaa\', \'1000\');

INSERT INTO `account` VALUES (\'2\', \'bbb\', \'1000\');

INSERT INTO `account` VALUES (\'3\', \'ccc\', \'1000\');

2.需要引入的Jar包

3.为数据库连接准备的配置文件- jdbc.properties

使用c3p0数据库连接池

jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc\\:mysql\\://127.0.0.1\\:3306/test
jdbc.username=root
jdbc.password=

4.创建两个接口
AccountDao:数据库操作
public interface AccountDao {public void outMoney(String out,Double money);public void inMoney(String in,Double money);
}
AccountService:逻辑处理操作
public interface AccountService {/*** * @param out 转出账号* @param in  转入账号* @param money 转账金额*/public void transfer(String out,String in,Double money);
}
5.创建以上两个接口的实现类
import org.springframework.jdbc.core.support.JdbcDaoSupport;

 

public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao{

    

    public void outMoney(String out, Double money) {

        String sql="update account set money=money-? where name=?";

        this.getJdbcTemplate().update(sql,money,out);

    }

 

    public void inMoney(String in, Double money) {

        String sql="update account set money=money+? where name=?";

        this.getJdbcTemplate().update(sql,money,in);

    }

}
public class AccountServiceImpl implements AccountService{

 

    private AccountDao accountDao;

    

    public void setAccountDao(AccountDao accountDao) {

        this.accountDao = accountDao;

    }

 

    public void transfer(String out,String in,Double money) {

        

        accountDao.outMoney(out, money);

        accountDao.inMoney(in, money);    

    }        

}

6.测试类(使用spring加JUnit4整合测试)

import javax.annotation.Resource;

import org.junit.Test;

import org.junit.runner.RunWith;

import org.springframework.test.context.ContextConfiguration;

import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

 

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration("classpath:applicationContext.xml")//引入xml文件

public class TestDemo1 {

    

    @Resource(name="accountService")//得到bean id为accountService的对象

    private AccountService accountService;

    

    @Test

    public void demo1(){

        accountService.transfer("aaa", "bbb", 200d);

    }    

}

7.xml文件 - 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:context="http://www.springframework.org/schema/context"

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

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

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

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

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

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

         http://www.springframework.org/schema/context/spring-context-3.1.xsd

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

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

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

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

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

         http://www.springframework.org/schema/task/spring-task-3.1.xsd">

    

    <!-- 引入外部的配置文件 -->

    <context:property-placeholder location="classpath:jdbc.properties"/>

 

    <!-- 配置c3p0连接池 -->

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">

        <property name="driverClass" value="${jdbc.driverClass}"/>

        <property name="jdbcUrl" value="${jdbc.url}"/>

        <property name="user" value="${jdbc.username}"/>

        <property name="password" value="${jdbc.password}"/>

    </bean>

    

    <bean id="accountService" class="demo1.AccountServiceImpl">

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

    </bean>

    

    <bean id="accountDao" class="demo1.AccountDaoImpl">

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

    </bean>

    

</beans>

代码实现方式:

 

(1)编程式事务管理

 

ServiceImpl类:

import org.springframework.transaction.TransactionStatus;

import org.springframework.transaction.support.TransactionCallbackWithoutResult;

import org.springframework.transaction.support.TransactionTemplate;

public class AccountServiceImpl implements AccountService{

 

    private AccountDao accountDao;

    

    public void setAccountDao(AccountDao accountDao) {

        this.accountDao = accountDao;

    }

 

    /**

     * 注入事务管理的模板

     * 在xml文件中添加Property

     * <property name="transactionTemplate" ref="transactionTemplate"/>

     */

    private TransactionTemplate transactionTemplate;

    

    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {

        this.transactionTemplate = transactionTemplate;

    }

 

    public void transfer(final String out,final String in,final Double money) {

        /**

         * 在这里面进行事务操作

         * 因为需要在匿名内部类中使用参数,所以给参数加上final关键字

         */

        transactionTemplate.execute(new TransactionCallbackWithoutResult(){

 

            @Override

            protected void doInTransactionWithoutResult(

                    TransactionStatus transactionStatus) {

                accountDao.outMoney(out, money);

            

                accountDao.inMoney(in, money);        

            }    

        });

    

    }        

}

xml文件中需要配置事务管理器,无论使用哪一种方式,都需要在xml文件中配置事务管理器,将事务管理器注入到模板中,而该模板又会自动注入到accountService中,业务逻辑处理都放在了execute方法中。

 

<!--spring事务编程式 -->

<!-- 配置事务管理器  -->

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

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

</bean>

    

<!-- 定义事务管理的模板 :Spring为了简化事务管理的代码而提供的类  -->

<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">

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

</bean> 
在DaoImpl类中,继承了JdbcDaoSupport类,可以省去jdbc复杂的代码
在XML文件配置中,注入dataSource用来获取数据库的连接

(2.1)基于TransactionProxyFactoryBean的方式
采用spring-aop的方式,需要用到代理模式
XML文件:
<!-- 配置事务管理器  -->

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

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

</bean>

    

<!-- 配置业务层代理: -->

<bean id="accountServiceProxy" 

    class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">

    <!-- 配置目标对象 -->

    <property name="target" ref="accountService"/>

    <!-- 注入事务管理器  -->

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

    <!-- 注入事务属性 -->

    <property name="transactionAttributes">

        <props>

        <!-- 

           格式 ,key为方法名称

           PROPAGATION:事务的传播行为

           ISOLATION:事务的隔离级别

           readOnly:只读

           Exception:发生哪些异常,回滚事务

           +Exception:发生哪些异常不回滚

         -->

            <prop key="transfer">PROPAGATION_REQUIRED</prop>

        </props>

    </property>

</bean>
在测试类中需自动注入的bean-id就不再是accountService了,而是代理类accountServiceProxy
此时代理类和被代理类实现了共同的接口,AccountService,有关代理模式请看:代理模式
@Resource(name="accountServiceProxy")
private AccountService accountService;
 
(2.2)基于AspectJ的XML方式
XML文件:
<!-- 配置事务管理器 -->

<bean id="transactionManager" 

    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

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

</bean>

    

<!-- 配置的事务的通知 -->

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

    <tx:attributes>

        <!-- 

        propagation:事务传播行为

        isolation:事务隔离级别

        read-only:只读

        rollback-for:发生哪些异常回滚

        no-rollback-for:发生哪些异常不回滚

        timeout:有效期

         -->

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

    </tx:attributes>

</tx:advice>

    

<!-- 配置切面 -->

<aop:config>

    <aop:pointcut expression="execution(* demo3.AccountService.*(..))" id="pointcut1"/>

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

</aop:config>

(2.3)基于注解的方式,配置简单,需要在业务层类中添加注解

<!-- 配置事务管理器 -->

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

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

</bean>

    

<!-- 开启注解事务 -->

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

在serviceImpl类中需要添加注解,参数和上面同理

@Transactional(propagation=Propagation.REQUIRED)

我这儿整理了比较全面的JAVA相关的面试资料,


需要领取面试资料的同学,请加群:473984645

 

 

 

以上是关于实现spring事务的四种方式的主要内容,如果未能解决你的问题,请参考以下文章

Spring事务管理的四种方式(以银行转账为例)

一文解析Spring编程式和声明式事务实例讲解

连载redis库存操作,分布式锁的四种实现方式[三]--基于Redis watch机制实现分布式锁

读书笔记——spring cloud 中 HystrixCommand的四种执行方式简述

必须要讨论的四种分布式事务方案

DI(依赖注入)的四种方式