0
点赞
收藏
分享

微信扫一扫

[SQL] 带你搞清SQL中的事务(学习总结)

老北京的热干面 2022-04-13 阅读 66
学习java

1.这个单词的指向含义的广泛的,并不一定就特指我们数据库中的事务处理

2.狭义:在业务方(开发者)看来,就是一个不可再分的业务动作,这个动作最终表达为一条或者多条SQL语句。

3.事务的四个特性:ACID

Atomic(原子性):

业务动作对应的 SQL 应该是看作一个整体,不可再分的。针对数据的修改只能 All or None。

Consistency(一致性):

业务方来定义的,针对数据整体做的不可变的承诺。

lsolation(隔离性):

当有多个 DBMS 的用户,同时针对数据做增删查改时,是否互相直接保持隔离的特性。最理想的情况下,一个用户在操作数据时,是意识不到其他用户同时也在操作数据的。

Durability(持久性):

一旦 DBMS 通知我们数据修改成功了,则数据必然修改成功了(修改被持久化了)。

4.一些区分

Consistency(最终目标)需要 DBMS 和 我们(程序员)共同付出努力来进行维护的

Atomic(原子性)lsolation(隔离性)Durability(持久性):这三个属于 DBMS 对我们的承诺,属于 DBMS 的责任

DBMS承诺支持事务,指的就是 A、D、I (C 是做不到的)

5.我们如何使用事务

(我们和 DBMS 之间在进行数据交换)

DBMS 不知道 SQL 是怎么划分成不同的事务的

第一个角度:我们如何在 SQL 中使用事务

        strat transaction; 告诉 DBMS 一个事务明确开启了(多条 SQL 组成的事务)

        执行属于这个事务的 SQL

        commit;   事务执行完成

比如,以上图为例:

第二个角度:通过 JDBC 的方式来使用事务

4个场景:

1.有事务,commit

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

// 一切正常下的 sql 演示
public class Demo1 {
    public static void main(String[] args) throws SQLException {
        String sql1 = "insert into records (rid, bid) values (1, 2)";
        String sql2 = "update books set count = count - 1 where bid = 2";

        // 要使用事务,在同一个事务中,操作 sql1 和 sql2,意味着必须在一条 Connection 完成
        try(Connection c = DBUtil.connection()){
            // connection 中有一个 自动提交(autocommit)的属性,默认情况下,是 true(开启)
            // 开启状态下,意味着,每一条 sql 都会被独立的视为一个事务
            // 我们要让 sql1 和 sql2 看作整体,只需要关闭 connection 的自动提交
            c.setAutoCommit(false);     // 不会提交,只能被我们进行手动提交
                                        // 我们就可以手动的控制事务的结束位置
            try(PreparedStatement ps = c.prepareStatement(sql1)){
                ps.executeUpdate();
            }
            try(PreparedStatement ps = c.prepareStatement(sql2)){
                ps.executeUpdate();
            }

            // 由于我们关闭了自动提交,所以,所修改的还没有真正的落盘
            c.commit();    // 只有加上这句,事务才被真正的提交了
        }

    }
}

2.没有事务,被动失败(重启服务器)

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

// 没有事务,被动失败(重启服务器)
public class Demo2 {
    public static void main(String[] args) throws SQLException {
        String sql1 = "insert into records (rid, bid) values (1, 2)";
        String sql2 = "update books set count = count - 1 where bid = 2";

        try(Connection c = DBUtil.connection()){

            try(PreparedStatement ps = c.prepareStatement(sql1)){
                ps.executeUpdate();
            }

            // 执行完第一条 SQL 之后,要让第二条 SQL 执行失败

            try(PreparedStatement ps = c.prepareStatement(sql2)){
                ps.executeUpdate();
            }

            // 由于我们关闭了自动提交,所以,所修改的还没有真正的落盘
            c.commit();    // 只有加上这句,事务才被真正的提交了
        }
    }
}

在两条sql语句中打上断点,执行第一条不执行第二条,关闭服务器,执行第二条,会出现错误提示

在Workbench中可以看到第一条语句执行成功了,第二条语句执行失败。

3.有事务,被动失败(重启服务器)

// 有事务,被动失败(重启服务器)
public class Demo3 {
    public static void main(String[] args) throws SQLException {
        String sql1 = "insert into records (rid, bid) values (1, 2)";
        String sql2 = "update books set count = count - 1 where bid = 2";

        // 要使用事务,在同一个事务中,操作 sql1 和 sql2,意味着必须在一条 Connection 完成
        try(Connection c = DBUtil.connection()){
            c.setAutoCommit(false);

            try(PreparedStatement ps = c.prepareStatement(sql1)){
                ps.executeUpdate();
            }

              // 执行完第一条 SQL 之后,要让第二条 SQL 执行失败

            try(PreparedStatement ps = c.prepareStatement(sql2)){
                ps.executeUpdate();
            }

            c.commit();
        }
    }
}

在两条sql语句中打上断点,执行第一条不执行第二条,关闭服务器,执行第二条

在Workbench中可以看到两条语句都没有执行成功,这是事务起到了作用

4.有事务,被动失败(程序导致)

// 有事务,主动失败
public class Demo4 {
    public static void main(String[] args) throws SQLException {
        String sql1 = "insert into records (rid, bid) values (1, 2)";
        String sql2 = "update books set count = count - 1 where bid = 2";

        // 要使用事务,在同一个事务中,操作 sql1 和 sql2,意味着必须在一条 Connection 完成
        try(Connection c = DBUtil.connection()){
            c.setAutoCommit(false);

            try(PreparedStatement ps = c.prepareStatement(sql1)){
                ps.executeUpdate();
            }

            // 执行完第一条 SQL 之后,要让第二条 SQL 执行失败
            // 由于我们代码中的异常导致的被动失败
            int a = 1 / 0;   // 一定会出现 除0 异常,下面的代码就不会执行
                             // 我们没有 catch 除 0 异常,导致异常交给 JVM 去处理
                             // 第一条 SQL 成功、第二条 SQL 失败

            try(PreparedStatement ps = c.prepareStatement(sql2)){
                ps.executeUpdate();
            }

            c.commit();
        }
    }
}

执行这段代码,由于中间有异常,所以第二段代码不会被执行,Workbench中的数据不会有变化。

5.有事务,主动失败(rollback)

// 有事务,主动失败
public class Demo5 {
    public static void main(String[] args) throws SQLException {
        String sql1 = "insert into records (rid, bid) values (1, 2)";
        String sql2 = "update books set count = count - 1 where bid = 2";

        // 要使用事务,在同一个事务中,操作 sql1 和 sql2,意味着必须在一条 Connection 完成
        try(Connection c = DBUtil.connection()){
            c.setAutoCommit(false);

            try(PreparedStatement ps = c.prepareStatement(sql1)){
                ps.executeUpdate();
            }

            try(PreparedStatement ps = c.prepareStatement(sql2)){
                ps.executeUpdate();
            }

            c.rollback();   //主动失败
        }
    }
}

执行程序,虽然两段 sql 都执行了,但最后没有使用commit 使用了 rollback主动失败,所以Workbench中的数据不会被改变。

6.通过事务保护了原子性Atomic

三个场景:

1.开启事务,一切正常情况下,数据会被修改成功(所有 sql 都成功执行了)

2.模拟没有事务时,需要数据库服务器故障的情况

        1.先执行 sql1 

        2.重启服务器

        3.观察数据(一致性被破坏了)

3.模拟有事务时,需要数据库服务器故障的情况

        1.先执行 sql1

        2.重启 mysql 服务器

        3.观察数据(一致性没有被破坏,部分修改的数据被回滚了)

事务只有两个结局:成功(All) 或者 失败(None)。无论成功还是失败,数据的一致性是不能被破坏的。

失败:

1.被动失败:硬件的原因 和 软件的原因(DBMS 本身的原因 和 我们的原因(比如异常))

由于没有执行 commit 所以,事务期间做的“部分修改” 都会被 回滚(rollback)

2.主动失败:我们可以主动要求事务失败(数据回滚)——不去执行 commit ,而去执行 rollback

举报

相关推荐

0 条评论