一、数据库事务概述

1、基本介绍

事务必需满足ACID(原子性、一致性、隔离性和持久性)特性,缺一不可:

  • 原子性(Atomicity):即事务是不可分割的最小工作单元,事务内的操作要么全做,要么全不做;
  • 一致性(Consistency):在事务执行前数据库的数据处于正确的状态,而事务执行完成后数据库的数据还是处于正确的状态,即数据完整性约束没有被破坏;如银行转帐,A转帐给B,必须保证A的钱一定转给B,一定不会出现A的钱转了但B没收到,否则数据库的数据就处于不一致(不正确)的状态。
  • 隔离性(Isolation):并发事务执行之间无影响,在一个事务内部的操作对其他事务是不产生影响,这需要事务隔离级别来指定隔离性;
  • 持久性(Durability):事务一旦执行成功,它对数据库的数据的改变必须是永久的,不会因比如遇到系统故障或断电造成数据不一致或丢失。

在实际项目开发中数据库操作一般都是并发执行的,即有多个事务并发执行,并发执行就可能遇到问题,目前常见的问题如下:

  • 丢失更新:两个事务同时更新一行数据,最后一个事务的更新会覆盖掉第一个事务的更新,从而导致第一个事务更新的数据丢失,这是由于没有加锁造成的;
  • 脏读:一个事务看到了另一个事务未提交的更新数据;
  • 不可重复读:在同一事务中,多次读取同一数据却返回不同的结果;也就是有其他事务更改了这些数据;
  • 幻读:一个事务在执行过程中读取到了另一个事务已提交的插入数据;即在第一个事务开始时读取到一批数据,但此后另一个事务又插入了新数据并提交,此时第一个事务又读取这批数据但发现多了一条,即好像发生幻觉一样。

为了解决这些并发问题,需要通过数据库隔离级别来解决,在标准SQL规范中定义了四种隔离级别:

  • 未提交读(Read Uncommitted:最低隔离级别,一个事务能读取到别的事务未提交的更新数据,很不安全,可能出现丢失更新、脏读、不可重复读、幻读;
  • 提交读(Read Committed:一个事务能读取到别的事务提交的更新数据,不能看到未提交的更新数据,不可能可能出现丢失更新、脏读,但可能出现不可重复读、幻读;
  • 可重复读(Repeatable Read:保证同一事务中先后执行的多次查询将返回同一结果,不受其他事务影响,可能可能出现丢失更新、脏读、不可重复读,但可能出现幻读;
  • 序列化(Serializable:最高隔离级别,不允许事务并发执行,而必须串行化执行,最安全,不可能出现更新、脏读、不可重复读、幻读。

2、事务类型

数据库事务类型有本地事务和分布式事务:

  • 本地事务:就是普通事务,能保证单台数据库上的操作的ACID,被限定在一台数据库上;
  • 分布式事务:涉及两个或多个数据库源的事务,即跨越多台同类或异类数据库的事务(由每台数据库的本地事务组成的),分布式事务旨在保证这些本地事务的所有操作的ACID,使事务可以跨越多台数据库;

Java事务类型有JDBC事务和JTA事务:

  • JDBC事务:就是数据库事务类型中的本地事务,通过Connection对象的控制来管理事务;
  • JTA事务:JTA指Java事务API(Java Transaction API),是Java EE数据库事务规范, JTA只提供了事务管理接口,由应用程序服务器厂商(如WebSphere Application Server)提供实现,JTA事务比JDBC更强大,支持分布式事务。

二、事务管理器

1、Spring事务管理的核心是事务管理器抽象,由PlatformTransactionManager接口定义:

public interface PlatformTransactionManager {
// 根据TransactionDefinition, 返回一个已经激活的事务或创建一个新的事务
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
// 提交事务
void commit(TransactionStatus status) throws TransactionException;
// 回滚事务
void rollback(TransactionStatus status) throws TransactionException;
}

PlatformTransactionManager的两个内置实现:

  • DataSourceTransactionManager位于org.springframework.jdbc.datasource包中,数据源事务管理器,提供对单个javax.sql.DataSource事务管理,用于Spring JDBC抽象框架、MyBatis框架的事务管理;
  • JtaTransactionManager位于org.springframework.transaction.jta包中,提供对分布式事务管理的支持,并将事务管理委托给Java EE应用服务器事务管理器;

2、简单实例(编程式)

定义事务管理器

<bean id="mysql" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
...
</bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="mysql"/>
</bean>

测试

public class TransactionTest {

    private static ApplicationContext context;
private static PlatformTransactionManager txManager;
private static DataSource dataSource;
private static JdbcTemplate jdbcTemplate; @BeforeClass
public static void init() {
context = new ClassPathXmlApplicationContext("spring-context.xml");
txManager = context.getBean(PlatformTransactionManager.class);
dataSource = context.getBean(DataSource.class);
jdbcTemplate = new JdbcTemplate(dataSource);
} @Test
public void testPlatformTransactionManager() {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = txManager.getTransaction(def);
jdbcTemplate.update("INSERT INTO `user` VALUES(170, 'hello3', 96)");
//txManager.commit(status);
txManager.rollback(status);
}
}

3、事务属性介绍

事务属性通过TransactionDefinition接口实现定义,主要有事务隔离级别、事务传播行为、事务超时时间、事务是否只读

i)隔离级别

用于解决并发事务时出现的问题,由TransactionDefinition中的静态变量指定:

ISOLATION_DEFAULT:默认隔离级别,即使用底层数据库默认的隔离级别
ISOLATION_READ_UNCOMMITTED:未提交读
ISOLATION_READ_COMMITTED:提交读,一般情况下我们使用这个
ISOLATION_REPEATABLE_READ:可重复读
ISOLATION_SERIALIZABLE:序列化

ii)事务传播行为

事务传播行为用于指定在多个事务方法间调用时,事务是如何在这些方法间传播的,Spring共支持7种传播行为,由TransactionDefinition中的静态变量指定:

PROPAGATION_REQUIRED:如果当前存在一个逻辑事务,则加入该逻辑事务,否则将新建一个逻辑事务
PROPAGATION_REQUIRES_NEW:每次都创建新的逻辑事务
PROPAGATION_SUPPORTS:如果当前存在逻辑事务,就加入到该逻辑事务,否则就以非事务方式执行
PROPAGATION_NOT_SUPPORTED:如果当前存在逻辑事务,就把当前事务暂停,以非事务方式执行
PROPAGATION_MANDATORY:如果当前存在逻辑事务,使用当前事务执行,否则抛出异常
PROPAGATION_NEVER:不支持事务,如果当前存在事务则抛出异常
PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行,如果当前不存在事务,则创建一个新的事务,嵌套事务使用数据库中的保存点来实现,即嵌套事务回滚不影响外部事务,但外部事务回滚将导致嵌套事务回滚

iii)事务超时时间

设置事务的超时时间,单位为秒,默认为-1表示使用底层事务的超时时间;事务超时后会抛出异常,并导致自动回滚

new DefaultTransactionDefinition().setTimeout(..); // 设置单个事务
DataSourceTransactionManager. setDefaultTimeout(..); // 设置全局

iv)只读

new DefaultTransactionDefinition().setReadOnly(true); 

三、基于XML的事务声明

1、简单实例

业务接口及实现

public interface UserService {
void addUser(String name, int age);
void updateUserName(String name);
}
public class UserServiceImpl implements UserService {

    private DataSource dataSource;

    public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
} @Override
public void addUser(String name, int age) {
String sql = String.format("INSERT INTO `user`(user_name, age) VALUES('%s', %d)", name, age);
new JdbcTemplate(dataSource).update(sql); this.updateUserName(name + "_" + name);
} @Override
public void updateUserName(String name) {
String sql = String.format("UPDATE `user` SET user_name = '%s'", name);
new JdbcTemplate(dataSource).update(sql); throw new RuntimeException("9965");
}
}

XML配置

<?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: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-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd"> <!-- 数据源 -->
<bean id="mysql" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
...
</bean> <!-- 业务类-->
<bean id="userService" class="cn.matt.transaction.UserServiceImpl">
<property name="dataSource" ref="mysql"/>
</bean> <!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="mysql"/>
</bean> <!-- advice -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED" isolation="READ_COMMITTED"/>
<tx:method name="*" propagation="REQUIRED" isolation="READ_COMMITTED" read-only="true"/>
</tx:attributes>
</tx:advice> <!-- aop -->
<aop:config>
<aop:pointcut id="serviceMethod" expression="execution(* cn.matt..*.*(..))"/>
<aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice"/>
</aop:config>
</beans>

测试

public class TransactionConfigTest {
@Test
public void testTransactionConfig() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
UserService userService = context.getBean(UserService.class);
userService.addUser("alanx", 30);
}
}

2、配置详解

<tx:advice id="..." transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="..."
propagation=" REQUIRED"
isolation="READ_COMMITTED"
timeout="-1"
read-only="false"
no-rollback-for=""
rollback-for=""/>
...
</tx:attributes>
</tx:advice>

详细说明:

  • <tx:advice>

    • id  指定通知的名字
    • transaction-manager  指定事务管理器,默认为“transactionManager”
  • <tx:method>
    • name      定义与事务属性相关联的方法名,可以使用"*"通配符匹配一组方法
    • propagation 定义事务传播行为,其值由TransactionDefinition的静态变量“PROPAGATION_”后边部分指定,默认为“REQUIRED”
    • isolation    定义事务隔离级别,其值可以通过TransactionDefinition的静态变量“ISOLATION_”后边部分指定,默认为“DEFAULT”
    • timeout      事务超时时间设置,单位为秒,默认-1,表示事务超时将依赖于底层事务系统
    • read-only   事务只读设置,默认为false,表示不是只读
    • rollback-for:定义触发回滚的异常,以“,”分割,默认任何RuntimeException 将导致事务回滚
    • no-rollback-for:不被触发进行回滚的 Exception(s),以“,”分割

 四、基于注解的事务声明

1、简单实例

业务接口及实现与XML方式的实例相同,不同之处是在实现方法上添加注解@Transactional

public interface UserService {
void addUser(String name, int age);
void updateUserName(String name);
}
public class UserServiceImpl implements UserService {

    private DataSource dataSource;

    public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
} @Transactional(propagation = Propagation.REQUIRED)
@Override
public void addUser(String name, int age) {
String sql = String.format("INSERT INTO `user`(user_name, age) VALUES('%s', %d)", name, age);
new JdbcTemplate(dataSource).update(sql); this.updateUserName(name + "_" + name);
} @Transactional(propagation = Propagation.REQUIRED)
@Override
public void updateUserName(String name) {
String sql = String.format("UPDATE `user` SET user_name = '%s'", name);
new JdbcTemplate(dataSource).update(sql); throw new RuntimeException("9965");
}
}

XML配置

<?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: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-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd"> <!-- 数据源 -->
<bean id="mysql" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
...
</bean> <!-- 业务类-->
<bean id="userService" class="cn.matt.transaction.UserServiceImpl">
<property name="dataSource" ref="mysql"/>
</bean> <!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="mysql"/>
</bean> <!-- 开启事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

测试类与XML相同

public class TransactionConfigTest {
@Test
public void testTransactionConfig() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
UserService userService = context.getBean(UserService.class);
userService.addUser("jerry", 30);
}
}

2、配置详解

<tx:annotation-driven/>用于开启对注解事务管理的支持,具有以下属性:

  • transaction-manager:指定事务管理器名字,默认为transactionManager
  • proxy-target-class:表示将使用的代码机制,默认false表示使用JDK代理,如果为true将使用CGLIB代理
  • order:定义事务通知顺序,默认Ordered.LOWEST_PRECEDENCE,表示将顺序决定权交给AOP来处理

@Transactional注解用于指定事务属性,具体属性如下:

  • value:指定事务管理器名字,默认使用<tx:annotation-driven/>指定的事务管理器,用于支持多事务管理器环境
  • propagation:指定事务传播行为,默认为Required,使用Propagation.REQUIRED指定
  • isolation:指定事务隔离级别,默认为“DEFAULT”,使用Isolation.DEFAULT指定
  • readOnly:指定事务是否只读,默认false表示事务非只读
  • timeout:指定事务超时时间,以秒为单位,默认-1表示事务超时将依赖于底层事务系统
  • rollbackFor:指定一组异常类,遇到该类异常将回滚事务
  • rollbackForClassname:指定一组异常类名字,其含义与<tx:method>中的rollback-for属性语义完全一样
  • noRollbackFor:指定一组异常类,即使遇到该类异常也将提交事务,即不回滚事务
  • noRollbackForClassname:指定一组异常类名字,其含义与<tx:method>中的no-rollback-for属性语义完全一样

3、注意事项

使用@Transactional注解事务管理需要特别注意以下几点:

  • 如果在接口、实现类或方法上都指定了@Transactional 注解,则优先级顺序为方法>实现类>接口
  • 建议只在实现类或实现类的方法上使用@Transactional,而不要在接口上使用,这是因为如果使用JDK代理机制是没问题,因为其使用基于接口的代理;而使用使用CGLIB代理机制时就会遇到问题,因为其使用基于类的代理而不是接口,这是因为接口上的@Transactional注解是“不能继承的”
  • 默认只对RuntimeException异常回滚
  • 在使用Spring代理时,默认只有在public可见度的方法的@Transactional 注解才是有效的,其它可见度(protected、private、包可见)的方法上即使有@Transactional 注解也不会应用这些事务属性的,Spring也不会报错,如果你非要使用非公共方法注解事务管理的话,可考虑使用AspectJ

参考:

第九章  Spring的事务 之 9.1 数据库事务概述 ——跟我学spring3

第九章  Spring的事务 之 9.2 事务管理器 ——跟我学spring3

第九章  Spring的事务 之 9.3 编程式事务 ——跟我学spring3

第九章  Spring的事务 之 9.4 声明式事务 ——跟我学spring3

javaweb学习总结(三十八)——事务

事务与隔离级别笔记

Spring 使用介绍(七)—— Spring事务的更多相关文章

  1. Spring Security 解析(七) —— Spring Security Oauth2 源码解析

    Spring Security 解析(七) -- Spring Security Oauth2 源码解析   在学习Spring Cloud 时,遇到了授权服务oauth 相关内容时,总是一知半解,因 ...

  2. 黑马_13 Spring Boot:01.spring boot 介绍&&02.spring boot 入门

    13 Spring Boot: 01.spring boot 介绍&&02.spring boot 入门 04.spring boot 配置文件 SpringBoot基础 1.1 原有 ...

  3. spring cloud学习(七)Spring Cloud Config(续)

    Spring Cloud Config(续) 个人参考项目 个人博客 : https://zggdczfr.cn/ 个人参考项目 : (整合到上一个案例中)https://github.com/Fun ...

  4. Spring框架系列(七)--Spring常用注解

    Spring部分: 1.声明bean的注解: @Component:组件,没有明确的角色 @Service:在业务逻辑层使用(service层) @Repository:在数据访问层使用(dao层) ...

  5. Spring Cloud 学习 (七) Spring Cloud Sleuth

    微服务架构是一个分布式架构,微服务系统按业务划分服务单元,一个微服务系统往往有很多个服务单元.由于服务单元数量众多,业务的复杂性较高,如果出现了错误和异常,很难去定位.主要体现在一个请求可能需要调用很 ...

  6. spring事务:事务控制方式,使用AOP控制事务,七种事务传播行为,声明事务,模板对象,模板对象原理分析

    知识点梳理 课堂讲义 1)事务回顾 1.1)什么是事务-视频01 事务可以看做是一次大的活动,它由不同的小活动组成,这些活动要么全部成功,要么全部失败. 1.2)事务的作用 事务特征(ACID) 原子 ...

  7. 黑马_13 Spring Boot:05.spring boot 整合其他技术

    13 Spring Boot: 01.spring boot 介绍&&02.spring boot 入门 04.spring boot 配置文件 05.spring boot 整合其他 ...

  8. 黑马_13 Spring Boot:04.spring boot 配置文件

    13 Spring Boot: 01.spring boot 介绍&&02.spring boot 入门 04.spring boot 配置文件 05.spring boot 整合其他 ...

  9. Spring五个事务隔离级别和七个事务传播行为

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt216 Spring五个事务隔离级别和七个事务传播行为 1. 脏读 :脏读就是 ...

  10. 手把手带你实战下Spring的七种事务传播行为

    目录 本文目录 一.什么是事务传播行为? 二.事务的7种传播行为 三.7种传播行为实战 本文介绍Spring的七种事务传播行为并通过代码演示下. 本文目录 一.什么是事务传播行为? 事务传播行为(pr ...

随机推荐

  1. Java使用Future设置方法超时

    1.使用线程包 java.util.concurrent.Future 2.Future代表一个异步计算的结果. 它提供了方法来检查是否计算已经完成,还是正在计算而处于等待状态,并且也提供了获取计算结 ...

  2. 史上最全面的Elasticsearch使用指南

    Elasticsearch使用指南 Elasticsearch使用指南 前言 ES是什么 什么是全文检索 ES的应用场景 ES的存储结构 第一章:安装 1.下载 2.解压 3.配置 4.启动 5.查看 ...

  3. HBase篇(2)-数据模型与操作

    HBase其实就是一个数据库,无非就是存储和增删改查,那我们先从数据模型说起把 这里有一张表,是用关系型数据库的思维画出来的表,这样比较易于理解: 概念 Table(表格) 没啥说的,和关系型数据库一 ...

  4. 【IE11请求中止】 XMLHttpRequest: 网络错误 0x2ef3的意外出现

    BUG现象 今天排查BUG遇到一个有趣的BUG,测试的截图显示 这个BUG只在IE11下出现. BUG原因 这个问题的原因是keep-alive机制引起. 当浏览器在向一个网址发起请求时,会建立一个t ...

  5. 【记一次pull request的惨痛教训】不可见的分隔符之Zero-with-space

    问题描述: 我在修改 ctf-wiki 目录后进行 mkdocs build 去生成索引目录的时候报错: 然后我尝试定位到第 2 行和第 288 行,这些行我似乎并没有修改过啊. 未果,开始去找师傅解 ...

  6. AtCoder Beginner Contest 053

    D - Card Eater Time limit : 2sec / Memory limit : 256MB Score : 400 points Problem Statement Snuke h ...

  7. Python学习第六篇——字典中的键和值

    favorite_language ={ "jen":"python", "sarah":"c", "edwa ...

  8. linux中根据名称kill进程

    shell函数如下: # kill processes by name kbn() { line=`ps -a | grep $1` arr=($line) for((i=0;i<${#arr[ ...

  9. 软件工程(FZU2015) 赛季得分榜,第五回合

    SE_FZU目录:1 2 3 4 5 6 7 8 9 10 11 12 13 积分规则 积分制: 作业为10分制,练习为3分制:alpha30分: 团队项目分=团队得分+个人贡献分 个人贡献分: 个人 ...

  10. 数组建 BST

    #include <bits/stdc++.h> using namespace std; const int maxn = 1e5 + 10; int N, root = 1; int ...