<返回更多

浅谈java事务及隔离级别

2019-10-14    
加入收藏

「原创」浅谈java事务及隔离级别

 

目录


一:事务的定义及作用

事务(Transaction),一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。事务一般由事务开始(begin transaction)和事务结束(end transaction)之间执行的全体操作组成。那么,在平时的应用中,为什么要使用事务呢?这里笔者想通过一个简单的例子来说明事务的重要性。支付宝是生活中经常使用的转账手段,假如账户A要通过支付宝将自己账户上的100元转到B账户,那么对于A账户来说余额就要减去100元,然后在B账户里增加100元,此时算是转账成功。假如就在A账户减去100元时,不巧出现了网络故障,B账户里还未来得及增加100元,那么整个转账业务就会出现问题。所以,为了保证业务的完整性,就需要通过事务来控制,将A减少100元和B增加100元放入同一个事务中,则该事务要么执行成功,要么执行失败后全部撤销,以此来保证数据的安全。

二:事务的四个特性(ACID)

事务的四个特性包括原子性(atomicity),一致性(consistency),隔离性(isolation)和持久性(durability)。笔者将通过上文的例子来帮助读者理解事务的这四个特性。1.原子性:事务是数据库的逻辑工作单位,不可再分。当我们把“A减少100和B增加100”加入一个事务时,这个将会成为数据库工作的最小单位,对于A和B数据的修改,要么全部成功,要么全部失败,不会出现某一个成功另一个失败的情况。2.一致性:在事务处理执行前后,数据库是一致的(数据库数据完整性约束)。假设A原来有100元,B原来有0元,那么最初A账户和B账户一共有100元;当事务结束时,即转账成功后,最终A账户和B账户一共有100元,与最初的值保持不变,是数据达到一致。3.隔离性:每个事务都是独立的,一个事务的执行不能被其他事务所影响。例如A给B转账100元,C给A转账100元,那么“A减少100元,B增加100元”与“C减少100元,A增加100元”属于两个不同的事务,并且两个事务相对独立。4.持久性:事务处理的结果能够被永久保存在数据库中。

三:JDBC事务

在JDBC中处理事务,都是通过Connection完成的。同一事务中所有的操作,都在使用同一个Connection对象。JDBC事务默认是开启的,并且是默认提交。下面是事务在JAVA中的最基本操作: connection.setAutoCommit(boolean);//设置是否为自动提交事务,如果true(默认值为true)表示自动提交,如果设置为false,需要手动提交事务。 connection.commit();//提交事务。 connection.rollback();//回滚事务。下面通过示例代码展示一下:

package cn.itcast.jdbc;
 
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Savepoint;
import java.sql.Statement;
/**
 * 事务测试 
 */ 
public class test {
 
 public static void main(String[] args) throws SQLException {
 testTransaction();
 }
 
 static void testTransaction() throws SQLException {
 Connection conn = null;
 Statement st = null;
 ResultSet rs = null;
 Savepoint sp = null;
 try {
 conn = JdbcUtils.getConnection();
 //将事务设置成手动提交 
 conn.setAutoCommit(false);
 st = conn.createStatement();
 //id为1的人的Money减100
 String sql = "update user set money=money-100 where id=1";
 st.executeUpdate(sql);
 //设置回滚点(savepoint) 
 sp = conn.setSavepoint();
 //id为2的人的Money减100
 sql = "update user set money=money-100 where id=2";
 st.executeUpdate(sql);
 sql = "select money from user where id=2";
 rs = st.executeQuery(sql);
 float money = 0.0f;
 if (rs.next()) {
 money = rs.getFloat("money");
 }
 if (money > 300){
 throw new RuntimeException("已经超过最大值!");
 }
 //id为2的人的Money加100
 sql = "update user set money=money+100 where id=2";
 st.executeUpdate(sql);
 //提交事务 
 conn.commit();
 } catch (RuntimeException e) {
 if (conn != null && sp != null) {
 //回滚事务,注意里面的参数sp即为我们上面设置的savePoint,如果回滚的话只能回滚到savePoint以下的部分 
 //上面的部分不会得到回滚 
 conn.rollback(sp);
 conn.commit();
 }
 throw e;
 } catch (SQLException e) {
 if (conn != null)
 conn.rollback();
 throw e;
 } finally {
 //释放资源 
 JdbcUtils.free(rs, st, conn);
 }
 }
}

四:隔离级别

为了应对多线程并发读取数据时出现的问题,事务有了“隔离级别”特性,多线程并发读取数据一般会引发如下三个问题:

  1. 脏读(dirtyreads):指一个事务读取了另外一个事务未提交的数据。假设A事务读取B事务尚未提交的数据,此时如果B事务发生错误并执行回滚操作,那么A事务读取到的数据就是脏数据。 2.不可重复读(non-repeatablereads):一个事务重新读取前面读取过的数据, 发现该数据已经被另一个已提交的事务修改过。假如事务A在执行读取操作,由整个事务A比较大,前后读取同一条数据需要经历很长的时间 。而在事务A第一次读取数据,比如此时读取了小明的年龄为20岁,事务B执行更改操作,将小明的年龄更改为30岁,此时事务A第二次读取到小明的年龄时,发现其年龄是30岁,和之前的数据不一样了,也就是数据不重复了,系统不可以读取到重复的数据,称为不可重复读。3.幻读(phantomread):是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。假设事务A在执行读取操作,需要两次统计数据的总量,前一次查询数据总量后,此时事务B执行了新增数据的操作并提交后,这个时候事务A读取的数据总量和之前统计的不一样,就像产生了幻觉一样,平白无故的多了几条数据,称为成为幻读。会发现,幻读和不可重复读是十分相似的,以致于很多人很难分辨它们,你只需要知道,它们最大的区别是:不可重复读读取到的是更新(update)数据,而幻读读取到的是插入(insert)数据。为了处理上面的读数据问题,java事务提供了4种隔离级别。
  2. 可串行化(Serializable):可避免脏读、不可重复读、虚读情况的发生。
  3. 可重复读(Repeatableread):可避免脏读、不可重复读情况的发生。不可以避免虚读。3.读已提交(Readcommitted):可避免脏读情况发生。4.读未提交(Read uncommitted):最低级别,以上情况均无法保证。

1表示有,0表示无

隔离级别脏读不可重复读幻读读未提交(Read uncommitted)111读已提交(Readcommitted)011可重复读(Repeatableread)001可串行化(Serializable)000

隔离级别由高到低排列:可串行化>可重复读>读已提交>读未提交。通常情况下,数据库都有自己的默认隔离级别,我们使用spring框架可以指定隔离级别,但是如果指定了数据库不支持的隔离级别,数据库就会使用自己默认的。在Oracle数据库中,默认隔离级别是Read committed,而另一个常用数据库MySQL中,默认隔离级别是Repeatable read。下面,我们用mysql的例子说明各个隔离级别的情况:开启两个命令行客户端分别为A,B;不断改变A的隔离级别,在B端修改数据。实际步骤同序号。

1.读未提交(最低的隔离级别):

「原创」浅谈java事务及隔离级别

 


「原创」浅谈java事务及隔离级别

 

  1. 读已提交:
「原创」浅谈java事务及隔离级别

 

  1.  
「原创」浅谈java事务及隔离级别

 

  1.  
「原创」浅谈java事务及隔离级别

 

  1.  
  2. 可重复读
「原创」浅谈java事务及隔离级别

 

  1.  
「原创」浅谈java事务及隔离级别

 

  1.  
「原创」浅谈java事务及隔离级别

 

  1.  

值得一提的是,如果在客户端A中接着执行update num= num + 1 where id = 1,num没有变成1+1=2,而是步骤(2)中更新过后的num=10来算的,所以是10 + 1 = 11,数据的一致性倒是没有被破坏。可重复读的隔离级别下使用了MVCC机制,select操作不会更新版本号,是快照读(历史版本);insert、update和delete会更新版本号,是当前读(当前版本)。

4.串行化

「原创」浅谈java事务及隔离级别

 


「原创」浅谈java事务及隔离级别

 

mysql中事务隔离级别为serializable时会锁表,若一个事务来查询同一份数据就必须等待,直到前一个事务完成并解除锁定为止,因此不会出现幻读的情况,这种隔离级别并发性极低,开发中很少会用到。

五、总结

事务控制是构建J2EE应用不可缺少的一部分,合理选择应用何种事务对整个应用系统来说至关重要。一般说来,在单个JDBC 连接连接的情况下可以选择JDBC事务,在跨多个连接或者数据库情况下,需要选择使用JTA事务,如果用到了EJB,则可以考虑使用EJB容器事务,有兴趣的朋友可以关注一下。对于隔离级别来说,读未提交、读已提交和可重复读这三种隔离级别隔离的是行数据,他们的不同只是对应读、写之间的锁定关系不同而已,读未提交,事务进行写操作时并没有锁定禁止读的动作;读已提交在进行事务在进行写操作时锁定了行数据,禁止在写期间读数据;而可重复读则是在读期间禁止数据的写;串行化即锁定整个表的读写。希望读者能够合理选择并使用它们。

声明:本站部分内容来自互联网,如有版权侵犯或其他问题请与我们联系,我们将立即删除或处理。
▍相关推荐
更多资讯 >>>