事务
基本概念:
事务使指一组最小逻辑操作单元,里面有多个操作组成
组成事务的每一部分必须要同时提交成功,
如果有一个操作失败,整个操作就回滚
事务的 ACID特性:
- 原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,
事务中的操作要么都发生,要么都不发生- 一致性(Consistency)
事务必须使数据库从一个一致性状态变换到另外一个一致性状态- 隔离性(Isolation)
事务的隔离性是多个用户、并发访问数据库时,
数据库为每一个用户开启的事务,
不能被其他事务的操作数据所干扰,
多个并发事务之间要相互隔离- 持久性(Durability)
持久性是指一个事务一旦被提交,
它对数据库中数据的改变就是永久性的,
接下来即使数据库发生故障也不应该对其有任何影响
默认情况下,Connection 对象处于自动提交模式下,
这意味着它在执行每个语句后都会自动提交更改
如果禁用了自动提交模式,那么要提交更改就必须显式调用 commit 方法;否则无法保存数据库更改
void setAutoCommit(boolean autoCommit) throws SQLException
将此连接的自动提交模式设置为给定状态。
如果连接处于自动提交模式下,则它的所有 SQL 语句将被执行并作为单个事务提交
否则,它的 SQL 语句将聚集到事务中,
直到调用 commit 方法或 rollback 方法为止。
默认情况下,新连接处于自动提交模式
那么,本人现在来展示下事务的使用:
例子:
这天,一个粉丝想要资助右转哥(做梦ing),粉丝和右转哥的银行账户金额分别为2333和10999,而粉丝要给右转哥转账4333,以便给右转哥凑个6666出来
那么,请看如下代码:
package edu.youzg.about_jdbc.core;
import edu.youzg.about_jdbc.utils.JDBCUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class FundDemo {
public static void main(String[] args) throws Exception {
//转账
//默认事务是自动开启的
Connection conn1 = JDBCUtils.getConnection();
//编写sql语句
String sql1="update bank set money=money-4333 where username='fan'";
String sql2 = "update bank set money=money+4333 where username='youzg'";
//获取操作对象
PreparedStatement statement1 = conn1.prepareStatement(sql1);
PreparedStatement statement2 = conn1.prepareStatement(sql2);
//执行:
statement1.executeUpdate(); //单独事务1 自动提交
//模拟一个异常
System.out.println(1/0);
statement2.executeUpdate();//单独的事务2 自动提交
//释放资源
conn1.close();
statement1.close();
statement2.close();
}
}
那么,本人来展示下运行后的结果:
可以看到:当转账过程中出现了异常之后,发生异常的那句代码之后的代码段都不会执行
所以导致了,粉丝已经转账过了,但是右转哥这里却没有收到转账
这跟本人之前讲解的异常的知识点有关:
异常相当于return
那么,上面的结果显然是不合理的,
转账不能因为一个异常导致转的金额就凭空消失了
所以,这里就运用到了事务性质
那么,本人现在来展示下使用事务的性质来解决上述问题的代码:
package edu.youzg.about_jdbc.core;
import edu.youzg.about_jdbc.utils.JDBCUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class FundDemo {
public static void main(String[] args) throws Exception {
Connection conn1=null;
try {
//转账
//默认事务是自动开启的
conn1 = JDBCUtils.getConnection();
//设置数据库的隔离级别,一般不要设置。数据库装好之后的默认级别就OK
//1.把事务改成手动提交。
conn1.setAutoCommit(false);
//编写sql语句
String sql1 = "update bank set money=money-4333 where username='fan'";
String sql2 = "update bank set money=money+4333 where username='youzg'";
//获取操作对象
PreparedStatement statement1 = conn1.prepareStatement(sql1);
PreparedStatement statement2 = conn1.prepareStatement(sql2);
//执行:
statement1.executeUpdate();
//模拟异常
System.out.println(1 / 0);
statement2.executeUpdate();
} catch (Exception e) {
try {
//一旦遇到异常,咱们就回滚事务
conn1.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally {
//不管有没有遇到异常,我们都提交事务
try {
conn1.commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
System.out.println("操作完成");
}
}
那么,我们再来运行下代码,来看看运行后的结果:
那么,可以看到:
即使发生了异常,也不会导致总金额减少,保证了数据的一致性
那么,现在,本人来讲解下 事务的隔离级别:
事务的隔离级别:
若是我们不考虑事务的隔离性,就会产生如下三种读问题:
- 脏读:
在一个事务中读取到另一个事务 没有提交的数据- 不可重复读:
在一个事务中,两次查询的结果不一致(针对的update操作)
即 在数据库访问中,一个事务范围内两个相同的查询却返回了不同数据- 虚读(幻读):
在一个事务中,两次查询的结果不一致(针对的insert操作) 无法演示出来
(MySQL已经默认避免了)
那么,现在,本人来讲解下事务的隔离级别:
隔离级别:
- read uncommitted:
读未提交
上面的三个问题都会出现- read committed:
读已提交
可以避免脏读的发生
(Oracle 默认级别)- repeatable read:
可重复读
可以避免脏读和不可重复读的发生
(MySQL 默认级别)- serializable:
串行化
可以避免所有的问题
相关语句和方法:
- sql语句:
- set session transaction isolation level 隔离级别:
将 隔离级别 设置为 指定级别- select @@tx_isolation:
查看数据库的隔离级别
- Java方法
- void setTransactionIsolation(int level):
控制隔离级别
那么,本篇博文的知识点在这里就讲解完毕了。
(请观看本人博文 —— 《JDBC 专栏总集篇》)