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. Linq初探

    1.什么是LINQ LINQ是语言集成查询(Language Integrated Query),这项技术是在.net 3.5就已经引入的技术,极大的方便了数据的查询,他可以支持数据库.XML.ADO ...

  2. render的几个应用

    1.render可以通过模版语法来渲染字符串,例如变量,标签,for循环,这里就不赘述,我就举个自己印象很深刻灵活应用,看看render到底做了什么,关心什么 注意! 在rander眼里,没有html ...

  3. 使用Navicat连接Mysql报错:can not get hostname for your address

    以管理员的身份使用cmd命令运行netsh winsock reset即可!

  4. sql小知识

    1:查询某一段落内的几条数据,按时间降序. LIMIT 5,10; //检索记录行6-15 2:创建视图,  查询出某些类别的数据,保存在视图中.  || 的优先级高于and ) ); 3:  查询出 ...

  5. Spring中基于Java的配置@Configuration和@Bean用法

    spring中为了减少xml中配置,可以声明一个配置类(例如SpringConfig)来对bean进行配置. 一.首先,需要xml中进行少量的配置来启动Java配置: <?xml version ...

  6. 主从同步DNS(BIND)

    看着别人搭建很简单,其实到自己做的时候需要考虑更多的问题. 1.环境 1)操作系统最好一样,配置一样 2)关闭防火墙,selinux,时间要同步(我就是用的纽约的时区,同步中国的时间,虽然时间是相同的 ...

  7. 画柱状图Java

    样例输入:THE QUICK BROWN FOX JUMPED OVER THE LAZY DOG.THIS IS AN EXAMPLE TO TEST FOR YOURHISTOGRAM PROGR ...

  8. addslashes — 使用反斜线引用字符串

    返回字符串,该字符串为了数据库查询语句等的需要在某些字符前加上了反斜线.这些字符是单引号(').双引号(").反斜线(\)与 NUL( NULL 字符). 一个使用 addslashes() ...

  9. 解决不能在本地使用JQuery load的方法

    $.ajaxSetup({ xhr: function () { if ("ActiveXObject" in window) { return new ActiveXObject ...

  10. Bootstrap fileinput v3.0(ssm版)

    说明在上一个版本即Bootstrap fileinput v2.0(ssm版)的基础上,增加了多处都需要上传的需求 核心代码ArticleController.java package com.isd ...