文档章节

数据库开发 - 事务 事务管理与开发

抢小孩糖吃
 抢小孩糖吃
发布于 2016/10/07 20:19
字数 2298
阅读 47
收藏 1
点赞 0
评论 0

#事务原理与开发

##常见业务场景 ###银行转账 如下图所示,张三给李四转账100元钱。
输入图片说明

##原子性 在银行转账的实例中,如果张三的账户增加了100元钱,李四账户没有增加100元钱。张三的账面价值发生问题。如果张三没有扣除100元钱,而李四增加了100元钱,则银行账面发生损失。所以我们需要把张三扣除100元钱与李四增加100元钱作为整体进行。要么全部执行,要么一个都不执行,不可分割。

原子性:整个交易必须是一个整体,要么全做,要不都不做!
输入图片说明

##一致性 在交易前和交易后,钱的总量是不变得。交易前,张三有100元钱,李四没有钱,在交易后,张三把100块钱给了李四,张三没有钱,李四有100元钱。整个交易前后,钱的总量没有发生变化。这就是整个交易的第二个特性一致性。

输入图片说明

##隔离性 在张三和李四交易的同时,又发生了赵五给张三转账200元,两个交易如果是并发的在执行。首先把张三和李四的交易称为交易一,张三和赵五的交易称之为交易二。首先交易一银行会读取张三的账户余额为100元钱,同时在发生交易二读取张三的账户余额为100元钱。此时交易一,因为给李四转账100元钱,张三账户被扣除100元钱,系统更新张三的账户余额为0元。此时,交易二,因为赵五给张三转账200元,由于之前读取了张三账户余额100元钱,则100+200=300,如果银行系统此时更新张三账户为300元钱。,则银行发生亏本。我们想要达到的目标是张三的账户最后余额为200元钱。由于两个交易是并发执行,没有进行隔离导致,银行系统错误,这就是我们要讲的第三个交易的特性隔离性。

隔离性:两个交易同时发生时,在一个交易执行之前,不能收到另外一个交易的影响。

输入图片说明

##持久性 由于银行设备系统故障,交易发生失效。也是没有办法接受的。交易的第四个特性持久性。

持久性:整个交易过程一旦结束,无论出现任何情况,交易都应当是永久生效的。

输入图片说明

##什么是事务 事务(Transaction)是并发控制的基本单位,指作为单个逻辑工作单元执行的一系列操作,而这些逻辑工作单元需要满足ACID特性。

  • 原子性:atomicity
  • 一致性:consistency
  • 隔离性:isolation
  • 持久性:durability

如果一个业务场景具有ACID特性,那我们就可以使用事务进行实现

##JDBC事务控制 JDBC的Connection对象提供了3种方法,可以帮助我们实现事务的逻辑。

  • Connection
    • .setAutoCommit() 开启事务
      首先我们通过setAutoCommit()方法,设置为false,之后的Connection对象后续执行的所有SQL语句,都将会作为JDBC的一个事务来进行处理。如果设置为true则,Connection对象后续的SQL语句作为一个单独的语句进行执行,都是以非事务的形式来执行的。这里需要注意的是,默认情况都是以非事务的形式进行执行,除非主动开启,将setAutoCommit()设置为false开启事务模式之后,Connection对象后续的SQL都将作为一个事务来执行,直到我们调用Connection对象的commit()方法
    • .commit() 提交事务
      commit()方法表示该事务被提交,也就是说事务结束,整个执行的SQL语句,都将会生效。
    • .rollback() 回滚事务
      当然,当我们执行事务过程中出现问题,需要回滚这个事务的时候,我们可以调用 .rollback() 回滚事务,回滚:虽然执行了这个事务的语句,但是我们可以回滚到事务开始之前的状态。

##实例演示JDBC事务回滚的控制 ###错误示例 在endpoint后面添加断点,这段代码不满足事务特性,存在中间状态。存在张三是0元,李四也是0元的,因此逻辑实现是不可接受的。我们的目标是要将其作为一个整体。

            connection = basicDataSource.getConnection();
            String sql = "UPDATE student_transaction SET account = ? WHERE name = ?";

            preparedStatement = connection.prepareStatement(sql);


            preparedStatement.setInt(1,0);
            preparedStatement.setString(2,"ZhangSan");
            preparedStatement.execute();

            // endpoint

            preparedStatement.setInt(1,100);
            preparedStatement.setString(2,"LiSi");
            preparedStatement.execute();

###事务示例 如下代码的例子我们可以看到,虽然我们执行到了endpoint处,但是查询数据库张三的账号还是100元。我们更新的这条SQL语句没有生效。全部执行后,提交了SQL数据内容。一次性张三减少成0元。李四增加100元。

    public void transferAccount() throws ClassNotFoundException {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        try {
            connection = basicDataSource.getConnection();
            //开启事务
            connection.setAutoCommit(false);
            String sql = "UPDATE student_transaction SET account = ? WHERE name = ?";

            preparedStatement = connection.prepareStatement(sql);

            preparedStatement.setInt(1,0);
            preparedStatement.setString(2,"ZhangSan");
            preparedStatement.execute();

            // endpoint

            preparedStatement.setInt(1,100);
            preparedStatement.setString(2,"LiSi");
            preparedStatement.execute();

            //提交事务
            connection.commit();
        } catch (SQLException e) {
            // ignore
            System.out.println("[SQLException]:" + e.toString());
            //如果发生异常则回滚事务
            if(connection != null)
                try {
                    connection.rollback();
                } catch (SQLException e1) {
                    e1.printStackTrace();
                }
        } finally {
                if (preparedStatement != null)
                {
                    try {
                        preparedStatement.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }

            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }

##检查点 JDBC提供了断点处理和控制的功能。在交易过程中,我们可以将其在执行到某个点的时候,保存该断点,在后续的处理过程中一旦出错(抛出异常)可以从该断点处执行其他的语句。整个过程还是作为一个完整的事务运行,可以对该断点执行Commit操作,来保证业务逻辑还是按照事务完整执行。
输入图片说明

##检查点实例 首先执行张三修改为0元,保存断点。之后执行李四修改100元,检查数据库,并没有发生数据变更。
输入图片说明
之后由于抛出异常,进行异常处理,首先回滚到断点位置,之后赵五添加100元,之后提交事务。
输入图片说明

    public void transferAccount() throws ClassNotFoundException {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        Savepoint savepoint = null;
        try {
            connection = basicDataSource.getConnection();
            //开启事务
            connection.setAutoCommit(false);
            String sql = "UPDATE student_transaction SET account = ? WHERE name = ?";

            preparedStatement = connection.prepareStatement(sql);

            preparedStatement.setInt(1,0);
            preparedStatement.setString(2,"ZhangSan");
            preparedStatement.execute();

            //保存检查点
            savepoint = connection.setSavepoint();

            preparedStatement.setInt(1,100);
            preparedStatement.setString(2,"LiSi");
            preparedStatement.execute();

            throw new SQLException();
//            //提交事务
//            connection.commit();

        } catch (SQLException e) {
            // ignore
            System.out.println("[SQLException]:" + e.toString());
            //如果发生异常则回滚事务
            if(connection != null)
                try {
                    //发生异常回滚到之前的断点
                    connection.rollback(savepoint);
                    //执行之后的业务逻辑
                    preparedStatement.setInt(1,100);
                    preparedStatement.setString(2,"ZhaoWu");
                    preparedStatement.execute();
                    connection.commit();

                } catch (SQLException e1) {
                    //如果出现异常直接回滚到事务之前
                    e1.printStackTrace();
                }
        } finally {
                if (preparedStatement != null)
                {
                    try {
                        preparedStatement.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }

            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }

##事务并发执行 涉及隔离性的案例 两个并发任务,任务一张三转账100元给李四,任务二张三寸银行200元钱。

##脏读 如果读取张三账户余额100元钱,更新张三账户余额为0元。此时,张三如果在银行又进行了存200元的交易。同时,读取张三账户余额0元,然后更新张三账户为200元,如果此时任务一,张三给李四转账交易失败,抛出异常,交易发生了回滚,这是张三应当回滚到交易前的状态。但是由于交易中0元状态,已经任务二,张三存钱200元,被其他事务已经读取,对其他事务产生影响。最后本来应当有300元钱的张三。最后却只有200元钱。任务一回滚失效。
脏读:读取一个事务未提交的更新。

输入图片说明

##不可重复读 假设事务一,张三读取两个张三的账户,事务二,向张三的账户余额存储了200元钱,事务第一次读取张三与第二次读取张三账户余额发生变化
不可重复读:同一个事务中,两次读取相同记录,结果不一致。 输入图片说明

##幻读

幻读:两次读取的结果包含的行记录不一样。

输入图片说明

##不可重复读与幻读的区别 幻读是行记录数不一致。不可重复读是指行记录的值不一样。

##事务隔离级别

  • 读未提交(read uncommitted)
    这个隔离级别,允许脏读现象发生,有可能会出现一个事务读取到另外一个事务没有被提交的修改。
  • 读提交(read committed)
    这个隔离级别不允许出现脏读,但可以允许不可重复读。多次读取同一行的值,有可能不一样。
  • 重复读(repeatable read)
    不允许出现不可重复读的,有可能会出现幻读。
  • 串行化(serializable)
    这个隔离级别连幻读都不允许发生。所有事务都是串行执行的,会导致数据库的性能急剧下降。

  • MySQL默认事务隔离级别为repeatable read
  • 事务隔离级别越高,数据库性能越差。开发的难度越低。

##设置隔离级别 输入图片说明

© 著作权归作者所有

共有 人打赏支持
抢小孩糖吃

抢小孩糖吃

粉丝 67
博文 200
码字总数 223760
作品 0
东城
程序员
Spring框架笔记(二十五)——强大的事务管理

事务管理是企业级应用程序开发中必不可少的技术, 用来确保数据的完整性和一致性. 事务就是一系列的动作, 它们被当做一个单独的工作单元. 这些动作要么全部完成, 要么全部不起作用 举个例子来...

HappyBKs ⋅ 2015/10/03 ⋅ 0

【step by step构建轻量级web框架】-何为轻量级web框架

本系列博文,将会一步一步介绍如何构建一个轻量级的web框架jbeer git地址:http://git.oschina.net/bieber/jbeer 在SSH/SSI充实着我们每个项目的开发过程中,我们所做的事情就是将他们一次组...

Bieber ⋅ 2014/06/14 ⋅ 4

Hasor 2.5.1 发布,支持 JFinal 整合

Hasor 2.5.1 正式发布了。Hasor 是一款基于 Java 语言的应用程序开发框架,它的核心设计目标是提供一个简单、且必要的环境给开发者。开发者可以在此基础上,通过 Hasor 强有力的粘合机制,构...

哈库纳 ⋅ 2016/11/08 ⋅ 3

Spring 事务管理高级应用难点剖析: 第 1 部分

Spring 最成功,最吸引人的地方莫过于轻量级的声明式事务管理,仅此一点,它就宣告了重量级 EJB 容器的覆灭。Spring 声明式事务管理将开发者从繁复的事务管理代码中解脱出来,专注于业务逻辑...

红薯 ⋅ 2010/03/28 ⋅ 2

Spring的事务管理难点剖析(1):DAO和事务管理的牵绊

有些人很少使用Spring而不使用Spring事务管理器的应用,因此常常有人会问:是否用了Spring,就一定要用Spring事务管理器,否则就无法进行数据的持久化操作呢?事务管理器和DAO是什么关系呢?...

icheer ⋅ 2012/07/17 ⋅ 0

Spring的事务管理机制

Spring对事务管理的支持 与EJB类似,Spring提供了对和事务管理的支持。但是,Spring对事务管理的能力远远超过EJB。这里就不详细介绍编码式事务和声明式事务的区别了。有兴趣的读者可以自行G...

小神神的大草原 ⋅ 2016/10/20 ⋅ 0

spring事务管理(Transaction)

事务概述 数据库事务(Transaction) 指作为单个逻辑工作单元执行的一系列操作。将一组相关操作组合为一个要么全部成功要么全部失败的单元,使应用程序更加可靠。 事务必须满足ACID: n原子性(...

学-无止境 ⋅ 2016/09/03 ⋅ 0

基于可靠消息方案的分布式事务(二):Java中的事务

前言:在上一篇文章 基于可靠消息方案的分布式事务:Lottor介绍 中介绍了常见的分布式事务的解决方案以及笔者基于可靠消息方案实现的分布式事务组件Lottor的原理,并展示了应用的控制台管理。...

aoho ⋅ 06/01 ⋅ 0

Spring之美-事务管理

一、概述 事务管理对于企业应用来说是至关重要的,即使出现异常情况,它也可以保证数据的一致性。 Spring Framework对事务管理提供了一致的抽象,其特点如下: 为不同的事务API提供一致的编程...

ws199358 ⋅ 2016/10/15 ⋅ 0

Spring 实践 -拾遗

标签: Java与设计模式 Junit集成 前面多次用到与,在测试类添加这两个注解,程序就会自动加载Spring配置并初始化Spring容器,方便Junit与Spring集成测试.使用这个功能需要在pom.xml中添加如下依...

hanqing280441589 ⋅ 2016/03/12 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

解决CentOS6、7,/etc/sysconfig/下没有iptables的问题

一、Centos 6版本解决办法: 1.任意运行一条iptables防火墙规则配置命令: iptables -P OUTPUT ACCEPT 2.对iptables服务进行保存: service iptables save 3.重启iptables服务: service ...

寰宇01 ⋅ 28分钟前 ⋅ 2

数据库备份和恢复

备份:mysqldump -u root -p 数据库>磁盘路径 恢复:mysql -u root -p 数据库<sql脚本的磁盘路径

anlve ⋅ 今天 ⋅ 0

发生了什么?Linus 又发怒了?

在一个 Linux 内核 4.18-rc1 的 Pull Request 中,开发者 Andy Shevchenko 表示其在对设备属性框架进行更新时,移除了 union 别名,这引发了 Linus 的暴怒。 这一次 Linus Torvalds 发怒的原...

问题终结者 ⋅ 今天 ⋅ 0

在树莓派上搭建一个maven仓库

在树莓派上搭建一个maven仓库 20180618 lambo init 项目说明 家里有台树莓派性能太慢。想搭建一个maven私服, 使用nexus或者 jfrog-artifactory 运行的够呛。怎么办呢,手写一个吧.所在这个...

林小宝 ⋅ 今天 ⋅ 0

Spring发展历程总结

转自与 https://www.cnblogs.com/RunForLove/p/4641672.html 目前很多公司的架构,从Struts2迁移到了SpringMVC。你有想过为什么不使用Servlet+JSP来构建Java web项目,而是采用SpringMVC呢?...

onedotdot ⋅ 今天 ⋅ 0

Python模块/包/库安装(6种方法)

Python模块/包/库安装(6种方法) 冰颖机器人 2016-11-29 21:33:26 一、方法1: 单文件模块 直接把文件拷贝到 $python_dir/Lib 二、方法2: 多文件模块,带setup.py 下载模块包(压缩文件zip...

cswangyx ⋅ 今天 ⋅ 0

零基础学习大数据人工智能,学习路线篇!系统规划大数据之路?

大数据处理技术怎么学习呢?首先我们要学习Python语言和Linux操作系统,这两个是学习大数据的基础,学习的顺序不分前后。 Python:Python 的排名从去年开始就借助人工智能持续上升,现在它已经...

董黎明 ⋅ 今天 ⋅ 0

openJdk和sun jdk的区别

使用过LINUX的人都应该知道,在大多数LINUX发行版本里,内置或者通过软件源安装JDK的话,都是安装的OpenJDK, 那么到底什么是OpenJDK,它与SUN JDK有什么关系和区别呢? 历史上的原因是,Ope...

jason_kiss ⋅ 今天 ⋅ 0

梳理

Redux 是 JavaScript 状态容器,提供可预测化的状态管理。 它是JS的状态容器,是一种解决问题的方式,所以即可以用于 react 也可以用于 vue。 需要理解其思想及实现方式。 应用中所有的 stat...

分秒 ⋅ 今天 ⋅ 0

Java 后台判断是否为ajax请求

/** * 是否是Ajax请求 * @param request * @return */public static boolean isAjax(ServletRequest request){return "XMLHttpRequest".equalsIgnoreCase(((HttpServletReques......

JavaSon712 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部