(十九)事务

目录


概念

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

例如: 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了 ;
posted @ 2018-06-12 21:32  Yiaz  阅读(102)  评论(0编辑  收藏  举报