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. 边的双联通+缩点+LCA(HDU3686)

    Traffic Real Time Query System Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K ...

  2. 记录用户操作历史命令history

    我们知道可以使用history命令,查看自己的操作记录,但如果你是root用户,如何查看其它用户的操作记录呢?   其实history命令只是把当前用户目录下的~/.bash_History文件内容列 ...

  3. Css-浅谈如何将多个inline或inline-block元素垂直居中

                一直以来,前端开发过程中本人遇到最多,最烦的问题之一是元素如何垂直居中,发现开发过程中,元素的垂直居中一直是个很大的麻烦事,经常发现PC端和电脑模拟元素都垂直居中了,但是遇到移 ...

  4. Oracle等待事件之log file parallel write

    log file parallel write表示等待 LGWR 向操作系统请求 I/O 开始直到完成 I/O.这种事件发生通常表示日志文件发生了I/O 竞争或者文件所在的驱动器较慢.这说明这种等待与 ...

  5. 转!idea启动后发现tomcat前面出现红色或是灰色的问号

    原博文地址:https://blog.csdn.net/z_zhy/article/details/83068168 直接在idea里 点击File------settings,在搜索框直接搜tomc ...

  6. 过山车---hdu2063(最大匹配)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2063最大匹配模板题: #include <iostream> #include <c ...

  7. 【我的Android进阶之旅】解决Android Studio 运行gradle命令时报错: 错误: 编码GBK的不可映射字符

    1.问题描述 最近在负责公司基础业务和移动基础设施的开发工作,正在负责Lint代码静态检查工作.因此编写了自定义的Lint规则,在调试过程中,编译的时候出现了如下所示的错误: 部分输出日志如下所示: ...

  8. Git学习-->如何通过Shell脚本自动定时将Gitlab备份文件复制到远程服务器?

    一.背景 在我之前的博客 git学习--> Gitlab如何进行备份恢复与迁移? (地址:http://blog.csdn.net/ouyang_peng/article/details/770 ...

  9. qt——类大全

    qt类总结地址 http://www.kuqin.com/qtdocument/ QWidget.QDialog及QMainWindow的区别 QWidget类是所有用户界面对象的基类. 窗口部件是用 ...

  10. 翻译:Bing地图瓦片体系

    Bing Maps Tile System Bing地图瓦片体系 原文链接:http://msdn.microsoft.com/en-us/library/bb259689.aspx Bing Map ...