事务

一、事务
1.事务的概念:事务是指逻辑上的一组操作,这组操作要么同时完成要么同时不完成。参考转账操作。
2.使用命令去开启一个事务:
    start transaction;--开启事务,这条语句之后的sql语句将处在一个事务当中,这些sql语句并不会立即执行
    Commit--提交事务,一旦提交事务,事务中的所有sql语句才会执行。
    Rollback -- 回滚事务,将之前所有的sql取消。
  
    conn.setAutoCommit(false);
    conn.commit();
    conn.rollback();
    conn.setSavePoint();
    conn.rollback(sp);

3、事物的实现原理

  默认情况下,我们向数据库发送的sql语句是会被自动提交的,开启事务就是相当于关闭自动提交功能,改为手动提交,我们只需要将提交事务的操作放在最后一个操作,这样一来,如果在提交事务之前出现异常,由于没有执行提交操作,事务中未提交的操作就会被回滚掉

4、事务的四大特性ACID
    1、
原子性(Atomicity):事务中所有操作是不可再分割的原子单位。事务中所有操作要么全部执行成功,要么全部执行失败。

  2、一致性 (Consistency):事务执行后,数据库状态与其它业务规则保持一致。如转账业务,无论事务执行成功与否,参与转账的两个账号余额之和应该是不变的。

  3、隔离性(Isolation):隔离性是指在并发操作中,不同事务之间应该隔离开来,使每个并发中的事务不会相互干扰。

  4、持久性(Durability):一旦事务提交成功,事务中所有的数据操作都必须被持久化到数据库中,即使提交事务后,数据库马上崩溃,在数据库重启时,也必须能保证通过某种机制恢复数据。


 

事务代码体现

public class AccountDao {

    // 全局参数
    private Connection con;
    private PreparedStatement pstmt;

    // 1. 转账,没有使用事务
    public void trans1() {

        String sql_zs = "UPDATE account SET money=money-1000 WHERE accountName='张三';";
        String sql_ls = "UPDATE account SET money=money+1000 WHERE accountName='李四';";

        try {
            con = JdbcUtil.getConnection(); // 默认开启的隐士事务
            con.setAutoCommit(true);

            /*** 第一次执行SQL ***/
            pstmt = con.prepareStatement(sql_zs);
            pstmt.executeUpdate();

            /*** 第二次执行SQL ***/
            pstmt = con.prepareStatement(sql_ls);
            pstmt.executeUpdate();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.closeAll(null,con, pstmt );
        }

    }

    // 2. 转账,使用事务
    public void trans2() {

        String sql_zs = "UPDATE account SET money=money-1000 WHERE accountName='张三';";
        String sql_ls = "UPDATE1 account SET money=money+1000 WHERE accountName='李四';";

        try {
            con = JdbcUtil.getConnection(); // 默认开启的隐士事务
            // 一、设置事务为手动提交
            con.setAutoCommit(false);

            /*** 第一次执行SQL ***/
            pstmt = con.prepareStatement(sql_zs);
            pstmt.executeUpdate();

            /*** 第二次执行SQL ***/
            pstmt = con.prepareStatement(sql_ls);
            pstmt.executeUpdate();

        } catch (Exception e) {
            try {
                // 二、 出现异常,需要回滚事务
                con.rollback();
            } catch (SQLException e1) {
            }
            e.printStackTrace();
        } finally {
            try {
                // 三、所有的操作执行成功, 提交事务
                con.commit();
                JdbcUtil.closeAll( null,con, pstmt);
            } catch (SQLException e) {
            }
        }

    }

    // 3. 转账,使用事务, 回滚到指定的代码段
    public void trans() {
        // 定义个标记
        Savepoint sp = null;
        
        // 第一次转账
        String sql_zs1 = "UPDATE account SET money=money-1000 WHERE accountName='张三';";
        String sql_ls1 = "UPDATE account SET money=money+1000 WHERE accountName='李四';";
        
        // 第二次转账
        String sql_zs2 = "UPDATE account SET money=money-500 WHERE accountName='张三';";
        String sql_ls2 = "UPDATE1 account SET money=money+500 WHERE accountName='李四';";

        try {
            con = JdbcUtil.getConnection(); // 默认开启的隐士事务
            con.setAutoCommit(false);       // 设置事务手动提交

            /*** 第一次转账 ***/
            pstmt = con.prepareStatement(sql_zs1);
            pstmt.executeUpdate();
            pstmt = con.prepareStatement(sql_ls1);
            pstmt.executeUpdate();
            
            // 回滚到这个位置?
            sp = con.setSavepoint(); 
            
            
            /*** 第二次转账 ***/
            pstmt = con.prepareStatement(sql_zs2);
            pstmt.executeUpdate();
            pstmt = con.prepareStatement(sql_ls2);
            pstmt.executeUpdate();
            

        } catch (Exception e) {
            try {
                // 回滚 (回滚到指定的代码段)
                con.rollback(sp);
            } catch (SQLException e1) {
            }
            e.printStackTrace();
        } finally {
            try {
                // 提交
                con.commit();
            } catch (SQLException e) {
            }
            JdbcUtil.closeAll(null,con, pstmt );
        }

    }
}
=========================================
public class App {

    @Test
    public void testname() throws Exception {
        
        // 转账
        AccountDao accountDao = new AccountDao();
        accountDao.trans();
    }
}

 

5、事物的隔离级别

 

  1、事务的并发读问题

        1、脏读 :读取到另一个事务未提交数据;

    2、不可重复读:两次读取不一致;

    3、幻读(虚读):读到另一事务已提交数据。

  

 2、并发事务问题

  因为并发事务导致的问题大致有5类,其中两类是更新问题,三类是读问题。

   脏读(dirty read):读到另一个事务的未提交更新数据,即读取到了脏数据;

  不可重复读(unrepeatable read):对同一记录的两次读取不一致,因为另一事务对该记录做了修改;

  幻读(虚读)(phantom read):对同一张表的两次查询不一致,因为另一事务插入了一条记录;

 

脏读

事务1:张三给李四转账100元

事务2:李四查看自己的账户 

t1:事务1:开始事务

t2:事务1:张三给李四转账100元

t3:事务2:开始事务

t4:事务2:李四查看自己的账户,看到账户多出100元(脏读)

t5:事务2:提交事务

t6:事务1:回滚事务,回到转账之前的状态

 

不可重复读

事务1:酒店查看两次1048号房间状态

事务2:预订1048号房间

t1:事务1:开始事务

t2:事务1:查看1048号房间状态为空闲

t3:事务2:开始事务

t4:事务2:预定1048号房间

t5:事务2:提交事务

t6:事务1:再次查看1048号房间状态为使用

t7:事务1:提交事务

对同一记录的两次查询结果不一致!

 

幻读

事务1:对酒店房间预订记录两次统计

事务2:添加一条预订房间记录

t1:事务1:开始事务

t2:事务1:统计预订记录100条

t3:事务2:开始事务

t4:事务2:添加一条预订房间记录

t5:事务2:提交事务

t6:事务1:再次统计预订记录为101记录

t7:事务1:提交

  对同一表的两次查询不一致!

 

不可重复读和幻读的区别:

不可重复读是读取到了另一事务的更新;

幻读是读取到了另一事务的插入(MySQL中无法测试到幻读);

 

 

3、四大隔离级别

   4个等级的事务隔离级别,在相同数据环境下,使用相同的输入,执行相同的工作,根据不同的隔离级别,可以导致不同的结果。不同事务隔离级别能够解决的数据并发问题的能力是不同的。

    1 SERIALIZABLE(串行化)

          不会出现任何并发问题,因为它是对同一数据的访问是串行的,非并发访问的;

          性能最差;

 

    2 REPEATABLE READ (可重复读)(MySQL默认级别)

          防止脏读和不可重复读,不能处理幻读问题;

          性能比SERIALIZABLE好

 

    3 READ COMMITTED (读已提交数据)(Oracle默认级别)

          防止脏读,没有处理不可重复读,也没有处理幻读;

          性能比REPEATABLE READ好

    4 READ UNCOMMITTED(读未提交数据)

          可能出现任何事务并发问题

          性能最好

 

 

MySQL的默认隔离级别为Repeatable read,可以通过下面语句查看:

select @@tx_isolation

 

也可以通过下面语句来设置当前连接的隔离级别:

set transaction isolationlevel [41]

4、JDBC设置隔离级别

con. setTransactionIsolation(int level)

参数可选值如下:

Connection.TRANSACTION_READ_UNCOMMITTED;

Connection.TRANSACTION_READ_COMMITTED;

Connection.TRANSACTION_REPEATABLE_READ;

Connection.TRANSACTION_SERIALIZABLE。

 

事务总结:

事务的特性:ACID;

事务开始边界与结束边界:开始边界(con.setAutoCommit(false)),结束边界(con.commit()或con.rollback());

事务的隔离级别: READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ、SERIALIZABLE。多个事务并发执行时才需要考虑并发事务。

 


 

posted @ 2017-04-18 15:25  皮皮虾我们上  阅读(279)  评论(0编辑  收藏  举报