博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
是的发生
阅读量:7125 次
发布时间:2019-06-28

本文共 10594 字,大约阅读时间需要 35 分钟。

hot3.png

Spring事务管理及几种简单的实现

2017年05月01日 22:30:15

阅读数:9235

事务是逻辑上的一组操作,这组操作要么全部成功,要么全部失败,最为典型的就是银行转账的案例:

A要向B转账,现在A,B各自账户中有1000元,A要给B转200元,那么这个转账就必须保证是一个事务,防止中途因为各种原因导致A账户资金减少而B账户资金未添加,或者B账户资金添加而A账户资金未减少,这样不是用户有损失就是银行有损失,为了保证转账前后的一致性就必须保证转账操作是一个事务。

事务具有的ACID特性,参考。

首先,这篇文章先提及一些Spring中事务有关的API,然后分别实现编程式事务管理和声明式事务管理,其中声明式事务管理分别使用基于TransactionProxyFactoryBean的方式、基于AspectJ的XML方式、基于注解方式进行实现。

首先,我们简单看一下Spring事务管理需要提及的接口,Spring事务管理高层抽象主要包括3个接口

PlatformTransactionManager :事务管理器(用来管理事务,包含事务的提交,回滚) 

TransactionDefinition :事务定义信息(隔离,传播,超时,只读) 
TransactionStatus :事务具体运行状态

Spring根据事务定义信息(TransactionDefinition)由平台事务管理器(PlatformTransactionManager)真正进行事务的管理,在进行事务管理的过程中,事务会产生运行状态,状态保存在TransactionStatus中

PlatformTransactionManager:

Spring为不同的持久化框架提供了不同的PlatformTransactionManager如: 

在使用Spring JDBC或iBatis进行持久化数据时,采用DataSourceTransactionManager 
在使用Hibernate进行持久化数据时使用HibernateTransactionManager

TransactionDefinition:

TransactionDefinition接口中定义了一组常量,包括事务的隔离级别,事务的传播行为,超时信息,其中还定义了一些方法,可获得事务的隔离级别,超时信息,是否只读。

传播行为主要解决业务层方法之间的相互调用产生的事务应该如何传递的问题。

TransactionDefinition中定义的属性常量如下:

Field(属性) Description(描述)
ISOLATION_DEFAULT 使用底层数据存储的默认隔离级别
ISOLATION_READ_COMMITTED 表示防止脏读;可能会发生不可重复的读取和幻像读取
ISOLATION_READ_UNCOMMITTED 表示可能会发生脏读,不可重复的读取和幻像读取
ISOLATION_REPEATABLE_READ 表示禁止脏读和不可重复读;可以发生幻影读取
ISOLATION_SERIALIZABLE 表示可以防止脏读,不可重复的读取和幻像读取
PROPAGATION_MANDATORY 支持当前交易;如果不存在当前事务,则抛出异常
PROPAGATION_NESTED 如果当前事务存在,则在嵌套事务中执行,其行为类似于PROPAGATION_REQUIRED
PROPAGATION_NEVER 不支持当前交易;如果当前事务存在,则抛出异常
PROPAGATION_NOT_SUPPORTED 不支持当前交易;而是总是非事务地执行
PROPAGATION_REQUIRED 支持当前交易;如果不存在,创建一个新的
PROPAGATION_REQUIRES_NEW 创建一个新的事务,挂起当前事务(如果存在)
PROPAGATION_SUPPORTS 支持当前交易;如果不存在,则执行非事务性的
TIMEOUT_DEFAULT 使用底层事务系统的默认超时,如果不支持超时,则为none

TransationStatus:

在该接口中提供了一些方法:

Method Description
flush() 将基础会话刷新到数据存储(如果适用):例如,所有受影响的Hibernate / JPA会话
hasSavepoint() 返回此事务是否内部携带保存点,也就是基于保存点创建为嵌套事务
isCompleted() 返回此事务是否完成,即是否已经提交或回滚
isNewTransaction() 返回当前交易是否是新的(否则首先参与现有交易,或者潜在地不会在实际交易中运行)
isRollbackOnly() 返回事务是否已被标记为仅回滚(由应用程序或由事务基础结构)
setRollbackOnly() 设置事务回滚

了解了上述接口,接下来我们通过转账案例来实现Spring的事务管理:

数据库中account表如下: 

这里写图片描述

1.编程式事务管理实现

AccountDao.java:

package com.spring.demo1;/** * Created by zhuxinquan on 17-4-27. */public interface AccountDao {    public void outMoney(String out, Double money);    public void inMoney(String in, Double money);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

AccountDaoImp.java

package com.spring.demo1;import org.springframework.jdbc.core.support.JdbcDaoSupport;/** * Created by zhuxinquan on 17-4-27. */public class AccountDaoImp 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);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

AccountService.java

package com.spring.demo1;/** * Created by zhuxinquan on 17-4-27. */public interface AccountService {    public void transfer(String out, String in, Double money);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

AccountServiceImp.java

package com.spring.demo1;import org.springframework.transaction.TransactionStatus;import org.springframework.transaction.support.TransactionCallbackWithoutResult;import org.springframework.transaction.support.TransactionTemplate;/** * Created by zhuxinquan on 17-4-27. */public class AccountServiceImp implements AccountService{    private AccountDao accountDao;    //    注入事务管理的模板    private TransactionTemplate transactionTemplate;    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {        this.transactionTemplate = transactionTemplate;    }    public void setAccountDao(AccountDao accountDao) {        this.accountDao = accountDao;    }    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);                //此处除0模拟转账发生异常                int i = 1 / 0;                accountDao.inMoney(in, money);            }        });    }}package com.spring.demo1;import org.springframework.transaction.TransactionStatus;import org.springframework.transaction.support.TransactionCallbackWithoutResult;import org.springframework.transaction.support.TransactionTemplate;/** * Created by zhuxinquan on 17-4-27. */public class AccountServiceImp implements AccountService{    private AccountDao accountDao;    //    注入事务管理的模板    private TransactionTemplate transactionTemplate;    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {        this.transactionTemplate = transactionTemplate;    }    public void setAccountDao(AccountDao accountDao) {        this.accountDao = accountDao;    }    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.inMoney(in, money);            }        });    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70

创建Spring配置文件applicationContext.xml:

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

编写测试类如下: 

SpringDemoTest1.java

import com.spring.demo1.AccountService;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.context.annotation.Configuration;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import javax.annotation.Resource;/** * Created by zhuxinquan on 17-4-27. * Spring编程式事务管理 */@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration("classpath:applicationContext.xml")public class SpringDemoTest1 {    @Resource(name = "accountService")    private AccountService accountService;    @Test    public void demo1(){        accountService.transfer("aaa", "bbb", 200d);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

2.基于TransactionProxyFactoryBean的声明式事务管理

Dao与Service代码与1中相同,applicationContext2.xml如下:

PROPAGATION_REQUIRED
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

此时注入时需要选择代理类,因为在代理类中进行增强操作,测试代码如下: 

SpringDemoTest2.java

import com.spring.demo2.AccountService;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import javax.annotation.Resource;/** * Created by zhuxinquan on 17-4-27. * Spring声明式事务管理:基于TransactionProxyFactoryBean的方式 */@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration("classpath:applicationContext2.xml")public class SpringDemoTest2 {    /*    此时需要注入代理类:因为代理类进行增强操作     *///    @Resource(name = "accountService")    @Resource(name = "accountServiceProxy")    private AccountService accountService;    @Test    public void demo1(){        accountService.transfer("aaa", "bbb", 200d);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

3.基于AspectJ的XML声明式事务管理

在这种方式下Dao和Service的代码也没有改变,applicationContext3.xml如下: 

applicationContext3.xml

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66

测试类与1中相同,增强是动态织入的,所以此时注入的还是accountService。

4.基于注解的声明式事务管理

基于注解的方式需要在业务层上添加一个@Transactional的注解。 

如下: 
AccountServiceImp.java

package com.spring.demo4;import org.springframework.transaction.annotation.Propagation;import org.springframework.transaction.annotation.Transactional;/** * Created by zhuxinquan on 17-4-27. * propagation  :事务的传播行为 * isolation    :事务的隔离级别 * readOnly     :只读 * rollbackFor  :发生哪些异常回滚 */@Transactional(propagation = Propagation.REQUIRED)public class AccountServiceImp 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);        int i = 1 / 0;        accountDao.inMoney(in, money);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

此时需要在Spring配置文件中开启注解事务,打开事务驱动 

applicationContext4.xml

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

测试类与1中相同

以上所有的代码可在进行下载,需要注意的是这里使用到了log4j,所以需要log4j的资源文件,同时,数据库的配置也是使用了资源文件的方式,还有需要注意的就是在Spring的配置中,我们需要注意配置文件的约束信息,再起次就是要注意jar包的导入了。

转载于:https://my.oschina.net/xiaomianyang/blog/1931668

你可能感兴趣的文章
Sublimne text3配置python3和robot开发环境
查看>>
shiro实战系列(十四)之配置
查看>>
MySQL查询数据表中数据记录(包括多表查询)
查看>>
Android Studio目录结构浅析
查看>>
visio 2013 如何制作形状的剪切、联合、组合、拆分、相交、剪除功能
查看>>
从壹开始前后端分离【 .NET Core2.0 Api + Vue 2.0 + AOP + 分布式】框架之一 || 前言...
查看>>
函数式编程与面向对象编程[3]:Scala的OOP-FP混合式编程与抽象代数理论
查看>>
bboss平台子系统切换方法
查看>>
Python全栈 项目(HTTPServer、PiP使用)
查看>>
隐私浏览器 Tor Browser 8.0.8 发布,安全更新版本
查看>>
12.2.0.1.0 Grid RU安装
查看>>
隐私浏览器 Tor Browser 8.0.7 发布,安全更新版本
查看>>
ATEC倒计时21天|大数据特色银行之贵阳银行
查看>>
使用树莓派进行24小时视频直播
查看>>
socket网络编程
查看>>
储能型数据中心对UPS电池的发展要求
查看>>
《Kotlin极简教程》第2章 快速开始:HelloWorld
查看>>
jedisPool使用遇到的bug
查看>>
精通SpringBoot——第八篇:整合RabbitMQ消息队列
查看>>
Linux系统优化
查看>>