什么是ACID?
ACID,指数据库事务正确执行的四个基本要素的缩写。包含:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。一个支持事务(Transaction)的数据库,必需要具有这四种特性,否则在事务过程(Transaction processing)当中无法保证数据的正确性,交易过程极可能达不到交易方的要求。
整个事务中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
一个事务可以封装状态改变(除非它是一个只读的)。事务必须始终保持系统处于一致的状态,不管在任何给定的时间并发事务有多少。
也就是说:如果事务是并发多个,系统也必须如同串行事务一样操作。其主要特征是保护性和不变性(Preserving an Invariant),以转账案例为例,假设有五个账户,每个账户余额是100元,那么五个账户总额是500元,如果在这个5个账户之间同时发生多个转账,无论并发多少个,比如在A与B账户之间转账5元,在C与D账户之间转账10元,在B与E之间转账15元,五个账户总额也应该还是500元,这就是保护性和不变性。
隔离状态执行事务,使它们好像是系统在给定时间内执行的唯一操作。如果有两个事务,运行在相同的时间内,执行相同的功能,事务的隔离性将确保每一事务在系统中认为只有该事务在使用系统。这种属性有时称为串行化,为了防止事务操作间的混淆,必须串行化或序列化请求,使得在同一时间仅有一个请求用于同一数据。
在事务完成以后,该事务对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。
事务用于保证数据的一致性,它由一组相关的dml语句组成,该组的dml语句要么全部成功,要么全部失败。
如:网上转账就是典型的要用事务来处理,用以保证数据的一致性。
当执行事务操作时(dml语句),oracle会在被作用的表上加锁,防止其它用户改表表的结构,这种锁机制采用的是文件锁形式,之前学习Java多线程的时候,对此一个比较直观的认识就是,上锁就表示在他没有解锁的情况下,只有他能用,其他人不能用。
当执使用commit语句可以提交事务,当执行了commit语句之后,会确认事务的变化、结束事务、删除保存点、释放锁,当使用commit语句结束事务后,其它会话将可以查看到事务变化后的新数据。
在介绍回退事务前,必须先知道保存点(savepoint)的概念和作用。保存点是事务中的一点,用于取消部分事务,当结束事务时,会自动的删除该事务所定义的所有保存点。
而回退事务就是在没有提交的情况下,返回当前事务的某一个保存点。
1、设置保存点
savepoint 保存点名;
2、取消部分事务
rollback to 保存点名;
3、取消全部事务
rollback;
1)保存点基本语法:savepoint 保存点名称;
2)在一个事务中允许设置多个保存点
3)返回保存点基本语法:rollback to 保存点名称;
4)回退至指定保存点后,此保存点会被删除。无法再次回退到此点,如要再回此点,需再次设置保存点
5)设置保存点,是有资源开销的
6)一旦提交commit事务,则不能回退到任何保存点,换言之,只要提交commit了事务,此事务保存点就全被删除了。
/**
* 使用jdbc连接Oracle
* 事务演示
* 1)从SMITH帐户减掉$100。
* 2)在ALLEN帐户加入$100。
*/
package com.oracle.db;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class db3 {
Connection ct = null;
PreparedStatement ps = null;
ResultSet rs = null;
public db3() {
try {
// 1.加载驱动
Class.forName("oracle.jdbc.driver.OracleDriver");
// 2.得到连接
ct = DriverManager.getConnection(
"jdbc:oracle:thin:@127.0.0.1:1521:Switch", "scott",
"123456");
// 3.创建PreparedStatement
// 4.执行SQL
// 设置不自动提交
ct.setAutoCommit(false);
// 这样事务就是原子的
// empno为7360的员工薪水-100
String sql1 = "update emp set sal=sal-100 where empno=7369";
ps = ct.prepareStatement(sql1);
ps.executeUpdate();
//如果在事务之中发生了异常,则回滚
//int a = 10/0;
// empno为7499的员工薪水+100
String sql2 = "update emp set sal=sal+100 where empno=7499";
ps = ct.prepareStatement(sql2);
ps.executeUpdate();
// 提交事务
ct.commit();
} catch (Exception e) {
//事务出现了异常,就可以回滚
try {
// 回滚
ct.rollback();
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
e.printStackTrace();
} finally {
try {
if (rs != null) {
rs.close();
}
if (ps != null) {
ps.close();
}
if (ct != null) {
ct.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
db3 d1 = new db3();
}
}
1、如果一个事务中,只有select那么事务控制可以忽略;
2、如果一个事务中,有多个dml语句(update,insert,delete)则需要考虑事务。
package com.pc.jdbc;
import java.sql.*;
/**
* Created by switch on 16/8/29.
* 该案例演示了JDBC中保存点(Savepoint)的使用
* 通过使用保存点,事务可以做到当超过保存点的语句发生异常时
* 回退到保存点,再执行其他语句
*/
public class JDBC8 {
public static final String DRIVER_NAME = "com.mysql.jdbc.Driver";
public static final String DB_URL = "jdbc:mysql://localhost:3306/cloud_study?useUnicode=true&characterEncoding=UTF8";
public static final String USER = "root";
public static final String PASSWORD = "123456";
public static Connection ct = null;
public static PreparedStatement ps = null;
public static ResultSet rs = null;
public static Savepoint sp = null;
public static void main(String[] args) {
try {
Class.forName(DRIVER_NAME);
ct = DriverManager.getConnection(DB_URL, USER, PASSWORD);
// 自动提交关闭
// 事务处理
ct.setAutoCommit(false);
String sql = "update product set inventory = ? where productName = ?";
ps = ct.prepareStatement(sql);
// 将面包的库存设置为5
ps.setInt(1, 5);
ps.setString(2, "bread");
ps.execute();
// 设置保存点
sp = ct.setSavepoint();
// 将苹果的库存设置为10
ps.setInt(1, 10);
ps.setString(2, "apple");
ps.execute();
// 抛出异常,
throw new SQLException();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
if (ct != null) {
try {
// 回滚到保存点
ct.rollback(sp);
// 将牛奶的库存设置为10
ps.setInt(1, 10);
ps.setString(2, "milk");
ps.execute();
ct.commit();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
} finally {
// 5. 关闭资源
try {
if (rs != null) {
rs.close();
}
if (ps != null) {
ps.close();
}
if (ct == null) {
ct.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
概念:隔离级别定义了事务与事务之间的隔离程度。
ANSI/ISO SQL92标准定义了一些数据库操作的隔离级别(这是国际标准化组织定义的一个标准而以,不同的数据库在实现时有所不同)。
隔离级别 |
脏读 |
不可重复读 |
幻影读 |
读未提交(Read uncommitted) |
√ |
√ |
√ |
读已提交(Read committed) |
× |
√ |
√ |
可重复读(Repeatable read) |
× |
× |
√ |
可串行化(Serializable) |
× |
× |
× |
√可能出现,×不会出现
脏读(dirty read):当一个事务读取另一个事务尚未提交的修改时,产生脏读。
在oracle中没有脏读的问题。
不可重复读(nonrepeatable read):同一查询在同一事务中多次进行,由于其他提交事务所做的修改,每次返回不同的结果数据,结果条数一样,此时产生非重复读。
幻影读(phantom read):同一查询在同一事务中多次进行,由于其他提交事务所做的插入或删除操作,每次返回不同的结果集,此时发生幻读。
oracle提供了sql92标准中的read committed(读已提交)和serializable(可串行化),同时提供了非sql92标准的read-only(读一致性)
oracle的read committed读已提交说明:
1、这里oracle缺省的事务隔离级别;
2、保证不会脏读,但可能出现非重复读和幻读。
oracle的serializable可串行化说明:
1、serializable就是使事务看起来象是一个接着一个地顺序地执行(从效果上可以这样理解);
2、仅仅能看见在本事务开始前由其它事务提交的更改和在本事务中所做的更改;
3、保证不会出现脏读、不可重复读和幻读;
4、serializable隔离级别提供了read-only事务所提供的读一致性(事务的读一致性),同时又允许DML(update/insert/delete)操作
oracle的read only读一致性说明:
1、遵从事务级的读一致性,仅仅能看见在本事务开始前由其它事务提交的更改;
2、不允许在本事务中进行DML(insert/update/delete)操作;
3、read only是serializable的子集。它们都避免了不可重复读和幻读。区别是在read only中是只读;而在serializable中可以进行DML操作。
set transaction isolation level read committed;(oracle默认的事务隔离级别“读已提交”)
set transaction isolation level serializable;(设置事务隔离级别为“可串行化”)
set transaction read only;(设置事务隔离级别为“读一致性”)
alter session set isolation_level serializable;(设置会话为“可串行化”)
alter session set isolation_level read committed;(设置会语为“读已提交”)
只读事务是指只允许执行查询的操作,而不允许执行任何其它dml操作的事务,使用只读事务可以确保用户只能取得某时间点的数据。假定机票代售点每天18点开始统计今天的销售情况,这时可以使用只读事务.在设置了只读事务后,尽管其它会话可能会提交新的事务,但是只读事务将不会取得最新数据的变化,从而可以保证取得特定时间点的数据信息。
ct.setTransactionIsolation(Connection.TRANSACTION_NONE);//不设置事务处理
ct.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED); //读未提交
ct.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);//读已提交
ct.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);//可重复读
ct.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);//可串行化
1、java程序中Connection.XXXXXX有五个,但不是所有的数据库对有对应的五个事务隔离级别实现。
2、在实际工作中,我们极少去修改各个数据库默认的隔离级别。
----------参考《韩顺平玩转Oracle》