Spring事务管理

事务

事务的基本概念
事务指的是逻辑上的一组操作,这组操作要么全部成功,要么全部失败。

事务的特性
原子性:事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。

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

隔离性:多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务所干扰,多个并发事务之间数据要相互隔离(数据库中相应的数据隔离级别,通过它避免事务间的冲突)。

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

Spring中的事务管理

Spring提供了一组接口进行事务的管理。
Spring提供事务管理的3个接口:
【1】PlatformTransactionManager:事务管理器,用来管理事务的接口,定义了事务的提交、回滚等方法。
【2】TransactionDefinition:事务定义信息(隔离级别、传播行为、是否超时、是否只读)
【3】TransactionStatus:事务具体运行状态(事务是否提交,事务是否有保存点,事务是否是新事物等状态)。
Spring事务管理时,这三个接口是有联系的,Spring首先会根据事务定义信息TransactionDefinition获取信息,然后由事务管理器PlatformTransactionManager进行管理,在事务管理过程中,会产生一个事务的状态,这个状态就保存在事务具体运行状态TransactionStatus中了。

PlatformTransactionManager接口介绍:
通过Spring的API可以知道该接口有许多实现类例如:DataSourceTransactionManager、HibernateTransactionManager等。Spring会为不同的持久化框架提供了不同PlatformTransactionManager接口实现。
比如当我们使用SpringJDBC或者iBatis进行持久化数据时使用DataSourceTransactionManager。

image.png

TransactionDefinition定义事务隔离级别
该接口还提供了一些方法,例如:获得隔离级别、获得超时信息、获得是否只是只读的等。
如果不考虑隔离性,就会引发安全问题:脏读、不可重复读、以及虚读或者叫做幻读。

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

不可重复读:同一事务中,多次读取同一数据返回的结果有所不同(读取到另一个事务已经提交的更新的数据)。

幻读:一个事务读取了几行记录后,另一个事务插入一些记录,幻读就发生了。再后来的查询中,第一个事务就会发现有些原来没有的记录。

正常情况下,数据库提供了四种隔离级别:
READ_UNCOMMITED:安全级别最低,如果设置为该级别,就可能会发生脏读、不可重复读、幻读等。

READ_COMMITED:如果设置该级别,可以避免脏读的发生,但是可能会发生不可重复读和幻读。

REPEATABLE_READ:如果设置该级别,可以避免脏读和不可重复读,但是可能会发生幻读。

SERIALIZABLE:事务是串行的,不会发生并发访问这种情况
Spring提供了DEFAULT,它代表使用数据库默认的隔离级别(例如:Mysql默认采用REPEATABLE_READ隔离级别,Oracle默认采用READ_COMMITTED隔离级别)。

TransactionDefinition定义事务传播行为
事务的传播行为:解决业务层方法之间相互调用的问题(一个service层里的方法调用另一个service里中的方法,这两个service中又分属于两个不同的事务,传播行为就是为了解决方法调用时事务的传递)。

事务的传播行为有7种,可以为3类:


image.png

第一类为前三个,重点掌握第一个(在相同事务里):支持当前事务(Service中bbb()调用Service中aaa()方法时,如果aaa()有事务,则使用该事务。如果没有事务,则使用bbb()当前事务,如果当前bbb()也没有事务,就会新创建一个事务)

第二类为接下来三个,重点掌握第一个(在不同事务中):如果aaa()有事务存在,挂起当前事务,创建一个新的事务(aaa()和bbb()不在一个事务中)。
第三类:如果当前事务存在,则嵌套事务执行(执行aaa()完后,会使用事务的保存点,在执行bbb()时如果发生异常,可以回滚到设置的保存点,也可以回滚到最初始的状态)。

TransactionStatus接口介绍
TransactionStatus接口:提供了获取事务状态的方法(例如:hasSavepoint()事务是否有保存点,isCompleted()事务是否已经完成,isNewTransaction()是否是新的事务)。

Spring事务管理的两种方式

【1】编程式的事务管理:手动在程序中编写代码实现事务管理,实际应用中很少使用,通过TransactionTemplate管理事务。

【2】声明式的事务管理:使用XML配置实现事务管理,推荐使用(代码侵入性最小),Spring的声明式事务管理是通过AOP实现的(没有代码之前开启事务,代码完成后提交事务)。

使用声明式事务管理实现转账示例

搭建事务管理环境(模拟转账环境)
【a】创建表及插入记录


image.png

【b】创建项目并引入jar包


image.png

【c】引入log4j.properties、applicationContext.xml、jdbc.properties配置文件。

【e】创建包结构,编写Dao及Service

package spring.demo3;
/*
 * 转账案例Dao层接口
 */
public interface AccountDao {
    
    public void outMoney(String out,Double money);
    
    public void inMoney(String in,Double money);

}
package spring.demo3;
/*
 * 转账案例的业务层接口
 */
public interface AccountService {

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

【f】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">  

    <!-- 引入外部的属性文件 --> 
    <context:property-placeholder location="classpath:jdbc.properties"/>
    
    <!-- 配置c3p0连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driverClassName}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="user" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
    
    <!--配置业务层类 -->
    <bean id="accountService" class="spring.demo3.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
        
    </bean>
    
    <!-- 配置DAO的类 -->
    <bean id="accountDao" class="spring.demo3.AccountDapImpl">
        <!-- 注入连接池  -->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

【g】Dao中获取JDBC模板
首先Dao实现类需要继承JdbcDaoSupport类,该类中就会注入JDBC模板,该类中定义了JDBC模板,并提供set方法只要Dao中注入模板就可以。也可以通过注入连接池获取JDBC模板。
【h】Dao方法具体编写


import org.springframework.jdbc.core.support.JdbcDaoSupport;



public class AccountDapImpl extends JdbcDaoSupport implements AccountDao {

    public void outMoney(String out, Double money) {
        // TODO Auto-generated method stub
        String sql="update acount set acount =acount - ? where name = ?";
        this.getJdbcTemplate().update(sql,money,out);

    }

    public void inMoney(String in, Double money) {
        // TODO Auto-generated method stub
        String sql="update acount set acount =acount + ? where name = ?";
        this.getJdbcTemplate().update(sql,money,in);
    }

}

【i】Service中注入Dao,并调用Dao中的方法

    package spring.demo3;

import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

/*
 * 业务层实现类
 */
public class AccountServiceImpl implements AccountService
{
    //注入转账的Dao的类
    private AccountDao accountDao;
    

    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
    
    

    public void transfer( String out,  String in,  Double money) {
        
        accountDao.outMoney(out, money);
        int i=1/0;
        accountDao.inMoney(in, money);
    }

}

【k】测试:由于Junit4和spring整合的包已经引入,所以通过注解@ContextConfiguration注解加载配置文件,这里使用注解@Resource(name="")方式注入AccountService。


import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/*
 * Spring的声明事务管理的方式二:基于AspectJ的xml方式配置
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext3.xml")
public class TestDemo3 {

    @Resource(name="accountService")
    private AccountService accountService;
    @Test
    /*
     * 转账案例测试
     */
    public void demo3(){
        accountService.transfer("aaa", "bbb", 100d);
    }
}

声明式事物管理方式:基于AspectJ的XML方式

【1】引入AspectJ的jar包和整合AspectJ的包。
AspectJ简介:Spring在开发AOP时,为了简化编程而建立的。(AspectJ实际是开源的、第三方的AOP开发框架,Spring为简化自身开发,就引入了AspectJ的包)
【2】配置事物管理器,并注入数据源dataSource。

<!-- 配置事务管理器  -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

【3】配置事物的通知
(事物的增强,通过<tx:advice id="" transaction-manager="事物管理器Id"></tx:advice>,这个标签中需要配置事物相关的一些属性<tx:attributes></tx:attributes>,该属性的作用就是哪些方法执行事务,<tx:method="方法名"></tx:method>如果多个方法可以用英文单词*,该标签中还定义了事物的传播行为、隔离级别、超时信息、只读等等属性)

    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--
              PROPAGATION:事务的传播行为 
              ISOLATION:事务的隔离级别
              readOnly:只读 
              rollback-for:发生哪些异常回滚
              no-rollback-for:发生哪些异常不回滚
              timeout:过期信息
               -->
        <tx:attributes>
            <tx:method name="transfer" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

【4】配置AOP的切面
(通过<aop:config></aop:config>,在<aop:config>下需要配置切入点<aop:point-cut expression="" id=""></aop:point-cut>,其中表达式最前面的表示方法返回值为任意的,+号表示及其子类,.表示任意的方法,(..)表示任意的参数,接下来需要配置切面其中<aop:aspect>代表多个切入点,多个通知,而<aop:advisor>是一个切入点,一个通知的。这里使用它即可。因为现在只有一个增强即事物增强,这样AccountService即其子类的任意方法都会事物增强了)

    <aop:config>
        <!--配置切入点  -->
        <aop:pointcut expression="execution(* spring.demo3.AccountService+.*(..))" id="pointcut1"/>
        <!-- 配置切面  -->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>
    </aop:config>

使用事务管理方式,一旦发生异常,整个事务不会执行,不会出现钱转丢的现象

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容