一、事务的基本理解
对于事务的理解,我觉得最好举一个例子来说比较容易理解,一个现实生活中的例子。
在银行转账的过程中,A账户向B账户赚钱,会发生两个操作:
A的账户中扣除1000元,
B的账户中增加1000元。
在数据库执行时,从A账户中扣除1000元,但是因为数据库发生了某种异常,可能B账户的存入1000元的操作可能就会抛出异常发生中断,所以B账户就不会增加1000元。
事务的操作就是上述的两个操作如果两个成功就都应该成功,如果只有一个成功都是失败的。
二、事务的特性
事务的四大特性:
原子性(Atomicity):事务中的操作都是不可再分割的原子单位。事务的所有操作要么全部执行成功,要么全部执行失败。
一致性(Consistency):事务执行后,数据库的状态与其他业务保持一致,例如转账业务,无论事务执行是否成功,两个账户的余额总数是不变的。
隔离性(Isolation):在并发操作中,不同的事务操作隔离开来,不会发生相互干扰。
持久性(Durability):一旦事务提交成功,数据库中的数据是持久性的改变,
即使提交事务后,数据库马上崩溃,在数据库重启时,也必须能保证通过某种机制恢复数据。
三、MySQL数据库中
在默认情况下,MySQL每执行一条SQL语句,都是一个单独的事务。如果需要在一个事务中包含多条SQL语句,那么需要开启事务和结束事务。
开启事务:start transaction;
结束事务:commit或rollback。
在执行SQL语句之前,先执行strat transaction,这就开启了一个事务(事务的起点),然后可以去执行多条SQL语句,最后要结束事务,commit表示提交,即事务中的多条SQL语句所做出的影响会持久化到数据库中。或者rollback,表示回滚,即回滚到事务的起点,之前做的所有操作都被撤消了!
面演示jerry给peter转账1000元的示例:
<span style="font-family:Microsoft YaHei;font-size:14px;">START TRANSACTION;
UPDATE account SET balance=balance-1000 WHERE id=1;
UPDATE account SET balance=balance+1000 WHERE id=2;
ROLLBACK;</span>
<span style="font-family:Microsoft YaHei;font-size:14px;">START TRANSACTION;
UPDATE account SET balance=balance-1000 WHERE id=1;
UPDATE account SET balance=balance+1000 WHERE id=2;
COMMIT;</span>
四、JDBC中的事务
Connection的三个方法与事务相关:
l setAutoCommit(boolean):设置是否为自动提交事务,如果true(默认值就是true)表示自动提交,也就是每条执行的SQL语句都是一个单独的事务,如果设置false,那么就相当于开启了事务了;con.setAutoCommit(false)表示开启事务!!!
l commit():提交结束事务;con.commit();表示提交事务
l rollback():回滚结束事务。con.rollback();表示回滚事务
jdbc处理事务的代码格式:
<span style="font-family:Microsoft YaHei;font-size:14px;">try {
con.setAutoCommit(false);//开启事务…
….
…
con.commit();//try的最后提交事务
} catch() {
con.rollback();//回滚事务
}</span>
保存点:
保存点是JDBC3.0的东西!当要求数据库服务器支持保存点方式的回滚。
校验数据库服务器是否支持保存点!
<span style="font-family:Microsoft YaHei;font-size:14px;">boolean b = con.getMetaData().supportsSavepoints();</span>
保存点的作用是允许事务回滚到指定的保存点位置。在事务中设置好保存点,然后回滚时可以选择回滚到指定的保存点,而不是回滚整个事务!
注意,回滚到指定保存点并没有结束事务!!!只有回滚了整个事务才算是结束事务了!
Connection类的设置保存点,以及回滚到指定保存点方法:
l 设置保存点:Savepoint setSavepoint();
l 回滚到指定保存点:void rollback(Savepoint)。
五、事务的隔离级别
1 事务的并发读问题
脏读:读取到另一个事务未提交数据;
不可重复读:两次读取不一致;
幻读(虚读):读到另一事务已提交数据。
2 并发事务问题
因为并发事务导致的问题大致有5类,其中两类是更新问题,三类是读问题。
脏读(dirty read):读到另一个事务的未提交更新数据,即读取到了脏数据;
不可重复读(unrepeatable read):对同一记录的两次读取不一致,因为另一事务对该记录做了修改;
幻读(虚读)(phantom read):对同一张表的两次查询不一致,因为另一事务插入了一条记录;
不可重复读和幻读的区别:
不可重复读是读取到了另一事务的更新;
幻读是读取到了另一事务的插入(MySQL中无法测试到幻读);
MySQL中无法测试的幻读,原因是MySQL中的隔离级别设置不同。
3 四大隔离级别
1、SERIALIZABLE(串行化)
不会出现任何并发问题,因为它是对同一数据的访问是串行的,非并发访问的;
性能最差;
2、REPEATABLE READ(可重复读)(MySQL默认设置)
防止脏读和不可重复读,不能处理幻读问题;
性能比SERIALIZABLE好
3、READ COMMITTED(读已提交数据)(Oracle默认设置)
防止脏读,没有处理不可重复读,也没有处理幻读;
性能比REPEATABLE READ好
4、READ UNCOMMITTED(读未提交数据)
可能出现任何事务并发问题
性能最好
在MySQL中可以通过下面语句查看和设置:
select@@tx_isolation <span style="font-family:Microsoft YaHei;">--查看隔离级别</span>
<p>set<span style="font-family:Microsoft YaHei;"> </span>transaction isolationlevel <span style="font-family:Microsoft YaHei;">--四选一隔离级别</span></p>
jdbj中设置隔离级别调用方法:
con. SetTransactionIsolation(int level)
参数选择:
Connection.TRANSACTION_READ_UNCOMMITTED
Connection.TRANSACTION_READ_COMMITTED
Connection.TRANSACTION_REPEATABLE_READ
Connection.TRANSACTION_SERIALIZABLE