`

第十章 Spring对事务的支持

阅读更多
Spring对事务的支持
分两种方式: 注释语法 与 XML的配置

 注释方式
首先在头文件中加入事务的支持
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd          
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
           http://www.springframework.org/schema/tx
           http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
</beans>
和AOP一样,注意红色部分

接着我们需要配置一个事务管理器对象(Spring提供了), 因为事务是需要使用到Connection对象的,所以需要告诉事务管理器对象Connection对象从哪里来(从我们配置的数据源中获取)
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />
然后我们就可以使用Spring提供的事务功能了.
<tx:annotation-driven transaction-manager="txManager" />
这一句就是开启事务注释的功能,如果事务采用XML配置的方式的话,这一句是可以不要的.

使用示例,我们可以在Service实现类中加入事务的支持(@Transactional),如下:
@Transactional
public class UserServiceImpl implements UserService {
private UserDao userDao;

public void batchCreateUser(String[] names) throws Exception {
for(String name : names){
userDao.create(new User(name));
}
throw new Exception();
}

public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
那么这个时候,Spring会自动的在我们服务层的方法开始时加入事务的begin,结束时加入事务的commit,异常时加入事务的rollback,

执行Test:
public class Test {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = (UserService)ctx.getBean("userService");
try {
userService.batchCreateUser(new String[]{"李四9","李四10"});
} catch (Exception e) {
e.printStackTrace();
}
}
}
请观察结果,我们发现事务好像没有起作用,虽然发生了异常,但是记住还是加入到数据库中去了.

接下来,我们修改服务层实现类的代码,让其抛出一个RuntimeException时异常, 如下:
@Transactional
public class UserServiceImpl implements UserService {
private UserDao userDao;

public void batchCreateUser(String[] names) throws Exception {
for(String name : names){
userDao.create(new User(name));
}
throw new RuntimeException();
}

public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
再次执行Test文件,这时,我们发现事务起作用了,数据并没有插入到数据库中,这时说明一个问题,Spring的事务管理器针对异常类型的不同,处理方式有些不一样.
运行时异常(可以不捕获): 回滚
非运行时异常(必须捕获): 不回滚
以上是默认行为,但是我们可以通过配置来更改这种行为了,我们可以让其 运行时异常不回滚或让非运行时异常回滚.

还有需要注意的地方就是: 事务的注释加入的位置
如果加在类头上
@Transactional
public class UserServiceImpl implements UserService {
}
这时事务会对类中所有的方法都起作用.

如果加在方法头上,那么此时,事务只会对指定方法起作用,如:
public class UserServiceImpl implements UserService {
private UserDao userDao;

@Transactional
public void batchCreateUser(String[] names) throws Exception {
for(String name : names){
userDao.create(new User(name));
}
throw new RuntimeException();
}

public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}

接下来,我们看一下,怎么更改事务对抛出不同类型异常的默认行为
让非运行时异常也回滚
public class UserServiceImpl implements UserService {
private UserDao userDao;

@Transactional(rollbackFor=Exception.class)
public void batchCreateUser(String[] names) throws Exception {
for(String name : names){
userDao.create(new User(name));
}
throw new Exception();
}

public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}

让运行时异常不回滚:
public class UserServiceImpl implements UserService {
private UserDao userDao;

@Transactional(noRollbackFor=RuntimeException.class)
public void batchCreateUser(String[] names) throws Exception {
for(String name : names){
userDao.create(new User(name));
}
throw new RuntimeException();
}

public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
通常的做法是: @Transactional(rollbackFor=Exception.class) 让非运行时异常回滚,那么这时我们就可以保证只要发生异常就会回滚事务了.

事务的传播属性
JDBC模式下通常的查询方法,因为并没有涉及到数据库的更新操作,所以一般是不需要加入事务的,如下配置:
@Transactional(propagation=Propagation.NOT_SUPPORTED)
public List<User> findUserAll() throws Exception {
return userDao.findUserAll();
}
告诉spring事务管理器, 此方法不需要加入事务

事务的只读属性
@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
public List<User> findUserAll() throws Exception {
return userDao.findUserAll();
}
对于查询数据库的操作,是没有更新操作的,那么我们可以在查询的方法上加上readOnly=true,指明方法不会执行数据库的更新操作,从而提高性能.如果方法上设置了readOnly=true,然而又执行了更新操作,Spring就会报错的.

 XML配置事务
首先删除掉Service类中的注释的地方,保证有一个干净的Service实现类
public class UserServiceImpl implements UserService {
private UserDao userDao;

public void batchCreateUser(String[] names) throws Exception {
for(String name : names){
userDao.create(new User(name));
}
throw new Exception();
}

public List<User> findUserAll() throws Exception {
return userDao.findUserAll();
}

public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
XML配置:
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="find*" read-only="true" />
<tx:method name="*" rollback-for="Exception"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="txPointcut"
expression="execution(* com.wdpc.springjdbc.service..*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" />
</aop:config>
定义一个事务通知(和AOP的通知有点相似): <tx:advice id="txAdvice" transaction-manager="txManager">
在通知中指明所有find开头的方法,不会涉及到数据库的更新操作,声明事务为只读属性,并不开启事务,注意的地方(propagation="NOT_SUPPORTED" read-only="true")不能同时配置在一起
<tx:method name="find*" read-only="true"/>

在通知中指明所有的方法,只要发生异常就回滚: <tx:method name="*" rollback-for="Exception"/>

利用AOP的特点来进行事务的拦截
定义一个切入点: <aop:pointcut id="txPointcut"
expression="execution(* com.wdpc.springjdbc.service..*.*(..))" />
绑定切入点和事务通知: <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" />

这个时候只要匹配了切入点中的方法,就会进行事务的拦截.

再次执行Test:
batchCreateUser方法抛出了Exception非运行时异常,按默认值配置来说,数据应该会进入数据库,但是我们在配置文件中指明了rollback-for="Exception",只要发生异常就回滚,可以观察一下数据进入数据库没有.

记住,事务一般在服务层进行拦截.

 事务的传播属性的研究
Propagation类规定了事务的传播属性
Propagation.REQUIRED: 开始一个新事务(也是默认值,不需要我们配置的),即如果已经存在事务,则加入到事务中,如果没有事务,则为自己新开一个事务.
Propagation.NOT_SUPPORTED: 声明方法不需要事务
Propagation.REQUIRES_NEW:不管事务是否存在,总会开启一个新的事务,如果方法在调用之前已经开启了一个事务,那么这个时候会出现事务嵌套.
Propagation.MANDATORY: 指定方法的调用必须在一个已经打开的事务中调用.方法没有自己的事务,如果方法的调用在没有事务的环境下调用,这时会抛出异常.
Propagation.NEVER:它和Propagation.MANDATORY相返,方法的调用绝对不能在事务的范围内执行.
Propagation.SUPPORTS: 随意型, 如果在事务范围内被调用,则会纳入事务的管理, 如果在没有事务管理范围内调用,也可以调用,但是就没有事务的特性了.

分享到:
评论

相关推荐

    跟我学spring3(8-13)

    【第十章】集成其它Web框架 之 10.1 概述 ——跟我学spring3 【第十章】集成其它Web框架 之 10.2 集成Struts1.x ——跟我学spring3 【第十章】集成其它Web框架 之 10.3 集成Struts2.x ——跟我学spring3 【第十章】...

    MyEclipse 6 Java 开发中文教程第十章

    第十章 开发Spring应用... 189 &lt;br&gt;10.1 简介... 189 &lt;br&gt;10.1.1 Spring简介... 189 &lt;br&gt;10.1.2 MyEclipse的Spring开发功能简介... 191 &lt;br&gt;10.2 开发简单的Spring应用... 191 &lt;br&gt;10.2.1 给项目加入...

    spring.net中文手册在线版

    第十章. 表达式求值 10.1.简介 10.2.表达式求值 10.3.语言参考 10.3.1.文字表达式 10.3.2.属性,数组,列表,字典,索引器 10.3.2.1.定义内联的数组、列表和词典 10.3.3.方法 10.3.4.操作符 10.3.4.1.关系操作符 ...

    搞定J2EE:STRUTS+SPRING+HIBERNATE整合详解与典型案例 (1)

    第十章 使用Spring快速实现Web开发 10.1 Spring介绍 10.1.1 Spring简介 10.1.2 下载Spring 10.1.3 配置Spring 10.2 Spring核心思想 10.2.1 反向控制(IoC) 10.2.2 依赖注入(DI) 10.3 利用Spring在JSP页面输出...

    JBPM5用户指南-PDF电子书-官方文档翻译

    第十章 设计器 第十一章 控制台 第十二章 Human Tasks 第十三章 特定领域的流程 第十四章 测试和调试 第十五章 流程知识库 第十六章 业务活动监视器 第十七章 复杂流程 第十八章 和Maven,OSGi,Spring的整合

    搞定J2EE:STRUTS+SPRING+HIBERNATE整合详解与典型案例 (3)

    第十章 使用Spring快速实现Web开发 10.1 Spring介绍 10.1.1 Spring简介 10.1.2 下载Spring 10.1.3 配置Spring 10.2 Spring核心思想 10.2.1 反向控制(IoC) 10.2.2 依赖注入(DI) 10.3 利用Spring在JSP页面输出...

    高质量Spring Cloud Alibaba学习笔记

    第一章 微服务介绍 第二章 微服务环境搭建 第三章 Nacos Discovery--服务治理 第四章 Sentinel--服务容错 第五章 Gateway--服务网关 第六章 Sleuth--链路追踪 ...第十章 Seata--分布式事务 扩展章节 Dubbo--rpc通信

    搞定J2EE:STRUTS+SPRING+HIBERNATE整合详解与典型案例 (2)

    第十章 使用Spring快速实现Web开发 10.1 Spring介绍 10.1.1 Spring简介 10.1.2 下载Spring 10.1.3 配置Spring 10.2 Spring核心思想 10.2.1 反向控制(IoC) 10.2.2 依赖注入(DI) 10.3 利用Spring在JSP页面输出...

    SpringCloudAilibaba笔记

    本资源主要是介绍了SpringCloudAlibaba中一些组件,手把手带你搭建一个微服务架构 第一章 微服务介绍 第二章 微服务环境搭建 第三章 Nacos-Discovery 服务治理 第四章 Sentinel 服务容错 ...第十章 seata 分布式事务

    《MyEclipse 6 Java 开发中文教程》前10章

    第十章 开发Spring应用 198 10.1 简介 198 10.1.1 Spring简介 198 10.1.2 MyEclipse的Spring开发功能简介 200 10.2 开发简单的Spring应用 200 10.2.1 给项目加入Spring功能 200 10.2.2 创建Bean类和配置信息 202 ...

    Java语言基础下载

    第十章:JAVA GUI概述 155 学习目标 155 GUI概述及组成 156 Swing优点 157 布局管理器 158 BorderLayout 159 GridLayout 161 CardLayout 162 GridBagLayout 164 实例分析 165 内容总结 172 独立实践 173 第十一章 ...

    Java/JavaEE 学习笔记

    第十章 Oracle Data Dictionary(数据字典表)........................138 第十一章 Manipulating Data(对表的操作)140 第十二章 Altering Tables and Constraints..142 第十三章 Creating Sequences..........145 第...

    jBPM5 用户指南-中文

    第十章 设计器 72 10.1 安装 73 第十一章 控制台 74 11.1 安装 74 11.2 运行流程管理控制台 74 11.2.1 管理流程实例 75 11.2.2 人工任务列表 77 11.2.3 报告 78 11.3 添加新的流程/任务表 79 11.4 REST interface 80...

    J2EE学习笔记(J2ee初学者必备手册)

    第十章 Oracle Data Dictionary(数据字典表)........................138 第十一章 Manipulating Data(对表的操作)140 第十二章 Altering Tables and Constraints..142 第十三章 Creating Sequences..........145 第...

    JBPM5 用户指南

    第十章 设计器 66 10.1 安装 67 第十一章 控制台 68 11.1 安装 68 11.2 运行流程管理控制台 68 11.2.1 管理流程实例 69 11.2.2 人工任务列表 71 11.2.3 报告 72 11.3 添加新的流程/任务表 73 11.4 REST interface 74...

    JBPM5权威指南

    第十章 设计器 66 10.1 安装 67 第十一章 控制台 68 11.1 安装 68 11.2 运行流程管理控制台 68 11.2.1 管理流程实例 69 11.2.2 人工任务列表 71 11.2.3 报告 72 11.3 添加新的流程/任务表 73 11.4 REST interface 74...

    《程序天下:J2EE整合详解与典型案例》光盘源码

    第十章 使用Spring快速实现Web开发 10.1 Spring介绍 10.1.1 Spring简介 10.1.2 下载Spring 10.1.3 配置Spring 10.2 Spring核心思想 10.2.1 反向控制(IoC) 10.2.2 依赖注入(DI) 10.3 利用Spring在JSP页面输出...

    Activiti5用户指南(中文版)

    第一章、简介 ........................................................................................................................................................................... 1 1.1 许可 ......

Global site tag (gtag.js) - Google Analytics