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-事务管理的更多相关文章

  1. java spring事务管理相关

    一般项目结构为: 数据持久层dao     业务层service     控制层controller 事务控制是在业务层service起作用的,所以需要同时对多张表做添加,修改或删除操作时应该在ser ...

  2. 【Java EE 学习 52】【Spring学习第四天】【Spring与JDBC】【JdbcTemplate创建的三种方式】【Spring事务管理】【事务中使用dbutils则回滚失败!!!??】

    一.JDBC编程特点 静态代码+动态变量=JDBC编程. 静态代码:比如所有的数据库连接池 都实现了DataSource接口,都实现了Connection接口. 动态变量:用户名.密码.连接的数据库. ...

  3. spring事务管理器设计思想(一)

    在最近做的一个项目里面,涉及到多数据源的操作,比较特殊的是,这多个数据库的表结构完全相同,由于我们使用的ibatis框架作为持久化层,为了防止每一个数据源都配置一套规则,所以重新实现了数据源,根据线程 ...

  4. 事务管理(下) 配置spring事务管理的几种方式(声明式事务)

    配置spring事务管理的几种方式(声明式事务) 概要: Spring对编程式事务的支持与EJB有很大的区别.不像EJB和Java事务API(Java Transaction API, JTA)耦合在 ...

  5. Spring事务管理(转)

    1 初步理解 理解事务之前,先讲一个你日常生活中最常干的事:取钱. 比如你去ATM机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱:然后ATM出1000元钱.这两个步骤必须是 ...

  6. [Spring框架]Spring 事务管理基础入门总结.

    前言:在之前的博客中已经说过了数据库的事务, 不过那里面更多的是说明事务的一些锁机制, 今天来说一下Spring管理事务的一些基础知识. 之前的文章: [数据库事务与锁]详解一: 彻底理解数据库事务一 ...

  7. Spring 事务管理高级应用难点剖析--转

    第 1 部分 http://www.ibm.com/search/csass/search/?q=%E4%BA%8B%E5%8A%A1&sn=dw&lang=zh&cc=CN& ...

  8. MyBatis6:MyBatis集成Spring事务管理(下篇)

    前言 前一篇文章<MyBatis5:MyBatis集成Spring事务管理(上篇)>复习了MyBatis的基本使用以及使用Spring管理MyBatis的事务的做法,本文的目的是在这个的基 ...

  9. spring事务管理器设计思想(2)

    spring事务管理器设计思想(二) 上文见<spring事务管理器设计思想(一)> 对于第二个问题,涉及到事务的传播级别,定义如下: PROPAGATION_REQUIRED-- 如果当 ...

  10. Spring事务管理配置示例

    (一).Spring事务特性 1.事务隔离级别 隔离级别是指若干个并发的事务之间的隔离程度. ISOLATION_DEFAULT:默认值,使用数据库的默认隔离级别,就是ISOLATION_READ_C ...

随机推荐

  1. ebay商品基本属性组合成数据表格式,可用上传到系统递交数据

    该刊登表设计是利用VB写的,当时因为两个系统的数据不能直接对接,又copy并且组合SKU,一个表格一个表格填写,比较麻烦,还好刊登系统可以允许用excel表格上传数据 所以就下好模板,学了VB语言,在 ...

  2. CH1809匹配统计【KMP】

    1809 匹配统计 0x18「基本数据结构」练习 描述 阿轩在纸上写了两个字符串,分别记为A和B.利用在数据结构与算法课上学到的知识,他很容易地求出了“字符串A从任意位置开始的后缀子串”与“字符串B” ...

  3. 南京网络赛I-Skr【回文树模板】

    19.32% 1000ms 256000K A number is skr, if and only if it's unchanged after being reversed. For examp ...

  4. Tensorflow 实战Google深度学习框架 第五章 5.2.1Minister数字识别 源代码

    import os import tab import tensorflow as tf print "tensorflow 5.2 " from tensorflow.examp ...

  5. Ensure Indexes Fit in RAM

    Ensure Indexes Fit in RAM — MongoDB Manual https://docs.mongodb.com/manual/tutorial/ensure-indexes-f ...

  6. Django - ORM - 进阶

    一.多表操作 创建模型 实例:我们来假定下面这些概念,字段和关系 作者模型:一个作者有姓名和年龄. 作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息.作者详情模型和作者模型之间是 ...

  7. Popular Cows---poj2186(缩点,强联通)

    题目链接:http://poj.org/problem?id=2186 求有多少个点满足其他n-1个点都能到达这个点,是单向图: 所以我们可以把图进行缩点,之后求出度为0的那个点内包含的点的个数就是求 ...

  8. How Instagram Feeds Work: Celery and RabbitMQ(转)

    原文:http://blogs.vmware.com/vfabric/2013/04/how-instagram-feeds-work-celery-and-rabbitmq.html Instagr ...

  9. tar 压缩解压命令详解

    tar -c: 建立压缩档案-x:解压-t:查看内容-r:向压缩归档文件末尾追加文件-u:更新原压缩包中的文件 这五个是独立的命令,压缩解压都要用到其中一个,可以和别的命令连用但只能用其中一个.下面的 ...

  10. rails 日期取值

    获取当前时间 当前时间 Time.now UTC时间, 国际标准时间 Time.now.utc 日相关 今日开始时间00:00:00 Time.now.beginning_of_day => 2 ...