(四)MySQL 事务

2020-07-20  本文已影响0人  小逸ing
一、介绍

1、概念

事务指的是逻辑上的一组操作,组成这组操作各个逻辑单元要么全都成功,要么全都失败。MySQL 事务主要用于处理操作量大,复杂度高的数据。比如说,在转账交易当中,甲转给乙100元,那么甲的账户要减少100,同时乙的账户增加100.

2、Mysql中的事务

3、事务的四大特点

原子性(Atomicity,或称不可分割性)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability)。

在 MySQL 命令行的默认设置下,事务都是自动提交的,即执行 SQL 语句后就会马上执行 COMMIT 操作。因此要显式地开启一个事务务须使用命令 BEGIN 或 START TRANSACTION,或者执行命令 SET AUTOCOMMIT=0,用来禁止使用当前会话的自动提交。

4、事务的隔离级别

Read Uncommitted(读取未提交内容)

在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。

Read Committed(读取提交内容)

这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别 也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。

Repeatable Read(可重读)

这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。

Serializable(可串行化)

这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。

5、不同隔离级别带来的问题

img
二、事务案例

1、数据准备

use mydb;
create table account(
    id int primary key auto_increment,
    name varchar(20),
    money double
);
insert into account values (null,'a',10000);
insert into account values (null,'b',10000);
insert into account values (null,'c',10000);

2、代码模拟转账

@Test
public void transferAccounts() {
    Connection conn = null;
    PreparedStatement pstmt = null;
    try {
        /**
         * 完成转账代码:
         * * 扣除某个账号的钱
         * * 给另外一个账号加钱
         */
        // 获得连接:
        conn = JDBCUtil.getConnection();
        // 编写SQL语句:
        String sql = "update account set money = money + ? where name = ?";
        // 预编译SQL:
        pstmt = conn.prepareStatement(sql);
        // 设置参数:
        // 用aaa账号给bbb账号转1000元
        pstmt.setDouble(1, -1000);
        pstmt.setString(2, "a");
        // 执行SQL:扣除aaa账号1000元
        pstmt.executeUpdate();

        //int i = 1 / 0;

        // 给bbb账号加1000
        pstmt.setDouble(1, 1000);
        pstmt.setString(2, "b");
        pstmt.executeUpdate();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        JDBCUtil.release(pstmt, conn);
    }
}

由于在转账的过程中出现了程序异常,出现a账号的钱被转丢了,但是b账号的钱没有任何变化,所以通过事务来控制

三、Mysql两种事务提交方式

SET AUTOCOMMIT=0 禁止自动提交

​ SET AUTOCOMMIT=1开启自动提交

四、使用代码实现事务
@Test
public void transferAccounts() {
    Connection conn = null;
    PreparedStatement pstmt = null;
    try{
        /**
         * 完成转账代码:
         * * 扣除某个账号的钱
         * * 给另外一个账号加钱
         */
        // 获得连接:
        conn = JDBCUtil.getConnection();
        // 开启事务
        conn.setAutoCommit(false);
        // 编写SQL语句:
        String sql = "update account set money = money + ? where name = ?";
        // 预编译SQL:
        pstmt = conn.prepareStatement(sql);
        // 设置参数:
        // 用aaa账号给bbb账号转1000元
        pstmt.setDouble(1, -1000);
        pstmt.setString(2, "aaa");
        // 执行SQL:扣除aaa账号1000元
        pstmt.executeUpdate();

        int i = 1 / 0;

        // 给bbb账号加1000
        pstmt.setDouble(1, 1000);
        pstmt.setString(2, "bbb");
        pstmt.executeUpdate();

        // 提交事务:
        conn.commit();
    }catch(Exception e){
        // 回滚事务:
        try {
            conn.rollback();
        } catch (SQLException e1) {
            e1.printStackTrace();
        }
        e.printStackTrace();
    } finally {
        JDBCUtil.release(pstmt, conn);
    }
}
上一篇 下一篇

猜你喜欢

热点阅读