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>
比较来看,最后的基于注解的方式是最容易,也是代码量最少的。
Java Spring-事务管理的更多相关文章
- java spring事务管理相关
一般项目结构为: 数据持久层dao 业务层service 控制层controller 事务控制是在业务层service起作用的,所以需要同时对多张表做添加,修改或删除操作时应该在ser ...
- 【Java EE 学习 52】【Spring学习第四天】【Spring与JDBC】【JdbcTemplate创建的三种方式】【Spring事务管理】【事务中使用dbutils则回滚失败!!!??】
一.JDBC编程特点 静态代码+动态变量=JDBC编程. 静态代码:比如所有的数据库连接池 都实现了DataSource接口,都实现了Connection接口. 动态变量:用户名.密码.连接的数据库. ...
- spring事务管理器设计思想(一)
在最近做的一个项目里面,涉及到多数据源的操作,比较特殊的是,这多个数据库的表结构完全相同,由于我们使用的ibatis框架作为持久化层,为了防止每一个数据源都配置一套规则,所以重新实现了数据源,根据线程 ...
- 事务管理(下) 配置spring事务管理的几种方式(声明式事务)
配置spring事务管理的几种方式(声明式事务) 概要: Spring对编程式事务的支持与EJB有很大的区别.不像EJB和Java事务API(Java Transaction API, JTA)耦合在 ...
- Spring事务管理(转)
1 初步理解 理解事务之前,先讲一个你日常生活中最常干的事:取钱. 比如你去ATM机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱:然后ATM出1000元钱.这两个步骤必须是 ...
- [Spring框架]Spring 事务管理基础入门总结.
前言:在之前的博客中已经说过了数据库的事务, 不过那里面更多的是说明事务的一些锁机制, 今天来说一下Spring管理事务的一些基础知识. 之前的文章: [数据库事务与锁]详解一: 彻底理解数据库事务一 ...
- Spring 事务管理高级应用难点剖析--转
第 1 部分 http://www.ibm.com/search/csass/search/?q=%E4%BA%8B%E5%8A%A1&sn=dw&lang=zh&cc=CN& ...
- MyBatis6:MyBatis集成Spring事务管理(下篇)
前言 前一篇文章<MyBatis5:MyBatis集成Spring事务管理(上篇)>复习了MyBatis的基本使用以及使用Spring管理MyBatis的事务的做法,本文的目的是在这个的基 ...
- spring事务管理器设计思想(2)
spring事务管理器设计思想(二) 上文见<spring事务管理器设计思想(一)> 对于第二个问题,涉及到事务的传播级别,定义如下: PROPAGATION_REQUIRED-- 如果当 ...
- Spring事务管理配置示例
(一).Spring事务特性 1.事务隔离级别 隔离级别是指若干个并发的事务之间的隔离程度. ISOLATION_DEFAULT:默认值,使用数据库的默认隔离级别,就是ISOLATION_READ_C ...
随机推荐
- ebay商品基本属性组合成数据表格式,可用上传到系统递交数据
该刊登表设计是利用VB写的,当时因为两个系统的数据不能直接对接,又copy并且组合SKU,一个表格一个表格填写,比较麻烦,还好刊登系统可以允许用excel表格上传数据 所以就下好模板,学了VB语言,在 ...
- CH1809匹配统计【KMP】
1809 匹配统计 0x18「基本数据结构」练习 描述 阿轩在纸上写了两个字符串,分别记为A和B.利用在数据结构与算法课上学到的知识,他很容易地求出了“字符串A从任意位置开始的后缀子串”与“字符串B” ...
- 南京网络赛I-Skr【回文树模板】
19.32% 1000ms 256000K A number is skr, if and only if it's unchanged after being reversed. For examp ...
- Tensorflow 实战Google深度学习框架 第五章 5.2.1Minister数字识别 源代码
import os import tab import tensorflow as tf print "tensorflow 5.2 " from tensorflow.examp ...
- Ensure Indexes Fit in RAM
Ensure Indexes Fit in RAM — MongoDB Manual https://docs.mongodb.com/manual/tutorial/ensure-indexes-f ...
- Django - ORM - 进阶
一.多表操作 创建模型 实例:我们来假定下面这些概念,字段和关系 作者模型:一个作者有姓名和年龄. 作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息.作者详情模型和作者模型之间是 ...
- Popular Cows---poj2186(缩点,强联通)
题目链接:http://poj.org/problem?id=2186 求有多少个点满足其他n-1个点都能到达这个点,是单向图: 所以我们可以把图进行缩点,之后求出度为0的那个点内包含的点的个数就是求 ...
- How Instagram Feeds Work: Celery and RabbitMQ(转)
原文:http://blogs.vmware.com/vfabric/2013/04/how-instagram-feeds-work-celery-and-rabbitmq.html Instagr ...
- tar 压缩解压命令详解
tar -c: 建立压缩档案-x:解压-t:查看内容-r:向压缩归档文件末尾追加文件-u:更新原压缩包中的文件 这五个是独立的命令,压缩解压都要用到其中一个,可以和别的命令连用但只能用其中一个.下面的 ...
- rails 日期取值
获取当前时间 当前时间 Time.now UTC时间, 国际标准时间 Time.now.utc 日相关 今日开始时间00:00:00 Time.now.beginning_of_day => 2 ...