07-数据库事务

数据库事务介绍

  • 事务:一组逻辑操作单元,使数据从一种状态变换到另一种状态。

  • 事务处理(事务操作):保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),那么这些修改就永久地保存下来;要么数据库管理系统将放弃所作的所有修改,整个事务**回滚(rollback)**到最初状态。

  • 为确保数据库中数据的一致性,数据的操纵应当是离散的成组的逻辑单元:当它全部完成时,数据的一致性可以保持,而当这个单元中的一部分操作失败,整个事务应全部视为错误,所有从起始点以后的操作应全部回退到开始状态。

事务的ACID属性

名称简介举例
Atomicity原子性所有的SQL语句要么全部成功,要么全部失败,不会存在部分更新。假设有以下场景,A转账100元给B。这里有两个动作:一是A账号减少100元,二是B账号增加100元,这两个动作不可分割。
Consistency一致性事务只能以允许的方式改变受其影响的数据。假设A和B两者的钱加在一起一共100元,那么无论A和B之间如何转账,转几次账,事务结束后两个用户的钱加起来一定还是100元。
Isolation隔离性同时发生的事务(并发事务)不应该导致数据库出于不一致的状态中。系统中每个事务都应该像唯一事务一样执行。任何事务都不应影响其他事务的存在。隔离性即是要达到这样一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。
Durability持久性无论数据库或系统是否发生故障,数据都会永久保存在磁盘上,并且不会丢失。当开发人员在使用JDBC操作数据库时,在提交事务后,提示用户事务操作完成,那么这个时候数据就已经存储在磁盘上了。即使数据库重启,该事务所做的更改操作也不会丢失。

数据库的并发问题

  • 对于同时运行的多个事务, 当这些事务访问数据库中相同的数据时, 如果没有采取必要的隔离机制, 就会导致各种并发问题:

    • 脏读: 对于两个事务 T1, T2, T1 读取了已经被 T2 更新但还没有被提交的字段。之后, 若 T2 回滚, T1读取的内容就是临时且无效的。
    • 不可重复读: 对于两个事务T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段。之后, T1再次读取同一个字段, 值就不同了。
    • 幻读: 对于两个事务T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插入了一些新的行。之后, 如果 T1 再次读取同一个表, 就会多出几行。
  • 数据库事务的隔离性: 数据库系统必须具有隔离并发运行各个事务的能力, 使它们不会相互影响, 避免各种并发问题。

  • 一个事务与其他事务隔离的程度称为隔离级别。数据库规定了多种事务隔离级别, 不同隔离级别对应不同的干扰程度, 隔离级别越高, 数据一致性就越好, 但并发性越弱。

四种隔离级别

  • 数据库提供的4种事务隔离级别:
Read UncommittedRead CommittedRepeatable ReadSerializable
中文名未提交读,读取未提交内容提交读,读取提交内容可重复读可串行化、序列化
简介在该隔离级别,所有的事务都可以看到其他未提交事务的执行结果,即在未提交读级别中,数据的修改,对其他事务也都是可见的,该隔离级别很少用于实际应用。读取未提交的数据,也被称为脏读。该隔离级别最低,并发性能最好。一个事务只能看到已经提交事务所做的改变,换句话说,一个事务从开始到提交之前,所做的任何修改对其他事务都是不可见的。这是大多数数据库系统的默认隔离级别。可重复读可以确保同一个事务,在多次读取同样的数据的时候,得到同样的结果。它解决了脏读的问题,不过理论上,这会导致另一个问题:幻读。这是最高的隔离级别,它通过强制事务排序,强制事务串行执行,使之不可能相互冲突,从而解决幻读问题。换言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。实际应用中也很少用到这种隔离级别,只有在非常需要确保数据一致性而且可以接受没有并发的情况下,才考虑用该级别。这是花费代价最高但是最可靠的事务隔离级别。
脏读支持
不可重复读支持支持
幻读支持支持支持
默认级别数据库Oracle、SQL ServerMySQL
并发性能最高比Read Uncommitted低比Read Committed低最低
  • Oracle 支持的 2 种事务隔离级别:READ COMMITED, SERIALIZABLE。 Oracle 默认的事务隔离级别为: READ COMMITED

  • Mysql 支持 4 种事务隔离级别。Mysql 默认的事务隔离级别为: REPEATABLE READ。

在MySql中设置隔离级别

  • 每启动一个 mysql 程序, 就会获得一个单独的数据库连接. 每个数据库连接都有一个全局变量 @@tx_isolation, 表示当前的事务隔离级别。

  • 查看当前的隔离级别:

    SELECT @@tx_isolation;
    
  • 设置当前 mySQL 连接的隔离级别:

    set  transaction isolation level read committed;
    
  • 设置数据库系统的全局的隔离级别:

    set global transaction isolation level read committed;
    
  • 补充操作:

    • 创建mysql数据库用户:

      create user tom identified by 'abc123';
      
    • 授予权限

      #授予通过网络方式登录的tom用户,对所有库所有表的全部权限,密码设为abc123.
      grant all privileges on *.* to tom@'%'  identified by 'abc123'; 
       #给tom用户使用本地命令行方式,授予atguigudb这个库下的所有表的插删改查的权限。
      grant select,insert,delete,update on db_demo.* to tom@localhost identified by 'abc123'; 
      

JDBC事务处理

  • 数据一旦提交,就不可回滚。

  • 数据什么时候意味着提交?

    • 当一个连接对象被创建时,默认情况下是自动提交事务:每次执行一个 SQL 语句时,如果执行成功,就会向数据库自动提交,而不能回滚。
    • **关闭数据库连接,数据就会自动的提交。**如果多个操作,每个操作使用的是自己单独的连接,则无法保证事务。即同一个事务的多个操作必须在同一个连接下。
  • JDBC程序中为了让多个 SQL 语句作为一个事务执行:

    • 调用 Connection 对象的 setAutoCommit(false); 以取消自动提交事务
    • 在所有的 SQL 语句都成功执行后,调用 commit(); 方法提交事务
    • 在出现异常时,调用 rollback(); 方法回滚事务

    若此时 Connection 没有被关闭,还可能被重复使用,则需要恢复其自动提交状态 setAutoCommit(true)。尤其是在使用数据库连接池技术时,执行close()方法前,建议恢复自动提交状态。

案例:用户A向用户B转账100元

import utils.JDBCUtils;

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

public class JDBCTransaction {
    public static void main(String[] args) {
        JDBCTransactionDemo();
    }
    public static void JDBCTransactionDemo(){
        Connection conn = null;
        PreparedStatement ps = null;
        try{
            conn = JDBCUtils.GetConnection();
            // 开启事务,关闭自动提交
            conn.setAutoCommit(false);
            // A的账户减少100
            String sqlstr1 = "update user_assets set assets = assets - 100 where user_name = ?";
            ps = conn.prepareStatement(sqlstr1);
            ps.setObject(1,"A");
            ps.execute();
            // B的账户增加100
            String sqlstr2 = "update user_assets set assets = assets + 100 where user_name = ?";
            ps = conn.prepareStatement(sqlstr2);
            ps.setObject(1,"B");
            ps.execute();
            // 如果没有异常,则提交事务
            conn.commit();
        }catch (Exception e){
            e.printStackTrace();
            // 5.若有异常,则回滚事务
            try{
                conn.rollback();
            }catch (SQLException sqlerr){
                sqlerr.printStackTrace();
            }
        }finally {
            try{
                // 恢复每次DML操作的自动提交功能
                conn.setAutoCommit(true);
            }catch (SQLException e){
                e.printStackTrace();
            }
            JDBCUtils.CloseResource(conn,ps);
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值