文章目录
事务
事务概念
由一条或多条SQL语句组成,要么都成功,要么都失败
事务原则(ACID)
- 原子性(Atomicity):事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用
- 一致性(Consistency)): 执行事务前后,数据保持一致(例如,完整性约束了a+b=10,一个事务改变了a,那么b也应该随之改变)多个事务对同一个数据读取的结果是相同的
- 隔离性(独立性)(Isolation): 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的
事务的隔离级别又分为
脏读: 指一个事务读取了另外一个事务未提交的数据
不可重复读: 在一个事务内读取表中的某一行数据,多次读取的结果不同(不一定错误,只是某些场合不对)
幻读: 指在一个事务内读取到了别的事务插入的数据,导致前后读取不一样
四种隔离级别
隔离级别 | 说明 |
---|---|
READ UNCOMMITTED (Read Uncommitted)读未提交数据 | 允许事务读取未被其他事务提交的数据,也被称之为脏读(Dirty Read )脏读、不可重复读和幻读都会出现 |
READ COMMITED (Read Committed)读已提交数据 | 这是Oracle 的默认事务隔离级别。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。可以避免脏读,但不可重复读和幻读问题仍然存在 |
REPEATABLE READ (Repeatable Read)可重复读 | 这是Mysql 的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,在这个事务持续期间,禁止其他事务对这个实例进行更新。但幻读仍然存在。InnoDB 和Falcon 存储引擎通过多版本并发控制(MVCC ,Multiversion Concurrency Control )机制解决了该问题 |
SERIALIZABLE (Serializable)串行化 | 这是最高的隔离级别,但是性能也是最差的,确保事务可以从一个表中读取相同的行,在这个事务的持续期间,禁止其他事务对该表执行插入、更新和删除操作,从而解决幻读问题。总的来说,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。 |
Oracle
支持的2种事务隔离级别:READ COMMITED
和SERIALIZABLE
。Oracle
默认的事务隔离级别是READ COMMITED
持久性(Durability):一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响
事务分类
隐式事务:没有明显的开启和结束标记(DML语句的INSERT
、UPDATE
、DELETE
语句本身就是一条事务)
显式事务:具有明显的开启和结束标记(一般由多条SQL
语句组成,必须具有明显的开启和结束标记)
/*
事务格式:
取消隐式事务自动开启的功能(一般事务是默认开启的,所有得先关闭)
开启事务
编写事务需要的sql语句(1条或者多条)
结束事务(提交和回滚)
*/
-- 这几个命令了解就好
SHOW VARIABLES LIKE '%auto%'-- 查看系统变量
SAVEPOINT 保存点名 -- 设置一个事务的保存点
ROLLBACK TO SAVEPOINT 保存点名 -- 回滚到保存点
RELEASE SAVEPOINT 保存点名 -- 撤销保存点
-- MySQL是默认开启事务自动提交的
SET autocommit=0;-- 关闭(一般先手动处理事务,关闭自动条件)
-- SET autocommit=1;-- 开启(默认的)
START TRANSACTION;-- 事务开启;标记一个事务的开启,从这个之后SQL都在同一个事务内
编写事务需要的SQL语句(1条或者多条)
-- 提交:提交就是持久化
COMMIT;
-- 回滚:回到原来的样子
ROLLBACK;
-- 事务结束
SET autocommit=1; -- 然后事务结束,自动开启提交
模拟转账
-- 创建数据库
CREATE DATABASE bank CHARSET SET utf8 COLLATE utf_8_general_ci;
-- 创建表
CREATE TABLE IF NOT EXISTS `account`(
`id` INT(3) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(20) NOT NULL,
`money` DECIMAL(9,4) NOT NULL,
PRIMARY KEY(`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
-- 添加数据
INSERT INTO `account`(`name`,`money`)
VALUES ('张三',2000.0000),('张四',10000.0000);
-- 关闭自动提交
SET autocommit=0;
-- 开启一个事务
START TRANSACTION;
UPDATE `account` SET money=money-500 WHERE `name`='张三';
UPDATE `account` SET money=money+500 WHERE `name`='张四';
COMMIT;-- 提交事务(持久化)
ROLLBACK;-- 回滚到原来的位置
SET autocommit=1;-- 恢复默认值
用Java代码模拟转账
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class AcidText {
public static void main(String[] args) {
Connection c=null;
PreparedStatement ps=null;
ResultSet r=null;
try {
c=JdbcUtils.getConnection();
//关闭自动提交(这一步不仅关闭事务提交,同时自动开启一个事务(也就是说不用另外开启一个事务))
c.setAutoCommit(false);//这也相当于开启事务
//编写事务需要的SQL语句
String sql="update account set money=money-500 where id=2";
ps= c.prepareStatement(sql);
ps.executeUpdate();
String sql1="update account set money=money+500 where id=1";
ps= c.prepareStatement(sql1);
ps.executeUpdate();
//提交事务
c.commit();
System.out.println("成功");
} catch (SQLException throwables) {
try {
c.rollback();//如果失败则回滚事务
} catch (SQLException e) {
e.printStackTrace();
}
throwables.printStackTrace();
}finally {
JdbcUtils.release(c,ps,null);
}
}
}
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class JdbcUtils {
public static String driver=null;
public static String url=null;
public static String username=null;
public static String password=null;
static {
try {
//加载配置文件
InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties properties = new Properties();
properties.load(in);
//获取配置文件的信息
driver=properties.getProperty("driver");
url=properties.getProperty("url");
username=properties.getProperty("username");
password=properties.getProperty("password");
//加载驱动
Class.forName(driver);
} catch (Exception e) {
e.printStackTrace();
}
}
//连接信息,连接成功,返回数据库对象(固定一个类(驱动管理));Connection代表数据库
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url,username,password);
}
//释放连接
public static void release(Connection c, Statement s, ResultSet r){
try {
if (c!=null){
c.close();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}try {
if (s!=null){
s.close();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}try {
if (r!=null){
r.close();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
#配置文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/bank?useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=111
补充
查看一些命令设置和隔离级别
-- 查看系统事务隔离级别
select @@global.tx_isolation;
-- 设置当前MySQL连接的隔离级别
set transaction isolation level read committed
-- 设置数据库系统的全局的隔离级别
set global transaction isolation level read committed;