zoukankan      html  css  js  c++  java
  • (十九)事务

    目录


    概念

    事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功 ;

    例如: A——B转账,对应如下两条sql语句 ;

        update from account set money = money + 100 where name = 'b' ;  // 数据库中不支持 += 
    
        update from account set money = money - 100 where name = 'a' ;
    

    数据库默认事务是自动提交的,也就是发一条sql它就执行一条 ;

    这样显然不能满足我们的需求,我们需要拿 2sql 一起执行,要么全部成功,要么全部失败 ;因此,我们需要使用事务 ;


    命令行使用事务

    如果想多条sql放在一个事务中执行,则需要如下操作 :

    ------------------------------------------
    
        start transcation  开启事务
    
    
        。。。。
    
        需要将要一起执行的一组语句,放在中间 ;
    
        。。。。
    
        Commit   提交事务
    
    ------------------------------------------
    
    • 命令行操控事务命令

      start transcation  开启事务
      
      Rollback  回滚事务 ,对一次开启事务的操作,进行回滚,也就是手动回滚;
      
      Commit   提交事务
      
      
      对事务的操作,需要放在他们之间,只要数据库,没有收到 commit,他就认为事务执行失败,会进行操作的回滚,也就是回滚事务 ;
      
      。
      

    使用事务(JDBC)

    JDBC 程序向数据库获得一个 Connection 对象时,默认情况下,这个Connection连接是没有开启事务的 ,它会自动提交它上面的 sql 语句;

    若想关闭这种默认提交方式,让多条 SQL 在一个事务中执行,可以使用下列语句:

    
        // 相当于开启了事务: start transaction ;
        Connection.setAutoCommit(false) ;  
    
        // rollback ;
        Connection.rollback() ;  
    
        // commit ;
        Connection.commit() ;
    
    

    回滚事务点

    Statement.setSavePoint() ; 返回代表回滚点的对象 SavePoint ;

    我们可以控制回滚到哪一个事务点 rollback(sp) ,但是我们自己控制回滚一定要记得提交事务,否则数据库发现没有提交,自己就又会进行回滚事务 。


    代码实例

    
            Connection connection = null;
            PreparedStatement statement = null;
            ResultSet resultSet = null;
            Savepoint savepoint = null ;
    
            try {
                connection = JdbcUtils.getConnection();
    //            开启事务
                connection.setAutoCommit(false);
                String sql1 = "update employee set money = money - 100 where name = 'a' ";
                statement = connection.prepareStatement(sql1);
    //            执行sql语句
                statement.executeUpdate();
    
    //            設置回滚点
                savepoint = connection.setSavepoint() ;
    
                String sql2 = "update employee set money = money + 100 where name = 'b' ";
                statement = connection.prepareStatement(sql2);
    //            执行sql语句
                statement.executeUpdate();
    //            发生异常
                int num = 1/0 ;
    
    //            提交事务
                connection.commit();
    
    
            } catch (Exception e) {
                e.printStackTrace();
    
    //            发生异常,回滚到我们设置的事务回滚点
                connection.rollback(savepoint);
    
    //            这里必须再次进行 提交事务,否则数据库即使回滚到事务点了,但是没有提交事务,数据库就自己再次回滚了。。
                connection.commit();
    
    
            } finally {
                JdbcUtils.closedConnection(resultSet, statement, connection);
            }

    事务的四大特性(ACID)

    ·原子性 : 指事务是一个不可分割的工作单位,事务中的操作都发生,要么都不发生 ;
    
    ·一致性 : 指事务前后数据的完整性必须保持一致 ;
    
            例如:存钱的例子,必须保证事务前后,表中的总钱数没有变化!
    
    ·隔离性 : 事务的隔离性是指多个用户并发访问数据库时,一个用户的事务,不能被其他用户的事务干扰,多个并发事务之间的数据要相互隔离 ;
    
    ·持久性 : 指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下里即使数据库发生故障也不应该对其有任何影响 ;
    

    事务的隔离级别

    ·多个线程开启各自事务操作数据库的数据时,数据库系统要负责隔离操作,以保证各个线程在获取数据的时候的准确性 ;
    
    ·如果不考虑隔离性,可能会引发如下问题:
    
    
        ·脏读 :
    
            指一个事务读取了另外一个事务 ‘未提交’  的数据 ;
    
            这是非常危险的,假设A向B转账100元,对应的sql语句是:
    
    
            update from account set money = money + 100 where name = 'b' ;// 数据库中不支持 += 
    
            update from account set money = money - 100 where name = 'a' ;  
    
    
            当第一条SQL语句执行完,第二条还没执行(A未提交时),如果此时B查询自己的账户;
    
    
            就会发现自己多了100元钱。如果A等B走后,再回滚,B就会损失100元 ;
    
    
    
                ---帮助理解的例子:A在网上买了B的东西,需要付款100元;然后A开启一个事务,向B转100元,然后并不提交事务,就打电话给B ,问他钱收到没。B看下自己的账户收到钱了,就说收到了,然后发货;这时候A确定B发货了,就进行事务的回滚,这样转给B的100元,就直接回来了。。。A或成最大赢家 ;
    
    
    
        ·不可重复读: 
    
            在一个事务内,读取表的某一行数据,多次读取,结果不同 ;(指一个事务读取了另外一个事务 ‘已提交’  的数据 ;针对读表中的同一行数据)
    
            例如:在银行查询A账户余额,第一次查询A账户为200元,此时A向账户存了100元并提交了,此时A账户为300元,。银行2次查询不一致,可能会疑惑,不知道哪一次查询是准确的 ;
    
            ·和脏读的区别就是,脏读是读取前一个事务未提交的脏数据;不可重复读是重新读取了前一事务已提交的数据 ;
    
    
            ·很多人认为这种情况是对滴,没有什么疑惑,理所当然的以后面的为准;其实,我们应该考虑一个情况,比如银行程序需要查询结果分别输出到电脑屏幕和写到文件中,结果在一个事务中针对输出的目的地,进行了2次查询,导致文件个屏幕中的结果不一致,银行工作人员就不知道以哪个为准了 ;
    
    
    
                ---帮助理解的例子 : 银行年度报表,开启一个事务,读表,生成一份报表,给国务院送去,比如现在是1000万亿;然后这个事务没有提交,继续生成另一份报表给总理送去,但是这是,有人给银行转账100亿,并提交事务了;所有第二份报表多了100亿。这样两份报表的数据对不上了 ;
    
    
    
        ·虚读(幻读,针对的是一张表)
    
    
            ·是指在一个事务内读取到别的事务‘插入’的数据,导致前后读取不一致;
    
    
            ·如甲存款100元未提交,这时候银行报表做报表统计account 表中,所有用户的总额为500元;然后甲提交了;这时候在统计账户为600元,也会发现两个甲,造成虚读;
    

    事务隔离级别的设置语句(从高到低)

    ·Serializable : 可避免脏读、不可重复读、幻读的情况产生 ;(串行化)   跟加锁一样,只要有链接在操作这张表,其他连接就必须等着,所有性能最差 ;
    
    ·Repeatable read : 可避免脏读、不可重复读(但是可以幻读) ---  这个看运气,有时候可以幻读,有时候不可以 ;(mysql默认级别)
    
    ·Read commit : 可避免脏读 ;
    
    ·Read uncommitted : 最低级别,以上情况都不能保证 ;
    
    
    set transaction isolation level ;  // 设置事务隔离级别,随连接有效,关闭连接;
    
    select @@tx_isolation ;   // 查询当前事务的隔离级别 ;
    
    
    出现四种级别是因为,越往上级别,越消耗数据库的性能,因此给出四个级别,供程序员自己选择适合的级别 ;
    
    
    
    mysql数据库默认级别是: ·Repeatable read ;mysql是严格按照数据库规范设计的,有四个级别;
    
    oracle数据库默认级别是:·Read commit : 可避免脏读 ,oracle只有2个级别(1 和 3 );
    
    
    
    JDBC改变每次连接的隔离级别: connection.setTransactionLevel(int level) ;
    
    
    
    一般默认级别就OK了 ;
    
  • 相关阅读:
    SpringBoot+mybatis的驼峰命名转换不生效
    vue3 ts遇到的问题
    阿里巴巴的Java 工程脚手架
    Mybatis获取插入值的ID
    Bootstrap的Modal与WebUploader联用的问题及办法
    Flex布局专题
    23种设计模式
    排序算法-Java实现快速排序算法
    中间件面试专题:kafka高频面试问题
    中间件面试专题:RabbitMQ高频面试问题
  • 原文地址:https://www.cnblogs.com/young-youth/p/11665702.html
Copyright © 2011-2022 走看看