(转)Spring中的事务操作
http://blog.csdn.net/yerenyuan_pku/article/details/70024364
事务的回顾
什么是事务
事务是逻辑上的一组操作,组成这组操作的各个逻辑单元,要么一起成功,要么一起失败。
事务的特性
- 原子性:强调事务的不可分割。
- 一致性:事务的执行的前后数据的完整性保持一致。
- 隔离性:一个事务执行的过程中,不应该受到其他事务的干扰。
- 持久性:事务一旦结束,数据就持久化到数据库。
如果不考虑隔离性会引发的安全性问题
- 脏读:一个事务读到了另一个事务的未提交的数据。
- 不可重复读:一个事务读到了另一个事务已经提交的update的数据,导致多次查询的结果不一致。
- 虚读:一个事务读到了另一个事务已经提交的insert的数据,导致多次查询的结果不一致。
解决读问题:设置事务的隔离级别
- 未提交读:脏读、不可重复读和虚读都有可能发生。
- 已提交读:避免脏读,但是不可重复读和虚读有可能发生。
- 可重复读:避免脏读和不可重复读,但是虚读有可能发生。
- 串行化的:避免以上所有读问题。
mysql数据库的默认隔离级别就是可重复读。
Spring进行事务操作常用的API
PlatformTransactionManager:平台事务管理器
Spring进行事务操作时候,主要使用一个PlatformTransactionManager接口,它表示事务管理器,即真正管理事务的对象。
Spring针对不同的持久化框架,提供了不同PlatformTransactionManager接口的实现类,如下: 
TrancactionDefinition:事务定义信息
事务定义信息有:
- 隔离级别
- 传播行为
- 超时信息
- 是否只读
TrancactionStatus:事务的状态
记录事务的状态。
Spring的这组接口是如何进行事务管理的
平台事务管理器根据事务定义的信息进行事务的管理,事务管理的过程中产生一些状态,将这些状态记录到TrancactionStatus里面。
事务的传播行为
PROPAGION_XXX:事务的传播行为。
保证在同一个事务中
PROPAGION_REQUIRED:支持当前事务,如果不存在,就新建一个(默认)
PROPAGION_SUPPORTS:支持当前事务,如果不存在,就不使用事务
PROPAGION_MANDATORY:支持当前事务,如果不存在,就抛出异常保证没有在同一个事务中
PROPAGION_REQUIRES_NEW:如果有事务存在,挂起当前事务,创建一个新的事务
PROPAGION_NOT_SUPPORTED:以非事务方式运行,如果有事务存在,挂起当前事务
PROPAGION_NEVER:以非事务方式运行,如果有事务存在,抛出异常
PROPAGION_NESTED:如果当前事务存在,则嵌套事务执行
关于事务的传播行为,我真的是一点都不了解啊!希望随着时间的推移,能够真心明白。
Spring的声明式事务管理方式
Spring进行声明式事务配置的方式有两种:
- 基于xml配置文件方式
- 基于注解方式
这两种方式我都会讲解,但无论使用什么方式进行Spring的事务操作,首先要配置一个事务管理器。
搭建转账的环境
现在我举例来演示Spring如何进行声明式事务的配置。例子就是模拟银行转账,首先要搭建好转账的环境。
第一步,创建数据库表。
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
`id` int(11) DEFAULT NULL,
`username` varchar(100) DEFAULT NULL,
`salary` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `account` VALUES ('1', '小郑', '10000');
INSERT INTO `account` VALUES ('2', '小谭', '10000');
- 1
第二步,创建一个Web项目,并引入Spring的相关jar包。
第三步,创建业务层和DAO层的类。
在Web项目的src目录下创建一个cn.itcast.tx包,并在该包下编写业务层和DAO层的类。
业务层——BookService.java
public class BookService { private BookDao bookDao; public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
} }- 1
DAO层——BookDao.java
public class BookDao { private JdbcTemplate jdbcTemplate; public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
} }- 1
第四步,配置业务层和DAO层的类。
<?xml version="1.0" encoding="UTF-8"?>
<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.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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 配置C3P0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql:///spring_lee"></property>
<property name="user" value="root"></property>
<property name="password" value="yezi"></property>
</bean>
<!-- 创建service和dao的对象 -->
<bean id="bookService" class="cn.itcast.tx.BookService">
<!-- 注入dao -->
<property name="bookDao" ref="bookDao"></property>
</bean>
<bean id="bookDao" class="cn.itcast.tx.BookDao">
<!-- 注入JdbcTemplate模板类的对象 -->
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<!-- 创建JdbcTemplate模板类的对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 注入dataSource -->
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
- 1
第五步,转账的具体实现,实现小郑转账1000元给小谭。
JavaEE中DAO层做的事情主要是对数据库进行操作,在DAO层里面一般不写业务操作,一般写单独操作数据库的方法。所以BookDao类的代码要修改为:
public class BookDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
// 小郑少1000
public void lessMoney() {
String sql = "update account set salary=salary-? where username=?";
jdbcTemplate.update(sql, 1000, "小郑");
}
// 小谭多1000
public void moreMoney() {
String sql = "update account set salary=salary+? where username=?";
jdbcTemplate.update(sql, 1000, "小谭");
}
}
JavaEE中Service层写具体的业务操作,所以BookService类的代码要修改为:
public class BookService {
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
// 转账的业务
public void accountMoney() {
// 1.小郑少1000
bookDao.lessMoney();
// 2.小谭多1000
bookDao.moreMoney();
}
}
- 1
第六步,编写一个测试类。
在cn.itcast.tx包下编写一个TestDemo单元测试类。
public class TestDemo {
@Test
public void testAccount() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
BookService bookService = (BookService) context.getBean("bookService");
bookService.accountMoney();
}
}
测试以上方法即可实现小郑转账1000元给小谭。现在我来演示一个问题,在BookService类中调用BookDao类的两个方法构成了转账业务,但是如果小郑少了1000元之后,这时突然出现异常,比如银行断电,就会出现小郑的钱少了,而小谭的钱没有多,钱丢失了的情况。
public class BookService {
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
// 转账的业务
public void accountMoney() {
// 1.小郑少1000
bookDao.lessMoney();
int x = 10 / 0; // 模拟银行断电的情况(出现的异常)
// 2.小谭多1000
bookDao.moreMoney();
}
}
- 1
这时应该怎么解决这个问题呢?就可使用事务来解决。Spring中进行事务的操作主要有两种方式:
- 第一种:编程式事务管理(这种了解就行,不用掌握)
- 第二种:声明式事务管理
- 基于xml配置文件方式
- 基于注解方式
Spring的声明式事务管理——XML方式:思想就是AOP
基于xml配置文件的方式来进行声明式事务的操作,不需要进行手动编写代码,通过一段配置完成事务管理。下面我在搭建好的转账环境下演示它。
第一步,配置事务管理器。
之前,我就讲过Spring针对不同的持久化框架,提供了不同PlatformTransactionManager接口的实现类,如下: 
所以我们需要在Spring的配置文件中添加如下配置:
<!-- 1.配置事务的管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 指定要对哪个数据库进行事务操作 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
第二步,配置事务的增强,即指定对哪个事务管理器进行增强。故需要向Spring的配置文件中添加如下配置:
<!-- 2.配置事务的增强,指定对哪个事务管理器进行增强 -->
<tx:advice id="txadvice" transaction-manager="transactionManager">
<tx:attributes>
<!--
表示来配置你要增强的方法的匹配的一个规则,
注意:只须改方法的命名规则,其他都是固定的!
propagation:事务的传播行为。
-->
<tx:method name="account*" propagation="REQUIRED"></tx:method>
<!-- <tx:method name="insert*" propagation="REQUIRED"></tx:method> -->
</tx:attributes>
</tx:advice>
- 1
第三步,配置切入点和切面。这步须向Spring的配置文件中添加如下配置:
<!-- 3.配置切入点和切面(最重要的一步) -->
<aop:config>
<!-- 切入点 -->
<aop:pointcut expression="execution(* cn.itcast.tx.BookService.*(..))" id="pointcut1"/>
<!-- 切面,即表示把哪个增强用在哪个切入点上 -->
<aop:advisor advice-ref="txadvice" pointcut-ref="pointcut1"/>
</aop:config>
- 1
这时测试TestDemo单元测试类的testAccount方法即可。
Spring的声明式事务的注解方式
基于注解方式来进行声明式事务的操作会更加简单,在实际开发中我们也会用的比较多。下面我在搭建好的转账环境下演示它。
第一步,配置事务管理器。
<!-- 1.配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
第二步,开启事务注解。
<!-- 2.开启事务的注解 -->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
以上配置添加完毕之后,Spring核心配置文件的内容就为:
<?xml version="1.0" encoding="UTF-8"?>
<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.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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 配置C3P0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql:///spring_lee"></property>
<property name="user" value="root"></property>
<property name="password" value="yezi"></property>
</bean>
<!-- 1.配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 2.开启事务的注解 -->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
<!-- 创建service和dao的对象 -->
<bean id="bookService" class="cn.itcast.tx.BookService">
<!-- 注入dao -->
<property name="bookDao" ref="bookDao"></property>
</bean>
<bean id="bookDao" class="cn.itcast.tx.BookDao">
<!-- 注入JdbcTemplate模板类的对象 -->
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<!-- 创建JdbcTemplate模板类的对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 注入dataSource -->
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
- 1
第三步,在具体使用事务的方法所在的类上面添加注解:@Transactional。即BookService类应修改为:
@Transactional
public class BookService {
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
// 转账的业务
public void accountMoney() {
// 1.小郑少1000
bookDao.lessMoney();
int x = 10 / 0; // 模拟银行断电的情况(出现的异常)
// 2.小谭多1000
bookDao.moreMoney();
}
}
注意:千万不要忘记这一步。
这时测试TestDemo单元测试类的testAccount方法即可。
(转)Spring中的事务操作的更多相关文章
- Spring中的事务操作
事务的特性 原子性:强调事务的不可分割. 一致性:事务的执行的前后数据的完整性保持一致. 隔离性:一个事务执行的过程中,不应该受到其他事务的干扰. 持久性:事务一旦结束,数据就持久化到数据库. 如果不 ...
- Spring 中的事务操作、注解、以及 XML 配置
事务 事务全称叫数据库事务,是数据库并发控制时的基本单位,它是一个操作集合,这些操作要么不执行,要么都执行,不可分割.例如我们的转账这个业务,就需要进行数据库事务的处理. 转账中至少会涉及到两条 SQ ...
- Spring中的事务管理详解
在这里主要介绍Spring对事务管理的一些理论知识,实战方面参考上一篇博文: http://www.cnblogs.com/longshiyVip/p/5061547.html 1. 事务简介: 事务 ...
- Spring,SpringMvc配置常见的坑,注解的使用注意事项,applicationContext.xml和spring.mvc.xml配置注意事项,spring中的事务失效,事务不回滚原因
1.Spring中的applicationContext.xml配置错误导致的异常 异常信息: org.apache.ibatis.binding.BindingException: Invalid ...
- Spring中@Transactional事务回滚
转载: Spring中@Transactional事务回滚 一.使用场景举例 在了解@Transactional怎么用之前我们必须要先知道@Transactional有什么用.下面举个栗子:比如一个部 ...
- SSM-Spring-23:概念《Spring中的事务是什么?》
------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 本篇博客会详细讲述Spring中的事务,会展开来用语言解释,用于了解概念和准备面试 事务的概念: 一个或者一组 ...
- Spring 中的事务
前言: 之前总结了事务以及数据库中事务相关的知识点,Spring 对于事务做了相应的封装,便于业务开发中使用事务. 项目中使用Spring中的事务首先时基于Mysql数据库中InnoDB 引擎的,如果 ...
- Spring中的事务管理
事务简介: 事务管理是企业级应用程序开发中必不可少的技术,用来确保数据的完整性和一致性 事务就是一系列的动作,它们被当作一个单独的工作单元.这些动作要么全部完成,要么全部不起作用 事务的四个关键属性( ...
- django基础之day05,orm字段参数,自定义需要的字段,orm中的事务操作
orm字段和参数 charfield varchar integerfield int bigintegerfield bigint emailfield varchar(254) datefield ...
随机推荐
- 替换Android自带apk【转】
本文转载自:http://www.voidcn.com/article/p-gonowdjh-vz.html 安卓自带的app放在/system/app/下,当我们想要替换这些应用时可以参考如下步骤: ...
- 删除oracle数据库用户的dba权限(当出现同一用户DBA可以登录,normal不能登录)“无法对SYS拥有的对象创建触发器”
系统报错:“无法对SYS拥有的对象创建触发器”,搞不懂是什么原因了,到底这触发器要用什么用户才能建立啊? ORA-04089: 无法对 SYS 拥有的对象创建触发器 第一种方式: 首先,用sys用户a ...
- 并不对劲的bzoj4198:loj2132:uoj130:p2168:[NOI2015]荷马史诗
题目大意 有\(n\)(\(n\leq10^5\))种单词,其中第\(i\)种单词在文章中的出现次数为\(w_i\) 要将每个单词替换成一个字符集为\(k\)(\(k\leq9\))的字符串,使对于任 ...
- 【AHOI 2005】 约数研究
[题目链接] 点击打开链接 [算法] 要求M,显然可以通过约数个数定理从1..N暴力计算答案,然而n最大10^6,这个算法的时间复杂度是 O(N * sqrt(N))的,不能通过此题 因此我们换一种思 ...
- 使用cgroups限制MongoDB的内存使用
cgroups,其名称源自控制组群(control groups)的简写,是Linux内核的一个功能,用来限制,控制与分离一个进程组群的资源(如CPU.内存.磁盘输入输出等). 这个项目最早是由Goo ...
- 点击button传递消息,但是页面不跳转的解决方法
最近在做一个物联网的项目时遇到的问题:界面上有很多控制开/关灯的button,通过点击button来控制各个灯的亮灭.我需要将获取的不同的点击事件消息,以Socket通信的方式发送给硬件端的服务监听程 ...
- C语言atoi()函数:将字符串转换成int(整数)
头文件:#include <stdlib.h> atoi() 函数用来将字符串转换成整数(int),其原型为:int atoi (const char * str); [函数说明]atoi ...
- BZOJ4571
BZOJ4571 Description Transmission Gate 给定n个数, m次询问, 每次询问[l,r]范围内的数加上x后异或b的最大值, x, b给出. \[n,m <= 2 ...
- codeforces 570 E. Pig and Palindromes (DP)
题目链接: 570 E. Pig and Palindromes 题目描述: 有一个n*m的矩阵,每个小格子里面都有一个字母.Peppa the Pig想要从(1,1)到(n, m).因为Peppa ...
- 51Nod 1315 合法整数集
1315 合法整数集 题目来源: TopCoder 基准时间限制:1 秒 空间限制:131072 KB 分值: 10 难度:2级算法题 收藏 关注 一个整数集合S是合法的,指S的任意子集subS有Fu ...