Java-SpringAop 编程式事物实现

SpringAop 编程式事物实现

1. 数据库事物特性
  • 原子性

多个数据库操作是不可分割的,只有所有的操作都执行成功,事物才能被提交;只要有一个操作执行失败,那么所有的操作都要回滚,数据库状态必须回复到操作之前的状态

  • 一致性

事物操作成功后,数据库的状态和业务规则必须一致。例如:从A账户转账100元到B账户,无论数据库操作成功失败,A和B两个账户的存款总额是不变的。

  • 隔离性

当并发操作时,不同的数据库事物之间不会相互干扰(当然这个事物隔离级别也是有关系的)

  • 持久性

事物提交成功之后,事物中的所有数据都必须持久化到数据库中。即使事物提交之后数据库立刻崩溃,也需要保证数据能能够被恢复。

2.事物隔离级别

当数据库并发操作时,可能会引起脏读、不可重复读、幻读、第一类丢失更新、第二类更新丢失等现象。

  • 脏读

事物A读取事物B尚未提交的更改数据,并做了修改;此时如果事物B回滚,那么事物A读取到的数据是无效的,此时就发生了脏读。

  • 不可重复读

一个事务执行相同的查询两次或两次以上,每次都得到不同的数据。如:A事物下查询账户余额,此时恰巧B事物给账户里转账100元,A事物再次查询账户余额,那么A事物的两次查询结果是不一致的。

  • 幻读

A事物读取B事物提交的新增数据,此时A事物将出现幻读现象。幻读与不可重复读容易混淆,如何区分呢?幻读是读取到了其他事物提交的新数据,不可重复读是读取到了已经提交事物的更改数据(修改或删除)

  • 第一类丢失更新

A事物的回滚覆盖了B事物已经提交的数据。如:账户有1000元,A事物执行取款100元操作,但未提交事物;此时B事物向账户存入100元并提交事物,账户余额改为1100元。此时A事物回滚了取款操作,账户余额被恢复成了1000元。

  • 第二类更新丢失

A事物的提交覆盖了B事物已经提交的数据。如:账户有1000元,A事物操作向账户存入100元,但未提交事物;此时B事物从账户取出100元并提交事物,账户余额改为900元;此时A事物提交事物,账户余额变为1100元。

对于以上问题,可以有多个解决方案,设置数据库事物隔离级别就是其中的一种,数据库事物隔离级别分为四个等级,通过一个表格描述其作用。

隔离级别脏读不可重复读幻象读第一类丢失更新第二类丢失更新
READ UNCOMMITTED允许允许允许允许允许
READ COMMITTED脏读允许允许允许允许
REPEATABLE READ不允许不允许允许不允许不允许
SERIALIZABLE不允许不允许不允许不允许不允许
3.Spring事物支持核心接口

在这里插入图片描述

TransactionDefinition–>定义与spring兼容的事务属性的接口

public interface TransactionDefinition {
    // 如果当前没有事物,则新建一个事物;如果已经存在一个事物,则加入到这个事物中。
    int PROPAGATION_REQUIRED = 0;
    // 支持当前事物,如果当前没有事物,则以非事物方式执行。
    int PROPAGATION_SUPPORTS = 1;
    // 使用当前事物,如果当前没有事物,则抛出异常。
    int PROPAGATION_MANDATORY = 2;
    // 新建事物,如果当前已经存在事物,则挂起当前事物。
    int PROPAGATION_REQUIRES_NEW = 3;
    // 以非事物方式执行,如果当前存在事物,则挂起当前事物。
    int PROPAGATION_NOT_SUPPORTED = 4;
    // 以非事物方式执行,如果当前存在事物,则抛出异常。
    int PROPAGATION_NEVER = 5;
    // 如果当前存在事物,则在嵌套事物内执行;如果当前没有事物,则与PROPAGATION_REQUIRED传播特性相同
    int PROPAGATION_NESTED = 6;
    // 使用后端数据库默认的隔离级别。
    int ISOLATION_DEFAULT = -1;
    // READ_UNCOMMITTED 隔离级别
    int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;
    // READ_COMMITTED 隔离级别
    int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;
    // REPEATABLE_READ 隔离级别
    int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;
    // SERIALIZABLE 隔离级别
    int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;
    // 默认超时时间
    int TIMEOUT_DEFAULT = -1;
    // 获取事物传播特性
    int getPropagationBehavior();
    // 获取事物隔离级别
    int getIsolationLevel();
    // 获取事物超时时间
    int getTimeout();
    // 判断事物是否可读
    boolean isReadOnly();
    // 获取事物名称
    @Nullable
    String getName();
}

  1. Spring事物传播特性表:
传播特性名称说明
PROPAGATION_REQUIRED如果当前没有事物,则新建一个事物;如果已经存在一个事物,则加入到这个事物中
PROPAGATION_SUPPORTS支持当前事物,如果当前没有事物,则以非事物方式执行
PROPAGATION_MANDATORY使用当前事物,如果当前没有事物,则抛出异常
PROPAGATION_REQUIRES_NEW新建事物,如果当前已经存在事物,则挂起当前事物
PROPAGATION_NOT_SUPPORTED以非事物方式执行,如果当前存在事物,则挂起当前事物
PROPAGATION_NEVER以非事物方式执行,如果当前存在事物,则抛出异常
PROPAGATION_NESTED如果当前存在事物,则在嵌套事物内执行;如果当前没有事物,则与PROPAGATION_REQUIRED传播特性相同
  1. Spring事物隔离级别表:
隔离级别脏读不可重复读幻象读第一类丢失更新第二类丢失更新
ISOLATION_DEFAULT同后端数据库同后端数据库同后端数据库同后端数据库同后端数据库
ISOLATION_READ_UNCOMMITTED允许允许允许允许允许
ISOLATION_READ_COMMITTED脏读允许允许允许允许
ISOLATION_REPEATABLE_READ不允许不允许允许不允许不允许
ISOLATION_SERIALIZABLE不允许不允许不允许不允许不允许
  • PlatformTransactionManager–>Spring事务基础结构中的中心接口
public interface PlatformTransactionManager {
    // 根据指定的传播行为,返回当前活动的事务或创建新事务。
    TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
    // 就给定事务的状态提交给定事务。
    void commit(TransactionStatus status) throws TransactionException;
    // 执行给定事务的回滚。
    void rollback(TransactionStatus status) throws TransactionException;
}

Spring将事物管理委托给底层的持久化框架来完成,因此,Spring为不同的持久化框架提供了不同的PlatformTransactionManager接口实现。列举几个Spring自带的事物管理器:

事物管理器说明
org.springframework.jdbc.datasource.DataSourceTransactionManager提供对单个javax.sql.DataSource事务管理,用于Spring JDBC抽象框架、iBATIS或MyBatis框架的事务管理
org.springframework.orm.jpa.JpaTransactionManager提供对单个javax.persistence.EntityManagerFactory事务支持,用于集成JPA实现框架时的事务管理
org.springframework.transaction.jta.JtaTransactionManager提供对分布式事务管理的支持,并将事务管理委托给Java EE应用服务器事务管理器
  • TransactionStatus–>事物状态描述
  1. TransactionStatus接口
public interface TransactionStatus extends SavepointManager, Flushable {
    // 返回当前事务是否为新事务(否则将参与到现有事务中,或者可能一开始就不在实际事务中运行)
    boolean isNewTransaction();
    // 返回该事务是否在内部携带保存点,也就是说,已经创建为基于保存点的嵌套事务。
    boolean hasSavepoint();
    // 设置事务仅回滚。
    void setRollbackOnly();
    // 返回事务是否已标记为仅回滚
    boolean isRollbackOnly();
    // 将会话刷新到数据存储区
    @Override
    void flush();
    // 返回事物是否已经完成,无论提交或者回滚。
    boolean isCompleted();
}

  1. SavepointManager接口
public interface SavepointManager {
    // 创建一个新的保存点。
    Object createSavepoint() throws TransactionException;
    // 回滚到给定的保存点。
    // 注意:调用此方法回滚到给定的保存点之后,不会自动释放保存点,
    // 可以通过调用releaseSavepoint方法释放保存点。
    void rollbackToSavepoint(Object savepoint) throws TransactionException;
    // 显式释放给定的保存点。(大多数事务管理器将在事务完成时自动释放保存点)
    void releaseSavepoint(Object savepoint) throws TransactionException;
}
  1. Flushable接口
public interface Flushable {
    // 将会话刷新到数据存储区
    void flush() throws IOException;
}

4.Spring编程式事物

CREATE TABLE `account` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
  `balance` int(11) DEFAULT NULL COMMENT '账户余额',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COMMENT='--账户表'

实现

package com.lyc.cn.v2.day08;

import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.sql.DataSource;

/**
 * Spring编程式事物
 * @author: LiYanChao
 * @create: 2018-11-09 11:41
 */
public class MyTransaction {

    private JdbcTemplate jdbcTemplate;
    private DataSourceTransactionManager txManager;
    private DefaultTransactionDefinition txDefinition;
    private String insert_sql = "insert into account (balance) values ('100')";

    public void save() {

        // 1、初始化jdbcTemplate
        DataSource dataSource = getDataSource();
        jdbcTemplate = new JdbcTemplate(dataSource);

        // 2、创建物管理器
        txManager = new DataSourceTransactionManager();
        txManager.setDataSource(dataSource);

        // 3、定义事物属性
        txDefinition = new DefaultTransactionDefinition();
        txDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

        // 3、开启事物
        TransactionStatus txStatus = txManager.getTransaction(txDefinition);

        // 4、执行业务逻辑
        try {
            jdbcTemplate.execute(insert_sql);
            //int i = 1/0;
            jdbcTemplate.execute(insert_sql);
            txManager.commit(txStatus);
        } catch (DataAccessException e) {
            txManager.rollback(txStatus);
            e.printStackTrace();
        }

    }

    public DataSource getDataSource() {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/my_test?useSSL=false&useUnicode=true&characterEncoding=UTF-8");
        dataSource.setUsername("root");
        dataSource.setPassword("liyanchao1989@");
        return dataSource;
    }

}

  • 增加Gradle模块和包
// 引入spring-jdbc模块
optional(project(":spring-jdbc"))// https://mvnrepository.com/artifact/commons-dbcp/commons-dbcp
compile group: 'commons-dbcp', name: 'commons-dbcp', version: '1.4'// https://mvnrepository.com/artifact/mysql/mysql-connector-java
compile group: 'mysql', name: 'mysql-connector-java', version: '5.1.38'
  • 测试类及结果
package com.lyc.cn.v2.day08;

import org.junit.Test;

/**
 * @author: LiYanChao
 * @create: 2018-11-07 18:45
 */
public class MyTest {
    @Test
    public void test1() {
        MyTransaction myTransaction = new MyTransaction();
        myTransaction.save();
    }
}

运行测试类,在抛出异常之后手动回滚事物,所以数据库表中不会增加记录。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/470029.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

力扣1. 两数之和

思路:用一个map存放 已遍历过的元素和下标; 若当前元素是nums[i], 且该元素的另一半 target-nums[i] 在已遍历过的map里面,则返回两个元素的下标; class Solution {public int[] twoSum(int[] nums, int target) {int[] ans new…

C++ 离散与组合数学之多重集合

1. 前言 数论是计算机学科的基础,将以一系列文章讨论组合数学中的一些概念,包括多重集合、等价类、多重集上的排列、错排列、圆排列、鸽巢原理、二项式定理、容斥原理、卡特兰数。 本文主要是讨论集合以及多重集合的概念以及多重集合上的排列问题。集合…

开发微信小程序被鹅厂背刺

最近在开发微信小程序,没来得及更文。等开发完成后,给大家写保姆帖系列。刚刚看到一张动图,忍不住分享给大家。属实反映了鹅厂风格了。

文件上传基础篇

文件上传基础篇 文件上传漏洞原理 ​ 目标网站存在文件上传接口,但是对用户上传的文件没有做仔细甄别,导致黑客可以根据此功能点直接上传木马到网站服务器,造成危害 文件上传存在点 ​ 通常有头像上传,pdf上传 文件上传防护 …

Word为图表设置图注并在图表清单中自动生成

1如果需要自动插入题注,请不要自己为文件增加新的标题样式或删除自带的标题1样式 2章节大标题最好是标题1,2,3而不要设置标题一、二、三,否则图例在自动生成时会显示 图一 -1,调整起来会非常不方便 若实在要使用大写中文标题&…

【隐私计算实训营-001数据可信流通,从运维信任到技术信任】

1. 数据可信流通体系 信任的基石: 身份的可确认利益可依赖能力有预期行为有后果 2.内循环——>外循环 内循环:数据持有方在自己的运维安全域内队自己的数据使用和安全拥有全责。 外循环:数据要素在离开持有方安全域后,持有方…

FX-数组的使用

1一维数组 1.1一维数组的创建和初始化 1.1.1数组的创建 //代码1 int arr1[10]; char arr2[10]; float arr3[1]; double arr4[20]; //代码2 //用宏定义的方式 #define X 3 int arr5[X]; //代码3 //错误使用 int count 10; int arr6[count];//数组时候可以正常创建&#xff1…

【Linux】进程排队的理解进程状态的表述僵尸进程和孤儿进程的理解

一、进程排队的理解 进程不是一直运行的,进程可能会在等待某种软硬件资源。即使把进程加载到CPU中,也不是一直会运行的。而进程排队,一定是在等待某种软硬件资源(可以是CPU,键盘,磁盘,网卡等等设…

Android Studio实现内容丰富的安卓图书馆座位图书预约系统

获取源码请点击文章末尾QQ名片联系,源码不免费,尊重创作,尊重劳动 项目编号109 1.开发环境android stuido jdk1.8 eclipse mysql tomcat 2.功能介绍 安卓端: 1.注册登录 2.查看公告 3.查看图书馆座位 4.查看图书馆图书&#xff0c…

k8s详细教程

Kubernetes详细教程 1. Kubernetes介绍 1.1 应用部署方式演变 在部署应用程序的方式上,主要经历了三个时代: 传统部署:互联网早期,会直接将应用程序部署在物理机上 优点:简单,不需要其它技术的参与 缺点…

《1w实盘and大盘基金预测 day7》

昨日预测有点差劲,最低点也相差五个点。 打分C 公众号:JavaHelmet 昨天预测: 3052-3062-3076-3115 3067是趋势线,有回踩需求 5-30-60分钟级别顶钝 大盘冲到标红的点位3115或者3100就需注意。不要随意追高(最高309…

第四百一十一回

文章目录 1. 概念介绍2. 思路与方法2.1 实现思路2.2 实现方法 3. 示例代码4. 内容总结 我们在上一章回中介绍了"给geolocator插件提交问题的结果"相关的内容,本章回中将介绍自定义标题栏.闲话休提,让我们一起Talk Flutter吧。 1. 概念介绍 我…

蓝桥杯备赛_python_DFS搜索算法_刷题学习笔记

1.是什么 沿着一条路径一直搜索下去,在无法搜索时,回退到刚刚访问过的节点。并且每个节点只能访问一次。本质上是持续搜索,遍历了所有可能的情况,必然能得到解。 流程是一个树的形式,每次一条路走到黑。 目的主要是达到…

哈尔滨工业大学 《材料物理》 笔记-3

原内容请参考哈尔滨工业大学何飞教授:https://www.bilibili.com/video/BV18b4y1Y7wd/?p12&spm_id_frompageDriver&vd_source61654d4a6e8d7941436149dd99026962 或《材料物理性能及其在材料研究中的应用》(哈尔滨工业大学出版社) 量…

杨氏矩阵的查找(复杂度<O(N))

题目: 解释:时间复杂度小于O(N)即不要一个一个的去遍历查找。 思路: 一个33的二维数组如图所示: 一:先找到一个最关键的数字,3(下标为0,2) 关键数的关键之处在于(处于…

【Unity】从0到1的横版2d制作笔记-DAY1

写在前面: 感谢旻子提供的Unity2d课程捏,红豆泥阿里嘎多 创建项目 测试Visual Studio的使用 右键选择【create】,右键创建C# Script,待文件创建完毕后双击查看能否正确跳转。 正确跳转的结果是能看见代码中注释标注有:…

15届蓝桥杯第二期模拟赛所有题目解析

文章目录 🧡🧡t1_求余🧡🧡思路代码 🧡🧡t2_灌水🧡🧡思路代码 🧡🧡t3_字符显示🧡🧡思路代码 🧡🧡t4_区间最大和…

(56)删除每行中的最大值

文章目录 1. 每日一言2. 题目3. 解题思路4. 代码5. 结语 1. 每日一言 抱怨过去发生的一切,就等于丧失了力量,白白浪费了往事要带给我们的成长。 2. 题目 题目链接:删除每行中的最大值 给你一个 m x n 大小的矩阵 grid ,由若干正…

线程工具类与原子类

参考文档: CountDownLatch、CyclicBarrier、Semaphore的用法和区别juc15_基本AtomicInteger、数组、引用AtomicStampedReference、对象的属性修改原子类 AtomicIntegerFieldUp 、原子操作增强类LongAdder 辅助工具类 CountDownLatch(闭锁) 做减法 允许一个或多个…

c语言文件操作(2)

1.文件的随机读写: fseek函数 int fseek ( FILE * stream, long int offset, int origin ); fseek函数是根据文件指针的位置和偏移量来定位文件指针。 其中,origin所含的参数: seek_set:文件开头 seek_cur:文件指…