数据库事务
为确保数据库中数据的一致性,所有的操作要么都成功,要么都失败。当这个单元中的一部分操作失败,整个事务应全部视为错误,所有从起始点以后的操作应全部回退到开始状态。
事务的操作:先定义开始一个事务,然后对数据作修改操作,这时如果提交(COMMIT),这些修改就永久地保存下来,如果回退(ROLLBACK),数据库管理系统将放弃您所作的所有修改而回到开始事务时的状态。
l事务的ACID属性
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
事务必须使数据库从一个一致性状态变换到另外一个一致性状态。(数据不被破坏)
(事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响
select @@autocommit;
0:表示手动提交
1 自动提交
begin //开始一个事务
rollback //事务回滚
commit //提交事务
例如:
mysql> begin;
mysql> insert into testvalues(9,'dd','dd');
mysql> commit;
MYSQL默认是自动提交的,也就是你提交一个QUERY,它就直接执行
set autocommit =0 禁止自动提交
set autocommit =1 开启自动提交, 来实现事务的处理。
* 当你用 set autocommit =0 的时候,你以后所有的SQL都将做为事务处理,直
到你用commit确认或rollback结束。
* 注意:当你结束这个事务的同时也开启了个新的事务
package cn.itcast.mysql.transaction;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.junit.Test;
import cn.itcast.mysql.util.JDBCUtils;
/**
* #账户基本信息表 create table account ( accountid varchar(18) , #账号 balance
* double(10,2) #余额 ) #存款表 create table inaccount ( accountid varchar(18) , #账号
* inbalance double(10,2) #存入余额 ) #办卡完成,初始化数据 INSERT INTO
* account(accountid,balance) VALUES('123456789',0)
*
* #向账户中存入100元:
* INSERT INTO inaccount(accountid,inbalance) VALUES('123456789',100) UPDATE
* account SET balance=balance+100 WHERE accountid='123456789'
*
* @author Administrator
*
*/
public class TestTransaction {
@Test
public void testSaveMoney() {
Connection conn = null;
PreparedStatement pstmt = null;
// 准备数据
String accountId = "123456789";
double money = 100;
try {
conn = JDBCUtils.getConnection();
/*****************************************************/
//设置事务为手动提交
System.out.println("默认事务为"+conn.getAutoCommit());//true代表自动提交,false手动提交
conn.setAutoCommit(false);
System.out.println("事务为"+conn.getAutoCommit());
/*****************************************************/
// 向inaccount表中插入一条记录
// sql: INSERT INTO inaccount(accountid,inbalance)
// VALUES('123456789',100)
String sql = "INSERT INTO inaccount(accountid,inbalance) VALUES(?,?)";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, accountId);
pstmt.setDouble(2, money);
pstmt.executeUpdate();//数据马上同步到数据库
// 更新account表,将指定账号的余额加上100
// UPDATE account SET balance=balance+100 WHERE
// accountid='123456789'
sql = "UPDATE account SET balance=balance+? WHERE accountid=?";
pstmt = conn.prepareStatement(sql);
pstmt.setDouble(1, money);
pstmt.setString(2, accountId);
//模拟因网络或不明原因出异常
boolean flag = true;
if(flag) {
throw new SQLException("因网络或不明原因出异常!");
}
pstmt.executeUpdate();
/*****************************************************/
//提交事务
conn.commit();
/*****************************************************/
} catch (SQLException e) {
/*****************************************************/
//回滚事务
try {
if(conn!=null) {
conn.rollback();
}
} catch (SQLException e1) {
e1.printStackTrace();
}
/*****************************************************/
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn, pstmt, null);
}
}
}
多个事务并发运行时的并发问题
l事务隔离级别(transactionisolation levels):
隔离级别就是对事务并发控制的四个等级。分为
1 串行化(SERIALIZABLE)
2 可重复读(REPEATABLE READ)
3 读已提交(READ COMMITED)
4 读未提交(READ UNCOMMITED)
常用数据库默认的隔离级别
1、mysql默认的隔离级别为Repeatable_Read
2、sqlserver 默认的隔离级别为Read Commited
3、oracle数据库支持READ COMMITTED和SERIALIZABLE
两种事务隔离性级别,不支持READ UNCOMMITTED和
REPEATABLE READ这两种隔离性级别,
Oracle数据库默认使用的事务隔离性级别却是READ COMMITTED.
READ COMMITTED
select@@tx_isolation
脏读:一个事务读取了另一个事务改写但还未提交的数据,如果这些数据被回滚,则读到的数据是无效的。
不可重复读:在同一事务中,多次读取同一数据返回的结果有所不同。换句话说就是,后续读取可以读到另一事务已提交的更新数据。
可重复读:在同一事务中多次读取数据时,能够保证所读数据一样,也就是,后续读取不能读到另一事务已提交的更新数据。
幻读:一个事务读取了几行记录后,另一个事务插入一些记录,幻读就发生了。再后来的查询中,第一个事务就会发现有些原来没有的记录。
数据库并发操作存在的异常情况
脏读
set TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
业务情景:
张三准备用ATM机向其北京招行的帐户汇1000块钱,在汇之前先用其智能手机的手机银行客户端查询到其北京账户还剩100,接着他用ATM机向其北京帐户打过去1000,在提示确认提交时,他又用手机查看了一下其帐户余额竟然为1100。
不可重复读
set TRANSACTION ISOLATION LEVEL READ COMMITTED ;
业务情景:
张三用其智能手机的手机银行客户端查询其招行帐户余额为100元,他没有马上退出,去做了一会别的事,在此过程中他老婆在一个ATM机上向其帐户上汇入了1000元,他回来后又刷新了一下余额,竟然为1100元。
幻读(Magic read)
set TRANSACTION ISOLATION LEVEL REPEATABLE READ;
业务情景:
张三用其智能手机的手机银行客户端查询其招行帐户余额为300元,他准备用其中的200去抢购一个商品,在他没付款之前他的老婆取走100元,他再次查看了一下余额,还是300元,他花了200支付了商品,在支付完后,他再次查了一下余额,竟然发现账户里一毛钱都没有啦!
不能并发
设置数据库的隔离级别: set TRANSACTION ISOLATION LEVEL SERIALIZABLE;
业务情景:
张三用其智能手机的手机银行客户端查询其招行帐户余额为99.9元,但他需要100元才能支付抢购,他QQ告知老婆在线等待,他老婆立马找到一个ATM机准备向其帐户上汇入了100元,但钱就是汇不进去。