1.课程目标

  • 事务回顾

  • spring中的事务管理的api

  • spring中编程式事务管理

  • spring中声明式事务管理

2.事务回顾

2.1 事务的概念

事务是指逻辑上的一组操作,要么全成功,要么全失败。以银行转账为例,详细说明,略。

2.2 事务的特性

原子性:事务是一个不可分割的工作单位。事务中的操作要么一起成功,要么一起失败。

一致性:事务前后数据的完整性必须保持一致。

隔离性:多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务干扰。可以通过设置隔离级别来实现。

持久性:一个事务一旦被提交,那么数据库中数据的改变是永久性的。即使数据库发生故障也不该对其有影响。

3.spring中事务管理的api

  • PlatformTransactionManager:包含了事务的提交、回滚等管理。

  • TransactionDefinition:包含了事务的隔离级别、传播、超时信息等。

  • TransactionStatus:包含了事务是否有保存点、是不是新事务等状态信息。

3.1 PlatformTransactionManager

是interface,有一些具体的实现类,比如datasourceTransactionManager。

spring为不同的持久化框架提供了不同的PlatformTransactionManager。以最常用的举例。

说明 事务
使用spring jdbc或ibatis、mybatis进行持久化数据时使用 org.springframework.jdbc.datasource.DataSourceTransactionManager
使用Hibernate进行持久化数据时使用 org.springframework.orm.hibernate3.HibernateTransactionManager

3.2 TransactionDefinition

(1) 隔离级别相关的常量,以ISOLATION开头。

不考虑隔离性会造成的问题:脏读、幻读、不可重复读。

脏读:一个事务读取了另一个事务改写但是还没提交的数据。如果这些数据被回滚,则读到的数据是无效的。

幻读:一个事务读取了几行数据后,另一个事务插入了一些记录。在后来的查询中,第一个事务就会发现一些原来不存在的记录。

不可重复读:在同一个事务中,多次读取同一个数据返回的结果不同。

事务的隔离级别:

隔离级别 含义
DEFAULT 使用后端数据库默认的隔离级别(spring中得选择项)
READ_UNCOMMITED 允许读取还没提交的已改变了的数据,可能导致脏读、幻读、不可重复读。
READ_COMMITED 允许在并发事务已经提交后读取,可防止脏读。但不可防止幻读和不可重复读。
REPEATABLE_READ 对相同字段的多次读取,结果是一致的,除非数据被事务本身改变。可防止脏读、不可重复读,但不可防止幻读。
SERIALIZABLE 完全服从ACID的隔离级别,可防止脏读、幻读、不可重复读。这在所有隔离级别中是最慢的,因为它是通过完全锁定在事务中涉及的数据表来完成的。是不可能出现并发访问的情况。

默认隔离级别举例:

mysql:REPEATABLE_READ

oracle:READ_COMMITED

(2) 传播行为相关的常量,以PROPAGATION开头。

事务的传播行为是什么?

用来解决业务层方法之间的相互调用问题。

web层-->业务层-->持久层

假设持久层有两个dao,dao1,dao2。业务层有两个service,service1和service2。

 Dao1{
xxx(){}
} Dao2{
yyy(){}
} Service1{
aaa(){
dao1.xxx();
dao2.yyy();
}
} Service2{
bbb();
}

事务是加在业务层的。若此时业务复杂,需要调用service1和service2的方法来共同完成业务功能。而此时service1和service2都有自己的事务管理,那么事务到底使用service1里的还是service2里的呢?此时就用到了业务的传播行为。

事务的传播行为 说明
PROPAGATION_REQUIRED 支持当前事务,如果不存在,就新建一个。
PROPAGATION_SUPPORTS 支持当前事务,如果不存在,就不使用事务。
PROPAGTION_MANDATORY 支持当前事务,如果不存在,就抛出异常。
PROPAGATION_REQUIRES_NEW 如果有事务存在,挂起当前事务,创建 一个新的事务。
PROPAGATION_NOT_SUPPORTED 如果有事务存在,挂起当前事务。以非事务的方式进行。
PROPAGATION_NEVER 如果有事务存在,抛出异常。以非事务的方式进行。
PROPAGATION_NESTED 如果有事务存在,则嵌套事务执行。

重点记忆这三种:PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW,PROPAGATION_NESTED。

PROPAGATION_REQUIRED:保证service1的aaa()和service2的bbb()在同一个事务里。

PROPAGATION_REQUIRES_NEW:保证service1的aaa()和service2的bbb()不在同一个事务里。

PROPAGATION_NESTED:service1的aaa()执行完后可以设置一个保存点,如果service2的bbb()执行时出了问题,可以选择回滚到保存点,也可以选择回滚到最初点。是一种嵌套事务。

3.3 TransactionStatus

提供一组方法来获取、设置事务的一些状态信息。比如isCompleted(),isRollBackOnly()。

4.为后面事务管理建设场景--转账示例

4.1创建表

数据库名:spring_transaction

表名:account

4.2 创建项目

eclipse-->new web project-->输入project name为spring_transaction

项目结构:

 spring_transaction
src
cn.muke.spring.*
log4j.properties
applicationContext.xml
jdbc.properties
WebRoot
META-INF
WEB-INF
lib
***.jar
web.xml

4.3 引入jar包

数据库驱动:mysql-connector-java-5.0.8-bin.jar

连接池:com.springsource.com.mchange.v2.c3p0.jar

spring:com.springsource.org.apache.commons.logging-1.1.1.jar

com.springsource.org.apache.log4j-1.2.15.jar

spring-beans-3.2.0.RELEASE.jar

spring-context-3.2.0.RELEASE.jar

spring-core-3.2.0.RELEASE.jar

spring-expression-3.2.0.RELEASE.jarr

spring-jdbc-3.2.0.RELEASE.jar(数据库)

spring-tx-3.2.0.RELEASE.jar(事务管理)

spring-test-3.2.0.RELEASE.jar(单元测试)

4.4 配置文件

(1) log4j.properties

(2) applicationContext.xml

<beans>

<!--引入外部属性文件-->

<context:property-placeholder location:"classpath:jdbc.properties"/>

<--配置c3p0连接池-->

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">

<property name="driverClass" value="${jdbc.driverClass}"/>

<property name="jdbcUrl" value="${jdbc.url}"/>

<property name="user" value="${jdbc.username}"/>

<property name="password" value="${jdbc.password}"/>

</bean>

<!--配置业务层类-->

<bean id="AccountService" class="cn.muke.spring.demo1.AccountServiceImpl"/>

<!--为业务层注入dao-->

<property name="accountDao" ref="AccountDao"/>

</bean>

<!--配置DAO的类,方法1-->

<bean id="AccountDao" class="cn.muke.spring.demo1.AccountDaoImpl">

<!--为dao注入jdbc连接池,就能自动创建jdbcTemplate-->

<property name="dataSource" ref="dataSource"/>

</bean>

<!--配置DAO的类,方法2-->

<bean id="AccountDao" class="cn.muke.spring.demo1.AccountDaoImpl">

<!--为dao注入jdbc模板-->

<property name="jdbcTemplate" ref="jdbcTemplate"/>

</bean>

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">

<property name="dataSource" ref="dataSource"/>

</bean>

</beans>

(3) jdbc.properties

jdbc.driverClass=com.mysql.jdbc.Driver

jdbc.url=jdbc:mysql:///spring_transaction

jdbc.username=root

jdbc.password=123

4.5 代码

package cn.muke.spring.demo1;

(1) AccountService 和 AccountServiceImpl

public interface AccountService{

//转出账户,转入账户,转账金额

public void transfer(String out, String in, Double money);

}

public class AccountServiceImpl implements AccountService{

private AccountDao accountDao;

public void SetAccountDao()AccountDao accountDao{

this.accountDao = accountDao;

}

//转出账户,转入账户,转账金额

public void transfer(String out, String in, Double money){

accountDao.outMoney(out,money);

accountDao.outMoney(in,money);

}

}

(2) AccountDao 和 AccountDaoImpl

public interface AccountDao{

public void outMoney(String out, Double money);

public void InMoney(String in, Double money);

}

//jdbcDaoSupport提供了一个属性:jdbcTemplate,查看jdbcDaoSupport源代码可知,只要提供一个连接池,就能自动创建一个jdbcTemplate。

public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao{

public void outMoney(String out, Double money){

String sql = "update account set money = money - ? where name = ? ";

this.getJdbcTemplate().update(sql,money,out);

}

public void InMoney(String in, Double money){

String sql = "update account set money = money + ? where name = ? ";

this.getJdbcTemplate().update(sql,money,in);

}

}

(3 ) 测试类SpringDemo

import org.junit.Test;

@RunWith(SpringJunit4ClassRunner.class)

@ContextConfiguration("classpath:applcationContext.xml")

public class SpringDemo{

//被测试的业务层类

@Resource("name=accountService")

private AccountService accountService;

@Test

public void demo1(){

accountService.transfer("aaa","bbb",200d);

}

}

选中方法demo1(),右键,选择run as junit4 test。

4.6 事务管理

前面的代码没有进行事务管理,如果在这里加一个会产生异常的代码。那么运行后,结果是:aaa从800变成了600,而bbb却仍然是1000,没有增加。

public class AccountServiceImpl implements AccountService{

//...

//转出账户,转入账户,转账金额

public void transfer(String out, String in, Double money){

accountDao.outMoney(out,money);

int i = 1/0 ; // 产生异常,后面的outMoney不会执行。

accountDao.outMoney(in,money);

}

}

5.编程式事务管理

(1)配置文件applicationContext.xml

<!--配置业务层类-->

<bean id="AccountService" class="cn.muke.spring.demo1.AccountServiceImpl"/>

<!--为业务层注入dao-->

<property name="accountDao" ref="AccountDao"/>

<!--为业务层配置事务管理的模板-->

<property name="transactionTemplate" ref="transactionTemplate"/>

</bean>

<!--配置事务管理的模板,是spring为了简化事务管理的代码而提供的类,需要提供事务管理器属性-->

<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">

<property name="transactionManager" ref="transactionManager"/>

</bean>

<!--配置事务管理器,需要提供连接池属性-->

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

<property name="dataSource" ref="dataSource"/>

</bean>

(2)代码

package cn.muke.spring.demo1;

public class AccountServiceImpl implements AccountService{

//...

private TransactionTemplate transactionTemplate;

public void SetTransactionTemplate(TransactionTemplate transactionTemplate){

this.transactionTemplate = transactionTemplate;

}

//这里要加上final,是因为在匿名内部类里使用了方法中的参数。所以方法中的参数要加上final,否则会报错。

public void transfer(final String out, final String in, final Double money){

transactionTemplate.execute(new TransactionCallbackWithoutResult(){// 匿名内部类

@Override

protected void doInTransactionWithoutResult( TransactionStatus transactionStatus ){

accountDao.outMoney(out,money);

int i = 1/0;

accountDao.outMoney(in,money);

}

});

}

}

(3)运行测试类

执行完,aaa还是800,没有变成600。因为事务回滚了。

5.声明式事务管理方式1--基于TransactionProxyFactoryBean

方式1实际中不适合使用,因为要为每一个类配置一个TransactionProxyFactoryBean,开发和维护都很麻烦。

(1)jar包

声明式事务管理是基于Spring的AOP思想,因此需要先引入AOP相关的jar包:

spring-aop-3.2.0.RELEASE.jar

com.springframework.org.aopalliance-1.0.0.jar

(2)配置文件applicationContext.xml

<!--配置业务层类-->

<bean id="AccountService" class="cn.muke.spring.demo1.AccountServiceImpl"/>

<!--为业务层注入dao-->

<property name="accountDao" ref="AccountDao"/>

</bean>

<!--配置事务管理器,需要提供连接池属性-->

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

<property name="dataSource" ref="dataSource"/>

</bean>

<!--配置业务层的代理-->

<bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">

<!--配置目标对象-->

<property name="target" ref="accountService"/>

<!--配置事务管理器-->

<property name="transactionManager" ref="transactionManager"/>

<!--注入事务属性-->

<property name="transactionAttributes" value="transactionManager">

<props>

<!--

key表示方法名,支持*的写法,比如tran*,*。

中间的值:

* PROPAGATION : 事务的传播行为

* ISOLATION:事务的隔离级别

* readOnly:只读,数据库不可以进行插入、修改、删除等操作。

* -Exception:发生哪些异常时进行事务回滚,比如 -java.lang.ArithmeticException。

* +Exception:发生哪些异常时不进行事务回滚

-->

<prop key="transfer"> PROPAGATION_REQUIRED <prop>

</props>

</property>

</bean>

ps: ref 和 value 的区别:ref里写的是类,value里写的是值。

(3)代码

package cn.muke.spring.demo1;

import org.junit.Test;

@RunWith(SpringJunit4ClassRunner.class)

@ContextConfiguration("classpath:applcationContext.xml")

public class SpringDemo1{

//被测试的业务层代理类

@Resource("name=accountServiceProxy")

private AccountService accountService;

@Test

public void demo1(){

accountService.transfer("aaa","bbb",200d);

}

}

(4)运行测试类

执行完后,报异常。查看数据库信息,aaa还是800,没有变成600。因为事务回滚了。

5.声明式事务管理方式2--基于AspectJ的xml方式

这种配置方式,不需要配置AccountServiceImpl的代理类。因为实现了自动代理,类AccountServiceimpl在生成过程中,这个类本身就已经是代理对象了。

在实际的使用中,也是比较多的。

(1)jar包

aspectJ是一个开源的、第三方的aop框架。需要引入aspectJ的相关jar包:

spring-aspects-3.2.0.RELEASE.jar

com.springframesource.org.aspectj.weaver-1.6.8.RELEASE.jar

(2)配置文件applicationContext.xml

注意命名空间的引入。

<beans ...

xmlns:aop="http://www.springframework.org/schema/aop"

xmlns:aop="http://www.springframework.org/schema/tx"

xsi:schemaLocation="...

http://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop/spring-aop.xsd

http://www.springframework.org/schema/tx

http://www.springframework.org/schema/tx/spring-tx.xsd"

...

/beans>

<!--配置业务层类-->

<bean id="AccountService" class="cn.muke.spring.demo1.AccountServiceImpl"/>

<!--为业务层注入dao-->

<property name="accountDao" ref="AccountDao"/>

</bean>

<!--配置事务管理器,需要提供连接池属性-->

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

<property name="dataSource" ref="dataSource"/>

</bean>

<!--配置事务的通知(事务的增强)-->

<tx:addvice id="txAdvice" transaction-manager="transactionManager">

<tx:attributes>

<!--

name表示方法名,支持*的写法,比如tran*,*。

属性key:

* propagation : 事务的传播行为。

* isolation:事务的隔离级别。

* read-only:false/true,只读,数据库不可以进行插入、修改、删除等操作。

* rollback-for:发生哪些异常时进行事务回滚。

* no-rollback-for:发生哪些异常时不进行事务回滚。

* time-out:过期信息。

-->

<tx:method name="transfer" propagation="REQUIRED"/>

</tx:attributes>

</tx:addvice>

<!-- 配置aop的切面-->

<aop:config>

<!--配置切入点,+表示AccountService的子类,*表示任何方法,..表示任何参数-->

<aop:pointcut expression="execution(* cn.muke.spring.demo.AccountService+.*(..))" id="pointcut1>"/>

<!--配置切面,下面这段配置表示在切入点pointcut1上,应用txAdvice增强。切入点的位置是AccountService所有子类的所有方法。-->

<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>

</aop:config>

(3)代码

无任何修改。

 

(4)运行测试类

执行完后,报异常。查看数据库信息,aaa还是800,没有变成600。因为事务回滚了。

6.声明式事务管理方式3--基于注解方式

(1)jar包

???

(2)配置文件applicationContext.xml

同样注意命名空间的声明。

<beans ...

xmlns:aop="http://www.springframework.org/schema/aop"

xmlns:aop="http://www.springframework.org/schema/tx"

xsi:schemaLocation="...

http://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop/spring-aop.xsd

http://www.springframework.org/schema/tx

http://www.springframework.org/schema/tx/spring-tx.xsd"

...

/beans>

<!--配置业务层类-->

<bean id="AccountService" class="cn.muke.spring.demo1.AccountServiceImpl"/>

<!--为业务层注入dao-->

<property name="accountDao" ref="AccountDao"/>

</bean>

<!--配置事务管理器,需要提供连接池属性-->

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

<property name="dataSource" ref="dataSource"/>

</bean>

<!--开启注解事务,开启之后就可以使用@Transactional注解了-->

<!--写成<tx:annotation-driven/>时默认加载名为transactionManager的事务管理器-->

<tx:annotation-driven transaction-mananger="transactionManager"/>

(3)代码

@Transactional

public class AccountServiceImpl implements AccountService{

//...

public void transfer( String out, String in, Double money){

accountDao.outMoney(out,money);

int i = 1/0;

accountDao.outMoney(in,money);

}

}

@Transactional注解也有属性配置:不写的时候按默认值配置。

* propagation : 事务的传播行为。

* isolation:事务的隔离级别。

* readOnly:false/true,只读,数据库不可以进行插入、修改、删除等操作。

* rollbackFor:发生哪些异常时进行事务回滚。

* noRollbackFor:发生哪些异常时不进行事务回滚。

* time-out:过期信息。

@Transactional(propagation=Propagation.REQUIRED, isolation=Isolation.DEFAULT, readOnly=false,...)

(4)运行测试类

执行完后,报异常。查看数据库信息,aaa还是800,没有变成600。因为事务回滚了。

7.总结

spring事务管理分为:编程式事务管理(很少使用)、声明式事务管理。

不论是声明式还是编程式,transactionManager都是必须配置的。

声明式事务管理:

基于TransactionFactoryProxyBean(很少使用,需要为每一个进行事务管理的类,配置一个增强生成代理。)

基于AspectJ的xml方式(常用,配置比xml复杂,但在配置文件中可以清晰看到增强的位置,并且类上不需要做任何修改。)

基于注解方式(常用,配置简单,但是需要修改业务层的类,为它们加上@Transactional注解。)

2017.4.18 慕课网-spring事务管理总结的更多相关文章

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

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

  2. Spring事务管理总结

    本文是对慕课网上"搞定SSM开发"路径的系列课程的总结,详细的项目文档和课程总结放在github上了.点击查看 本文对应慕课网上课程Spring事务管理,详情可查看:点我 1: 概 ...

  3. 框架源码系列十一:事务管理(Spring事务管理的特点、事务概念学习、Spring事务使用学习、Spring事务管理API学习、Spring事务源码学习)

    一.Spring事务管理的特点 Spring框架为事务管理提供一套统一的抽象,带来的好处有:1. 跨不同事务API的统一的编程模型,无论你使用的是jdbc.jta.jpa.hibernate.2. 支 ...

  4. Spring事务管理的四种方式(以银行转账为例)

    Spring事务管理的四种方式(以银行转账为例) 一.事务的作用 将若干的数据库操作作为一个整体控制,一起成功或一起失败.   原子性:指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不 ...

  5. Spring事务管理的demo

    事务是逻辑上的一组操作,这组操作要么全部成功,要么全部失败,最为典型的就是银行转账的案例: A要向B转账,现在A,B各自账户中有1000元,A要给B转200元,那么这个转账就必须保证是一个事务,防止中 ...

  6. Spring事务管理笔记

    事务的目的就是要保证数据的高度完整性和一致性. 在实际的项目中,大多都是使用注解的方式来实现事物,这里也就简单记录下使用@Transactional方法和注意事项. 在xml中添加配置 1234567 ...

  7. Spring事务管理全面分析

    Spring 事务属性分析什么是事物  事务管理对于企业应用而言至关重要.它保证了用户的每一次操作都是可靠的,即便出现了异常的访问情况,也不至于破坏后台数据的完整性.就像银行的自助取款机,通常都能正常 ...

  8. Spring - 事务管理概述

      什么是事务管理? 第一个问题:什么是事务? 事务一般是相对数据库而言的,对于数据库一次操作就属于一个事务, 一次操作可以是几句 SQL 语句,也可以是若干行 JDBC 的 Java 语句.事务既然 ...

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

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

随机推荐

  1. bzoj1264 [AHOI2006]基因匹配Match 树状数组+lcs

    1264: [AHOI2006]基因匹配Match Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1255  Solved: 835[Submit][ ...

  2. Http错误大全

    HTTP/IIS错误类型 1XX 信息提示 用于表示临时的响应.客户端在收到常规响应之前,应准备接受一个或多个1XX响应. 100 :继续101 :切换协议 2XX 成功 表示服务器成功地接受了客户端 ...

  3. Create Windows Server 2008 cluster from the command line

    How to create a Windows Server 2008 cluster from the command line? Creating a cluster in Server 2008 ...

  4. hdu 4089 概率dp

    /* 题目大意:注册一款游戏需要排队,一共有四种事件: 1.注册失败,队列不变,概率为p1 2.注册过程中断开连接,正在注册的人排到队列的末尾,概率为p2 3.注册成功,移出队列,概率为p3 4.服务 ...

  5. 汕头市队赛 SRM 08 A

    比赛没参加 所以回来补题咯 A还是自己YY出来了的 可惜比赛没有打 描述 给一个 01 串设为其 S,询问是否存在只出现两次的 01 串 T. 这里的出现定义为存在一串下标 ,满足  且 . 输入格式 ...

  6. Linux Mint---开启桌面三维特效

    其实系统默认已经安装好了compiz,我们只需要切换就可以了 menu->control center->desktop setting->window 开启compiz的时候,由于 ...

  7. ffmpeg muxer 参数简要说明

    参数 值 说明 movflags MP4 Muxer 标记 rtphint 增加RTP的hint track empty_moov 初始化空的moov box frag_keyframe 在视频关键帧 ...

  8. 关于boostrapValidator动态添加字段(addField)验证的bug

    每次码博客,都觉得自己怀才不遇,哎~脑袋有瑕疵,文笔拿不粗手,就直接上干货吧. 在使用boostrapValidator这个验证插件的时候,如果某一个字段是动态添加来的,我们需要调用方法:addFie ...

  9. CF978E Bus Video System【数学/前缀和/思维】

    [链接]: CF [分析]: 设上车前人数 x ,中途最大人数为 x+max ,最小人数为 x+min (max≥0,min≤0) 可得不等式组 x+max≤w, x+min≥0 整数解个数为 max ...

  10. Educational Codeforces Round 1D 【DFS求联通块】

    http://blog.csdn.net/snowy_smile/article/details/49924965 D. Igor In the Museum time limit per test ...