jdbc事务和事务的隔离级别

转载自   jdbc事务和事务的隔离级别

在jdbc的使用中以最简单的jdbc的使用为例,说明了jdbc的具体用法。然而在通常项目中,需要考虑更多内容,例如事务。
事务,在单个数据处理单元中,存在若干个数据处理,要么整体成功,要么整体失败。事务需要满足ACID属性(原子性、一致性、隔离性和持久性)。

  • 原子性:所谓原子性是指本次数据处理要么都提交、要么都不提交,即不能先提交一部分,然后处理其他的程序,然后接着提交未完成提交的剩余部分。概念类似于编程语言的原子操作。
  • 一致性:所谓一致性是指数据库数据由一个一致的状态在提交事务后变为另外一个一致的状态。例如,用户确认到货操作:确认前,订单状态为待签收、客户积分为原始积分,此状态为一致的状态;在客户确认到后后,订单状态为已完成、客户积分增加本次消费的积分,这两个状态为一致状态。不能出现,订单状态为待签收,客户积分增加或者订单状态为已完成,客户积分未增加的状态,这两种均为不一致的情况。一致性与原子性息息相关。
  • 隔离性:所谓隔离性是指事物与事务之间的隔离,即在事务提交完成前,其他事务与未完成事务的数据中间状态访问权限,具体可通过设置隔离级别来控制。
  • 持久性:所谓持久性是指本次事务提交完成或者回滚完成均为持久的修改,除非其他事务进行操作否则数据库数据不能发生改变。

本文重点描述事物隔离性及使用方法。
要详细说明数据库隔离级别,需要先对数据库并发事务可能出现的几种状态进行说明:

1、读脏:一个事务读取另外一个事务尚未提交的数据。如下图,线程thread1在事务中在time1时刻向库表中新增一条数据‘test’并在time3时刻回滚数据;线程thread2在time2时刻读取,若thread2读取到‘test’,则为读脏。

  

2、不可重新读:其他事务的操作导致某个事务两次读取数据不一致。如下图,线程thread1在事务中time1时刻将数据库中‘test’更新为‘00’,并在time3时刻提交;thread2在一个事务中分别在time2和time4两个时刻读取这条记录,若两次读取结果不同则为不可重读。(注意:1.不可重读针对已经提交的数据。2.两次或多次读取同一条数据。

 

3、幻读:其他事务的数据操作导致某个事务两次读取数据数量不一致。如下图,线程thread1在事务中time1时刻向数据库中新增‘00’,并在time3时刻提交;thread2在一个事务中分别在time2和time4两个时刻扫描库表,若两次读取结果不同则为幻读。(注意:1.幻读针对已经提交的数据。2.两次或多次读取不同行数据,数量上新增或减少。

针对上诉3中事务并发情况,jdbc定义了5中事务隔离级别:
- TRANSACTION_NONE 无事务
- TRANSACTION_READ_UNCOMMITTED 允许读脏,不可重读,幻读。
- TRANSACTION_READ_COMMITTED 直译为仅允许读取已提交的数据,即不能读脏,但是可能发生不可重读和幻读。
- TRANSACTION_REPEATABLE_READ 不可读脏,保证同一事务重复读取相同数据,但是可能发生幻读。
- TRANSACTION_SERIALIZABLE 直译为串行事务,保证不读脏,可重复读,不可幻读,事务隔离级别最高。

**> 注意:

  • 隔离级别对当前事务有效,例如若当前事务设置为TRANSACTION_READ_UNCOMMITTED,则允许当前事务对其他事务未提交的数据进行读脏,而非其他事务可对当前事务未提交的数据读脏。
  • 部分数据库不支持TRANSACTION_NONE,例如mysql。
  • 在TRANSACTION_SERIALIZABLE 隔离级别下,为先执行DML更新,再执行查询,此处为实验的结论。
  • 若未显示设置隔离级别,jdbc将采用数据库默认隔离级别。文中实验数据库的默认隔离级别为:**

以下将分别在各种事务隔离级别下,通过设置事务内访问间隔时间,模拟读脏、不可重读、幻读。

建立库表脚本如下:

CREATE TABLE `t_dict` (`dict_type` varchar(255) DEFAULT NULL,`dict_code` varchar(255) DEFAULT NULL,`dict_name` varchar(255) DEFAULT NULL,`dict_remark` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

主线程,用于建立数据库连接、设置隔离级别、打印输出等

package DBTest;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;public class DBTest {private String url ;private String user;private String password;/*** 创建数据连接* @return*/private Connection getCon(){Connection con = null;try{Class.forName("com.mysql.jdbc.Driver");url = "jdbc:mysql://localhost:3306/twork";user = "root";password = "root";con = DriverManager.getConnection(url, user, password);}catch (Exception e){e.printStackTrace();try {con.close();} catch (SQLException e1) {e1.printStackTrace();}}return con;}/*** 通过链接获取声明* @param con* @return*/private Statement getStat(Connection con){Statement state = null;try{state = con.createStatement();}catch(Exception e){e.printStackTrace();}return state;}/***  打印数据库所有数据*/public void selectAll(int transactionType){Connection con = null;Statement state = null;ResultSet rs = null;try{con = getCon();if(transactionType >= 0 ){con.setTransactionIsolation(transactionType);}System.out.println("-------------当前事务隔离级别为:"+con.getTransactionIsolation()+"-------------");state = getStat(con);rs = state.executeQuery("select * from t_dict");ResultSetMetaData rsmd = rs.getMetaData();for(int i = 1;i<= rsmd.getColumnCount() ;i++){System.out.print(rsmd.getColumnName(i)+"| ");}System.out.println();System.out.println("-------------------------------------------");//打印所有行while(rs.next()){for(int i = 1;i<= rsmd.getColumnCount() ;i++){System.out.print(rs.getString(i)+"|     ");}System.out.println();}}catch (Exception e){try {con.rollback();} catch (SQLException e1) {e1.printStackTrace();}e.printStackTrace();}finally {try {if(rs != null){rs.close();}} catch (SQLException e) {e.printStackTrace();}try {if(state != null){state.close();}} catch (Exception e){}try {if(con != null){con.close();}} catch (SQLException e) {e.printStackTrace();}}}/*** 新增一行* @param needExcepition* @param sleepTimes* @param values* @return*/public int insertOne(int needExcepition,int sleepTimes, List<String> values){Connection con = getCon();PreparedStatement pre = null;String sql = "INSERT INTO t_dict (dict_type, dict_code, dict_name, dict_remark) VALUES (?, ?, ?, ?)";int res = 0;try {con.setAutoCommit(false);pre = con.prepareStatement(sql);for(int i = 0; i < values.size() ;i++){pre.setString(i+1, values.get(i));}Thread.sleep(sleepTimes);System.out.println("before execute");res = pre.executeUpdate();System.out.println("after execute");Thread.sleep(sleepTimes);int i = 1/needExcepition;System.out.println("before commit");con.commit();System.out.println("after commit");} catch (Exception e) {try {System.out.println("before roll back");con.rollback();System.out.println("after roll back");res = 0;} catch (SQLException e1) {e1.printStackTrace();}e.printStackTrace();} finally {try {if(pre != null){pre.close();}} catch (Exception e){}try {if(con != null){con.close();}} catch (SQLException e) {e.printStackTrace();}}return res;}/*** 间隔一定时间读取多次* @param dictType 要去读取的数据类型* @param sleepTimes 每次读取之间的间隔时间* @param printTimes 打印次数* @param transactionType 事务隔离级别*/private void printMultiple(String dictType,int sleepTimes,int printTimes, int transactionType){Connection con = null;Statement state = null;ResultSet rs = null;try{con = getCon();con.setAutoCommit(false);if(transactionType >= 0){con.setTransactionIsolation(transactionType);}System.out.println("-------------当前事务隔离级别为:"+con.getTransactionIsolation()+"-------------");state = getStat(con);for (int j = 0; j < printTimes; j++) {Thread.sleep(sleepTimes);rs = state.executeQuery("select * from t_dict where dict_type = '"+dictType+"' ");ResultSetMetaData rsmd = rs.getMetaData();System.out.println("第"+(j+1)+"次读取");for(int i = 1;i<= rsmd.getColumnCount() ;i++){System.out.print(rsmd.getColumnName(i)+"| ");}System.out.println();System.out.println("-------------------------------------------");while(rs.next()){for(int i = 1;i<= rsmd.getColumnCount() ;i++){System.out.print(rs.getString(i)+"|     ");}System.out.println();}   }con.commit();}catch (Exception e){try {con.rollback();} catch (SQLException e1) {e1.printStackTrace();}e.printStackTrace();}finally {try {if(rs != null){rs.close();}} catch (SQLException e) {e.printStackTrace();}try {if(state != null){state.close();}} catch (Exception e){}try {if(con != null){con.close();}} catch (SQLException e) {e.printStackTrace();}}}/*** 更改一条数据的内容* @param dict_type* @param sleepTimes* @param values* @return*/public int updateOne(String dict_type,int sleepTimes, List<String> values){Connection con = null;PreparedStatement pre = null;String sql = "UPDATE t_dict  SET dict_code = ?, dict_name = ?, dict_remark = ? WHERE dict_type ='"+dict_type+"'";int res = 0;try {con = getCon();con.setAutoCommit(false);con.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);pre = con.prepareStatement(sql);for(int i = 0; i < values.size() ;i++){pre.setString(i+1, values.get(i));}Thread.sleep(sleepTimes);System.out.println("before execute ");res = pre.executeUpdate();System.out.println("after execute ");Thread.sleep(sleepTimes);System.out.println("before commit");con.commit();System.out.println("after commit");} catch (Exception e) {try {con.rollback();} catch (SQLException e1) {e1.printStackTrace();}e.printStackTrace();} finally {try {if(pre != null){pre.close();}} catch (Exception e){}try {if(con != null){con.close();}} catch (SQLException e) {e.printStackTrace();}}return res;}/*** @param transType*/public void testTransaction(int transType){intDate();System.out.println("-------------------读脏模拟---------------------");testDirty(transType);try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("-------------------不可重读模拟------------------");testRepeat(transType);try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("-------------------幻读模拟----------------------");testTrick(transType);}/*** 初始化数据*/private void intDate(){System.out.println("------------初始化数据 start-------------");Connection con = getCon();Statement pre = null;String sqlDelete = "delete from t_dict";String sqlInsert = "INSERT INTO `twork`.`t_dict` (`dict_type`, `dict_code`, `dict_name`, `dict_remark`) VALUES ('type0', '00', 'type00', 'type00')";try {con.setAutoCommit(false);pre = con.createStatement();pre.execute(sqlDelete);pre.execute(sqlInsert);con.commit();} catch (Exception e) {try {con.rollback();} catch (SQLException e1) {e1.printStackTrace();}e.printStackTrace();} finally {try {if(pre != null){pre.close();}} catch (Exception e){e.printStackTrace();}try {if(con != null){con.close();}} catch (SQLException e) {e.printStackTrace();}}System.out.println("------------初始化数据 end-------------");}/*** 模拟读脏,抛出未捕获异常,插入数据不提交*/public void testDirty(int transactionType){List<String> list = new ArrayList<String>();list.add("type1");list.add("11");list.add("type11");list.add("type11");TestThread testThread = new TestThread("insert",0,300,list);Thread thread = new Thread(testThread);thread.start();try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}selectAll(transactionType);}/*** 模拟幻读,第N次读取多出数据*/public void testTrick(int transactionType){List<String> list = new ArrayList<String>();list.add("type0");list.add("11");list.add("type11");list.add("type11");//执行插入,不产生异常TestThread testThread = new TestThread("insert",1,400,list);Thread thread = new Thread(testThread);thread.start();//打印两次printMultiple("type0", 300,4,transactionType);}/*** 模拟不可重读,多次读取同一条记录,记录被更改*/public void testRepeat(int transactionType){List<String> list = new ArrayList<String>();list.add("type0");list.add("11");list.add("type11");list.add("type11");//执行插入,不产生异常TestThread testThread = new TestThread("update",1,400,list);Thread thread = new Thread(testThread);thread.start();//打印4次printMultiple("type0", 300,4,transactionType);}public static void main(String[] args){DBTest dbTest = new DBTest();/*分别执行下面的方法,即可模拟各个隔离级别下,线程并发事务间的访问结果*/System.out.println(" -----------------------TRANSACTION_READ_UNCOMMITTED test start------------------------");dbTest.testTransaction(Connection.TRANSACTION_READ_UNCOMMITTED);//      System.out.println(" -----------------------TRANSACTION_READ_COMMITTED test start------------------------");
//      dbTest.testTransaction(Connection.TRANSACTION_READ_COMMITTED);
//      
//      System.out.println(" -----------------------TRANSACTION_REPEATABLE_READ test start------------------------");
//      dbTest.testTransaction(Connection.TRANSACTION_REPEATABLE_READ);
//      
//      System.out.println(" -----------------------TRANSACTION_SERIALIZABLE test start------------------------");
//      dbTest.testTransaction(Connection.TRANSACTION_SERIALIZABLE);
//      
//      System.out.println(" -----------------------default test start------------------------");
//      dbTest.testTransaction(-1);}}    

并发线程,调用读取方法

package DBTest;import java.util.ArrayList;
import java.util.List;/** 
* Created by ygl on 2016/5/1. 
*/ 
public class TestThread implements Runnable {int needException = 1;int sleepTimes = 0;List<String> list = new ArrayList<String>();String method = "";DBTest dbTest = new DBTest();/*** @param method insert 或 update* @param needException 是否需要抛出异常,0抛出异常,1不抛出异常* @param sleepTimes 线程睡眠时间(毫秒)* @param list 更新数据库的数据,当method为update时,list的第一个元素为条件,其他为更新内容*/public TestThread(String method,int needException, int sleepTimes , List<String> list){this.needException = needException;this.sleepTimes = sleepTimes;this.list = list;this.method = method;}public void run(){if("insert".equals(method)){insert();} else if("update".equals(method)){update();}}private void insert(){int res = dbTest.insertOne(needException, sleepTimes, list);if(res == 1){System.out.println("insert success");}else{System.out.println("insert fail");}}private void update(){String updateKey = list.get(0);list.remove(0);int res = dbTest.updateOne(updateKey, sleepTimes, list);if(res == 1){System.out.println("update success");}else{System.out.println("update fail");}}
}

读者可以使用上述程序分别测试,这里仅以TRANSACTION_READ_UNCOMMITTED为例,输出结果为:

 -----------------------TRANSACTION_READ_UNCOMMITTED test start------------------------
------------初始化数据 start-------------------------初始化数据 end-------------
-------------------读脏模拟---------------------before execute
after execute-------------当前事务隔离级别为:1-------------
dict_type| dict_code| dict_name| dict_remark| 
-------------------------------------------
type0|     00|     type00|     type00|     
type1|     11|     type11|     type11|     
before roll back
after roll back
java.lang.ArithmeticException: / by zeroat DBTest.DBTest.insertOne(DBTest.java:141)at DBTest.TestThread.insert(TestThread.java:41)at DBTest.TestThread.run(TestThread.java:34)at java.lang.Thread.run(Unknown Source)
insert fail
-------------------不可重读模拟-------------------------------当前事务隔离级别为:1-------------
第1次读取
dict_type| dict_code| dict_name| dict_remark| 
-------------------------------------------
type0|     00|     type00|     type00|     
before execute 
after execute 
第2次读取
dict_type| dict_code| dict_name| dict_remark| 
-------------------------------------------
type0|     11|     type11|     type11|     
before commit
after commit
update success
第3次读取
dict_type| dict_code| dict_name| dict_remark| 
-------------------------------------------
type0|     11|     type11|     type11|     
第4次读取
dict_type| dict_code| dict_name| dict_remark| 
-------------------------------------------
type0|     11|     type11|     type11|     
-------------------幻读模拟-----------------------------------当前事务隔离级别为:1-------------
第1次读取
dict_type| dict_code| dict_name| dict_remark| 
-------------------------------------------
type0|     11|     type11|     type11|     
before execute
after execute
第2次读取
dict_type| dict_code| dict_name| dict_remark| 
-------------------------------------------
type0|     11|     type11|     type11|     
type0|     11|     type11|     type11|     
before commit
第3次读取
dict_type| dict_code| dict_name| dict_remark| 
-------------------------------------------
type0|     11|     type11|     type11|     
type0|     11|     type11|     type11|     
after commit
insert success
第4次读取
dict_type| dict_code| dict_name| dict_remark| 
-------------------------------------------
type0|     11|     type11|     type11|     
type0|     11|     type11|     type11|    

--------------------- 本文来自 光礼同学 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/crow_feiyu/article/details/51305826?utm_source=copy

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/325862.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android自定义View画钟表

第一种使用背景表盘 主要代码&#xff1a; package com.zjs.zidingyiview; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.os.Handler; import android.os.Message; impo…

理解C# 4 dynamic(3) – DynamicObject的使用

上篇文章"理解C# 4 dynamic(2) – ExpandoObject的使用" 了解了xpandoObject的基本使用。但ExpandoObject的问题就是它是一个万金油&#xff0c;什么都可以做&#xff0c;但是又都不专注。使用DynamicObject正好可以解决这个问题。这篇文章介绍DynamicJson是如何继承…

jsx中的注释的写法

{}表示里面要写js语句 js语句里面的注释为/**/

微软正在用实际行动告诉你: 拥抱开源,微软是认真的

2017年4月19日至20日&#xff0c;由工业和信息化部指导、中国信息通信研究院主办、云计算开源产业联盟承办的"全球云计算开源峰会"在国家会议中心举行。微软.NET CORE开发平台荣获由峰会云计算开源产业联盟(OSCAR) 评选出的“尖峰开源技术”奖&#xff0c;标志着国内…

React不提交表单并且获取表单中的数据

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>1_非受控组件</title> </head> <body><!-- 准备好一个“容器” --><div id"test"></div><!-- 引入…

分布式系统Paxos算法

转载自 分布式系统Paxos算法 这是一个有关Paxos算法非常形象的讲解与示范。Paxos是能够基于一大堆完全不可靠的网络条件下却能可靠确定地实现共识一致性的算法。也就是说&#xff1a;它允许一组不一定可靠的处理器&#xff08;服务器&#xff09;在某些条件得到满足情况下就能…

winform实现简单的计算器V1版本

最近在整winform程序&#xff0c;就做了些简单的案例出来&#xff0c;比如说下面的这个计算器&#xff1a; 这个的实现方式还是比较简单的。 首先按照图中的界面从工具箱中拉出来一个窗体&#xff0c;其中的显示结果“86”“1849”也是lable控件&#xff0c;最后放一个计算的…

用.netcore写一个简单redis驱动,调试windows版本的redis

1. 下载windows版本的redis 2.开发环境vs2017 新建一个 .net core控制台。 private static Socket socket new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); private static BufferedStream buffer null; socket.NoDelay true; s…

分布式系统的Raft算法

转载自 分布式系统的Raft算法 过去, Paxos一直是分布式协议的标准&#xff0c;但是Paxos难于理解&#xff0c;更难以实现&#xff0c;Google的分布式锁系统Chubby作为Paxos实现曾经遭遇到很多坑。 来自Stanford的新的分布式协议研究称为Raft&#xff0c;它是一个为真实世界应…

拆分:分解单块系统——《微服务设计》读书笔记

通常&#xff0c;我们可能已有有一个巨大的单块系统&#xff0c;如何实现微服务&#xff0c;我们需要把它分解。 从哪里开始拆分&#xff1a;接缝 接缝&#xff1a;从接缝处可以抽取相对独立的一部分代码&#xff0c;对这部分代码的修改不会影响系统的其他部分。这些接缝就可以…

winfrom实现简单计算器V2版本

前面&#xff0c;使用winform实现了个简单的计算器&#xff0c;今天&#xff0c;再来给大家看一个稍微复杂点的计算器&#xff0c;效果图如下&#xff1a; 包括归零&#xff0c;退格&#xff0c;加减乘除都已经实现&#xff0c;如果想要继续扩展的可以在稍微改改即可。 现在看…

ACID中C与CAP定理中C的区别

转载自 ACID中C与CAP定理中C的区别 ACID和CAP定理中都有C&#xff0c;代表Consistent一致性&#xff0c;很多人容易将这两个C混为一谈&#xff0c;其实这两个一致性是有区别的。 事务的定义是一系列操作要么全部成功&#xff0c;要么全部不成功&#xff0c;数据库的事务机制是…

winform通过ListView绑定数据库数据源

来&#xff0c;我们开始拉窗体&#xff0c;和我一样的这个就可以&#xff1a; 很简单&#xff0c;在窗体里面只放一个ListView控件即可&#xff0c;然后点击ListView的属性Columns 分别在Text里面写用户名和密码&#xff0c;点击确定。 然后设置显示视图View为Details&…

理解C# 4 dynamic(4) – 让人惊艳的Clay

一&#xff0c;多种方式初始化对象 1, 最简单的对象构建和初始化 dynamic New new ClayFactory();var person New.Person(); person.FirstName "Louis"; person.LastName "Dejardin"; 注意这里的Person并不是一个具体的&#xff0c;实际存在的类或者…

配置struts.xml时extends=struts-default会报错,原因和解决

提示&#xff1a;此种解决方法只适用于Intellij IDEA&#xff0c;MyEclipse或者Eclipse还得另寻它法&#xff0c;但估计原因应该是类似的。 在Intellij IDEA 2017使用Struts2框架时&#xff0c;若新建项目时并未导入Struts2框架而是在后期手动新建lib目录导入Struts2框架后&…

React生命周期(新)

三个标红的需要前面加上UNSAFE_ 三个常用的组件

发力企业级市场,微软Hololens开辟了一条VR新道路

近日微软Hololens可谓是动作频频&#xff0c;2月份Hololens与BGC Engineering合作&#xff0c;从矿山规划到泥石流建模&#xff0c;该应用程序可帮助BGC Engineering及其客户可视化一个场景并解决工程问题。 3月份HoloLens与Cigna展开医疗合作&#xff0c;将Hololens用于健康检…

写给工程师的10条精进原则

转载自 写给工程师的10条精进原则 引言 时间回到8年前&#xff0c;我人生中的第一份实习工作&#xff0c;是在某互联网公司的无线搜索部做一个C工程师。当时的我可谓意气风发&#xff0c;想要大干一场&#xff0c;结果第一次上线就写了人生中第一个CaseStudy。由于对部署环境…