(转)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进行声明式事务配置的方式有两种:

  1. 基于xml配置文件方式
  2. 基于注解方式

这两种方式我都会讲解,但无论使用什么方式进行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中进行事务的操作主要有两种方式:

  1. 第一种:编程式事务管理(这种了解就行,不用掌握)
  2. 第二种:声明式事务管理 
    • 基于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方法即可。

转载于:https://www.cnblogs.com/telwanggs/p/6944046.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值