MySQL+ JDBC事务详解

事务的概念
一个事务是由一条或者多条对数据库操作的SQL语句所组成的一个不可分割的单元,只有当事务中所有的SQL语句都正常执行完,整个事务才会被提交给数据库,如果有部分失败,那么事务就要退回到执行SQL语句之前的状态,因此,事务要么全部成功,要么全部失败。
你是不是好奇为啥要把多条SQL语句打包进行操作呀?
我给你举个例子:比方你要给别人转账10000?,后台其实分为三个步骤:
1.检查你账户金额是否高于10000?
2.先从你帐上划掉10000?
3.对方账户增加10000?
如果不将多条SQL语句进行打包处理,现假设刚从你帐上把?划掉,电脑死机了,等把电脑救过来,该条语句已经执行成功,而对方缺没有收到这10000钱,他的账户金额不会增加,那么请问你白白损失的10000?你心疼吗?

事务的ACID特性
每一个运行良好的事务处理系统,必须所要满足的四个特性
1.事务的原子性(Atomic)
将若干的sql语句组成一个事务,不可分割。倘若一条语句失败,则整个语句失败。
2.事务的一致性(Consistency)
一个事务执行之前和执行之后,数据库数据必须保持一致性状态。数据库的一致性状态必须由用户来负责,由并发控制机制实现。
例如转账,从这个银行卡转出10000元 则对方账户增加10000元。
3.事务的隔离性(Isolation)
当两个或者多个事务并发执行时,为了保证数据的安全性,将一个事物内部的操作与其它事务的操作隔离起来,不被其它正在执行的事务所看到。隔离性使得每个事务的更新在它被提交之前,对其它事务都是不可见的。
4.事务的持久性(Durability)
一旦事务提交,则其所做的修改就会永久保存到数据库中,即使系统崩溃,所修改的数据也不会丢失。

事务的隔离级别
我们先来看一下如果事务处理不经隔离,并发执行事务时通常会发生哪些问题?以下都假设只有事务A和事务B并发执行。
1.脏读(Dirty Read)
一个事务读取了另一个事务未提交的数据。例如,当事务A更新后,事务B查询读取到A尚未提交的数据,此时事务A回滚,则事务B读到的数据就是无效的脏数据。(事务B读取了事务A尚未提交的数据)

//A事务
insert user;
//B事务
select user;
//A事务
rollback;
//事务B已经查询到数据,但因为事务A回滚了,事务A回到最初的状态,这种情况下则认为事务B读取了事务A尚未提交的数据,即产生了脏读。

2.不可重复读(NonRepeatable Read)
一个事务的操作导致另一个事务前后两次读取到不同的数据。例如,当事务B查询读取数据后,事务A更新操作更改事务B查询到的数据,此时事务B再次去读该数据,发现前后两次读的数据不一样。(事务B读取了事务A已提交的数据)
例如:
B事务 得到 age=16
select user_age;
A事务 更新 age=20
update user_age;
事务 得到 age=20
select user_age;
事务B前后读到的数据不一致,则认为发生了不可重复读。

3.虚读(Phantom Read)/幻读
一个事务的操作导致另一个事务前后两次查询的结果数据量不同。例如当事务A和事务B并发执行时,当事务B查询读取数据后,事务A新增或者删除了一条满足事务B查询条件的记录,此时事务B再去查询,发现查询到前一次不存在的记录,或者前一次查询的一些记录不见了。(事务B读取了事务A新增加的数据或者读不到事务A删除的数据)
例如:
B事务 select查询到user1 user2
A事务 delete user1
B事务 再次查询只得到了user2
!!!事务B前后读到的数据量不一致,则认为发生了不可重复读。

数据库的四种隔离界别
1.read uncommitted(未提交读)
在read uncommitted级别,事务中的修改,即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,这也被称为脏读。
2.read committed(提交读)
大多数数据库系统默认事务隔离级别是read committed(但MySQL不是)。read committed满足前面提到的隔离性的简单定义:一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。这个级别也可叫做不可重复读,因为两次执行同样的查询,可能得到不一样的结果。
3.repeatable read(可重复读)
repeatable read解决了脏读的问题,该级别保证了在同一事务中多次读取同样记录的结果是一致的。但未解决另外一个幻读的问题。
可重复读是MySQL的默认事务隔离级别
4.serializable(可串行化)
serializable是最高的隔离级别,它通过强制事务串行执行,避免了前面说的幻读的问题。简单地说serializable会在读取的每一行数据上都加锁?,所以可能导致大量的超时和锁争用的问题,该级别只有在非常需要确保数据的一致性而且可以接受没有并发的情况下,才考虑采用该级别。
在这里插入图片描述
JDBC的五种隔离界别
由于多个线程会请求相同的数据,事务之间通常都会用锁互相隔离,由于数据库支持不同类型的锁,因此Java JDBC支持不同级别的事务处理,它们由Connection对象指定。在JDBC中,定义了以下5种事务隔离级别:
1.TRANSACTION_NONE。 表示不支持事务
2.TRANSACTION_READ_UNCOMMITTED。 未提交读。
说明在提交前一个事务可以看到另一个事务的变化。这样读”脏”数据,不可重复读和虚读都是被允许的。
3.TRANSACTION_READ_COMMITTED。 已提交读。
说明读取未提交的数据是不允许的。这个级别仍然允许不可重复读和虚读产生。
4. TRANSACTION_REPEATABLE_READ。 可重复读。
说明事务保证能够再次读取相同的数据而不会失败,但虚读仍然会出现。
5. TRANSACTION_SERIALIZABLE。 可序列化/串行化。
是最高的事务级别,它防止读脏数据,不可重复读和虚读。
需注意:事务隔离级别越高,为避免冲突所花费的性能也就越多。JDBC中可以通过Connection接口下面的函数来设置事务的隔离级别

void setTransactionIsolation(int level) throws SQLException;

MySQL的事务处理命令
1.查看MySQL是否自动提交事务

show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    |
+---------------+-------+
//或者
select @@Autocommit;
+--------------+
| @@Autocommit |
+--------------+
|            1 |
+--------------+

1/on表示启用,0/off 表示禁用或者手动提交事务。
2.设置事务提交方式

set @@Autocommit = 0;

关闭自动提交 commit ,直到所有sql语句全部执行成功,手动调用commit写入文件里。
需要注意的是!!!:开启手动提交事务,需要执行begin命令,告诉数据库你开始执行事务了。
MySQL如何实现事务?
1.修改存储引擎改为InnodB
2.开启事务
3.输入组成事务的sql语句 - - - -设置一个保存点
4.失败 - - - rollback 退回到最初的状态 — 文件里的状态。如果有设置保存点(savepoint 名字;),则可使用 rollback to 使sql返回到保存点
5.commit 组成事务的所有sql语句都执行成功,调用commit真正提交给文件,不可再调用rollback。

1. begin;   开启一个事务
2. commit;   提交一个事务
3. rollback;          回滚一个事务到初始的位置
4. savepoint point1;  设置一个名字为point1的保存点
5. rollback to point1;  事务回滚到保存点point1,而不是回滚到初始状态
6. set global tx_isolation ='REPEATABLE-READ'; 设置事务的隔离级别
7. select @@ TX_ISOLATION;    查询事务的隔离级别

MySQL的事务处理需要注意的点
因为MySQL存储引擎中只有InnoDB支持事务处理,所以想要对数据库进行事务操作,就得保证你的数据库引擎是InnoDB的。
存储引擎修改方式可见:MySQL存储引擎

JDBC中事务处理
在JDBC当中,可以通过调用setAutoCommit(false)方法来禁止自动提交事务,然后就可以把多个数据库操作的SQL表达式作为一个事务,在操作完成以后调用commit方法来实现事务提交,如果其中一个表达式失败,就会抛出异常而不会调用commit。在这种情况下,就可以在异常代码处理中调用rollback对已经发生的事务操作进行回滚。通过此种方法可以保持对数据库进行多次操作后,数据仍然是保持一致的。
如果是使用JDBC对数据库的事务设置隔离级别的话,也应该是在调用Connection对象的setAutoCommit(false)方法之前,调用Connection对象的setTransactionIsolation(level)即可设置当前链接的隔离级别,至于参数level,可以使用Connection对象的字段:
在这里插入图片描述
在JDBC中设置隔离级别的部分代码

public Connection getConnection() throws ClassNotFoundException, SQLException {
    //和MySQL建立连接
    //加载驱动
    Class.forName(jdbcDriver);
    connection = DriverManager.getConnection(jdbcUrl,user,password);
    //设置该链接的隔离级别
    connection.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
    connection.setAutoCommit(false); //开启事务
    if(connection.isClosed()){
        System.out.println("failed");
        st = connection.createStatement();
    }else{
        System.out.println("success");
        st = connection.createStatement(); // 初始化传输sql语句的对象
    }
    return connection;
}

隔离级别的设置只对当前链接有效。对于使用MySQL命令窗口而言,一个窗口就相当于一个链接,也称作一个会话,当前窗口设置的隔离级别只对当前窗口中的事务有效;对于JDBC操作数据库来说,一个Connection对象相当于一个链接(或者称一个会话),而对于Connection对象设置的隔离级别只对该Connection对象有效,与其他链接Connection对象无关,也就是说每个Connection对象都需要设置各自的隔离级别。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值