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
- spring的事务管理方式!
- spring事务管理方式
- Spring中的事务管理方式
- spring事务管理方式
- spring 事务管理实现方式
- spring 事务管理的方式
- Spring中的事务管理方式
- spring事务管理方式
- Spring事务管理方式实现
- spring事务管理方式
- spring事务管理几种方式
- 1.1 Spring事务管理及方式
- spring事务管理几种方式
- spring事务管理几种方式
- spring事务管理几种方式
- spring事务管理几种方式
- spring事务管理几种方式
- spring 事务管理的配置方式
- CodeForces 614 B.Gena's Code
- 性能优化:多线程
- 基于TCP的Socket通信传文件
- 最大公约数和最小公倍数
- CSDN学霸课表——网络工程师(软考中级)
- spring事务管理方式
- 《ACM程序设计》N题
- echarts饼状图的创建
- 17.3.18 数据结构 校内赛(rotinv)(rise)
- web前端-在迷惘中的探索HTML5(三)文件操作FileReader
- 封装DLL到EXE
- handler机制 源码分析 梳理
- Why bootstrap/How to use bootstrap
- C语言学习笔记7