数据库事务
指的是一组最小的逻辑操作单元,组成事务的每一个部分,要么全部执行,如果有某一步执行行错误,整个操作,要回滚到最初开始的状态。
事务的4大特性
- 原子性
事务是不可分割的,组成事务的每个部分,要么同时成功, 要么同时失败。 - 一致性
事务从一种状态 变换成另一种状态,结果的总和是一样的。比如转账,两边加起来的钱是一样的。 - 隔离性
并发访问的时候,一个事务不受其他事务的影响 - 持久性
事务一旦被提交,对数据库的改变是永久的。
例如: bank银行用户表
现在张三要给李四转1000块钱
正常情况如下:
package com.jingfei.sql;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
public class Transaction {
public static void main(String[] args) throws Exception{
Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql:///mydb", "root", "123456");
//禁止自动提交,将sql语句聚集到一个事务中
connection.setAutoCommit(false);
//张三给李四转1000块钱
String sql="update bank set money=money-1000 where username='张三'";
String sql2="update bank set money=money+1000 where username='李四'";
PreparedStatement statement=connection.prepareStatement(sql);
PreparedStatement statement2=connection.prepareStatement(sql2);
statement.executeUpdate();
statement2.executeUpdate();
//提交事务
connection.commit();
//释放资源
connection.close();
statement.close();
statement2.close();
}
}
若中途出错需要回滚到初始状态
package com.jingfei.sql;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class Transaction {
static Connection connection=null;
static PreparedStatement statement=null;
static PreparedStatement statement2=null;
public static void main(String[] args) {
try {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql:///mydb", "root", "123456");
//禁止自动提交,将sql语句聚集到一个事务中
connection.setAutoCommit(false);
//张三给李四转1000块钱
String sql="update bank set money=money-1000 where username='张三'";
String sql2="update bank set money=money+1000 where username='李四'";
statement=connection.prepareStatement(sql);
statement2=connection.prepareStatement(sql2);
statement.executeUpdate();
//手动制造错误
System.out.println(1/0);
statement2.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
//若注释掉以下try catch的话,发生错误后不回滚,就会发现张三少了1000,但李四并没有加上1000.
try {
//发生错误回滚到初始状态
connection.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}finally {
try {
//手动提交事务
connection.commit();
//释放资源
connection.close();
statement.close();
statement2.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
也可以设置回滚点
Savepoint savepoint = conn.setSavepoint();
//z爱事务前设置一个回滚点
conn.rollback(savepoint);
在发生错误的时候回滚到回滚点
数据库隔离级别
- 按照隔离级别由低到高
read uncommited<read comited <repeatable read <serializable - 查看数据库隔离级别
select @@tx_isolation
mysql默认为REPEATABLE-READ
oracle默认READ COMMITED
级别read uncommited 不能避免脏读,
- 一个事务中可以读到另一个事务未提交(没有持久化)的数据
级别read commited 可以避免脏读
- 但在一个事务中读取的结果会出现不同,也叫(不可重复读)。就是在一个李四有一个事务查看自己的钱,张三有一个事务转钱给李四1000,但是张三并没有提交的时候让李四查一下,李四查看了后说钱没到,然后张三说你别急,然后张三执行了commit提交,然后再让李四查看,李四说钱到了。就是再一个事务中 查询结果不同。
级别repeatable read 可以避免不可重复读
- 在一次事务中两次读取的结果一致,即李四两次读取的结果都是张三没转钱给他,张三说你把卡拔下重新插一下(重新一个事务)就好了,李四照做,就可以查看到张三转来了1000块钱。
级别serializable 可以避免所有问题,会锁表,效率低,这边事务不提交,另一边一直处于等待查询状态。
谢谢