日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

jdbc事务和事务的隔离级别

發(fā)布時間:2023/12/3 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 jdbc事务和事务的隔离级别 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

轉(zhuǎn)載自? ?jdbc事務(wù)和事務(wù)的隔離級別

在jdbc的使用中以最簡單的jdbc的使用為例,說明了jdbc的具體用法。然而在通常項目中,需要考慮更多內(nèi)容,例如事務(wù)。
事務(wù),在單個數(shù)據(jù)處理單元中,存在若干個數(shù)據(jù)處理,要么整體成功,要么整體失敗。事務(wù)需要滿足ACID屬性(原子性、一致性、隔離性和持久性)。

  • 原子性:所謂原子性是指本次數(shù)據(jù)處理要么都提交、要么都不提交,即不能先提交一部分,然后處理其他的程序,然后接著提交未完成提交的剩余部分。概念類似于編程語言的原子操作。
  • 一致性:所謂一致性是指數(shù)據(jù)庫數(shù)據(jù)由一個一致的狀態(tài)在提交事務(wù)后變?yōu)榱硗庖粋€一致的狀態(tài)。例如,用戶確認(rèn)到貨操作:確認(rèn)前,訂單狀態(tài)為待簽收、客戶積分為原始積分,此狀態(tài)為一致的狀態(tài);在客戶確認(rèn)到后后,訂單狀態(tài)為已完成、客戶積分增加本次消費的積分,這兩個狀態(tài)為一致狀態(tài)。不能出現(xiàn),訂單狀態(tài)為待簽收,客戶積分增加或者訂單狀態(tài)為已完成,客戶積分未增加的狀態(tài),這兩種均為不一致的情況。一致性與原子性息息相關(guān)。
  • 隔離性:所謂隔離性是指事物與事務(wù)之間的隔離,即在事務(wù)提交完成前,其他事務(wù)與未完成事務(wù)的數(shù)據(jù)中間狀態(tài)訪問權(quán)限,具體可通過設(shè)置隔離級別來控制。
  • 持久性:所謂持久性是指本次事務(wù)提交完成或者回滾完成均為持久的修改,除非其他事務(wù)進(jìn)行操作否則數(shù)據(jù)庫數(shù)據(jù)不能發(fā)生改變。

本文重點描述事物隔離性及使用方法。
要詳細(xì)說明數(shù)據(jù)庫隔離級別,需要先對數(shù)據(jù)庫并發(fā)事務(wù)可能出現(xiàn)的幾種狀態(tài)進(jìn)行說明:

1、讀臟:一個事務(wù)讀取另外一個事務(wù)尚未提交的數(shù)據(jù)。如下圖,線程thread1在事務(wù)中在time1時刻向庫表中新增一條數(shù)據(jù)‘test’并在time3時刻回滾數(shù)據(jù);線程thread2在time2時刻讀取,若thread2讀取到‘test’,則為讀臟。

??

2、不可重新讀:其他事務(wù)的操作導(dǎo)致某個事務(wù)兩次讀取數(shù)據(jù)不一致。如下圖,線程thread1在事務(wù)中time1時刻將數(shù)據(jù)庫中‘test’更新為‘00’,并在time3時刻提交;thread2在一個事務(wù)中分別在time2和time4兩個時刻讀取這條記錄,若兩次讀取結(jié)果不同則為不可重讀。(注意:1.不可重讀針對已經(jīng)提交的數(shù)據(jù)。2.兩次或多次讀取同一條數(shù)據(jù)。

?

3、幻讀:其他事務(wù)的數(shù)據(jù)操作導(dǎo)致某個事務(wù)兩次讀取數(shù)據(jù)數(shù)量不一致。如下圖,線程thread1在事務(wù)中time1時刻向數(shù)據(jù)庫中新增‘00’,并在time3時刻提交;thread2在一個事務(wù)中分別在time2和time4兩個時刻掃描庫表,若兩次讀取結(jié)果不同則為幻讀。(注意:1.幻讀針對已經(jīng)提交的數(shù)據(jù)。2.兩次或多次讀取不同行數(shù)據(jù),數(shù)量上新增或減少。

針對上訴3中事務(wù)并發(fā)情況,jdbc定義了5中事務(wù)隔離級別:
- TRANSACTION_NONE 無事務(wù)
- TRANSACTION_READ_UNCOMMITTED 允許讀臟,不可重讀,幻讀。
- TRANSACTION_READ_COMMITTED 直譯為僅允許讀取已提交的數(shù)據(jù),即不能讀臟,但是可能發(fā)生不可重讀和幻讀。
- TRANSACTION_REPEATABLE_READ 不可讀臟,保證同一事務(wù)重復(fù)讀取相同數(shù)據(jù),但是可能發(fā)生幻讀。
- TRANSACTION_SERIALIZABLE 直譯為串行事務(wù),保證不讀臟,可重復(fù)讀,不可幻讀,事務(wù)隔離級別最高。

**> 注意:

  • 隔離級別對當(dāng)前事務(wù)有效,例如若當(dāng)前事務(wù)設(shè)置為TRANSACTION_READ_UNCOMMITTED,則允許當(dāng)前事務(wù)對其他事務(wù)未提交的數(shù)據(jù)進(jìn)行讀臟,而非其他事務(wù)可對當(dāng)前事務(wù)未提交的數(shù)據(jù)讀臟。
  • 部分?jǐn)?shù)據(jù)庫不支持TRANSACTION_NONE,例如mysql。
  • 在TRANSACTION_SERIALIZABLE 隔離級別下,為先執(zhí)行DML更新,再執(zhí)行查詢,此處為實驗的結(jié)論。
  • 若未顯示設(shè)置隔離級別,jdbc將采用數(shù)據(jù)庫默認(rèn)隔離級別。文中實驗數(shù)據(jù)庫的默認(rèn)隔離級別為:**

以下將分別在各種事務(wù)隔離級別下,通過設(shè)置事務(wù)內(nèi)訪問間隔時間,模擬讀臟、不可重讀、幻讀。

建立庫表腳本如下:

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;

主線程,用于建立數(shù)據(jù)庫連接、設(shè)置隔離級別、打印輸出等

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;/*** 創(chuàng)建數(shù)據(jù)連接* @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;}/*** 打印數(shù)據(jù)庫所有數(shù)據(jù)*/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("-------------當(dāng)前事務(wù)隔離級別為:"+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 要去讀取的數(shù)據(jù)類型* @param sleepTimes 每次讀取之間的間隔時間* @param printTimes 打印次數(shù)* @param transactionType 事務(wù)隔離級別*/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("-------------當(dāng)前事務(wù)隔離級別為:"+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();}}}/*** 更改一條數(shù)據(jù)的內(nèi)容* @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);}/*** 初始化數(shù)據(jù)*/private void intDate(){System.out.println("------------初始化數(shù)據(jù) 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("------------初始化數(shù)據(jù) end-------------");}/*** 模擬讀臟,拋出未捕獲異常,插入數(shù)據(jù)不提交*/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次讀取多出數(shù)據(jù)*/public void testTrick(int transactionType){List<String> list = new ArrayList<String>();list.add("type0");list.add("11");list.add("type11");list.add("type11");//執(zhí)行插入,不產(chǎn)生異常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");//執(zhí)行插入,不產(chǎn)生異常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();/*分別執(zhí)行下面的方法,即可模擬各個隔離級別下,線程并發(fā)事務(wù)間的訪問結(jié)果*/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);}}

并發(fā)線程,調(diào)用讀取方法

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 更新數(shù)據(jù)庫的數(shù)據(jù),當(dāng)method為update時,list的第一個元素為條件,其他為更新內(nèi)容*/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為例,輸出結(jié)果為:

-----------------------TRANSACTION_READ_UNCOMMITTED test start------------------------ ------------初始化數(shù)據(jù) start-------------------------初始化數(shù)據(jù) end------------- -------------------讀臟模擬---------------------before execute after execute-------------當(dāng)前事務(wù)隔離級別為: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 -------------------不可重讀模擬-------------------------------當(dāng)前事務(wù)隔離級別為: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| -------------------幻讀模擬-----------------------------------當(dāng)前事務(wù)隔離級別為: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|

--------------------- 本文來自 光禮同學(xué) 的CSDN 博客 ,全文地址請點擊:https://blog.csdn.net/crow_feiyu/article/details/51305826?utm_source=copy

總結(jié)

以上是生活随笔為你收集整理的jdbc事务和事务的隔离级别的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。