1.事務概述
- 什麼是事務
一件事情有n個組成單元,要麼這n個組成單元同時成功,要麼n個單元同時失敗。java
就是將n個組成單元放到一個事務中。mysql
- mysql的事務
默認的事務:一條sql語句就是一個事務,默認開啓事務並提交面試
- 手動事務:
1)顯式的開啓一個事務:start transactionsql
2)事務提交:commit表明從開啓事務到提交事務,中間全部的sql都認爲有效----真正的更新數據庫數據庫
3)事務的回滾:rollback 表明事務的回滾,從開啓事務到事務回滾,中間的全部的sql操做都認爲無效,數據庫沒有被更新安全
2.JDBC的事務操做
默認是自動事務:session
執行sql語句:executeUpdate() ---- 每執行一次executeUpdate方法,表明事務自動提交併發
經過jdbc的API手動操做事務:oracle
(1)開啓事務:conn.setAutoCommit(false);工具
(2)提交事務:conn.commit();
(3)回滾事務:conn.rollback();
注意:控制事務的connnection必須是同一個(執行sql的connection與開啓事務的connnection必須是同一個才能對事務進行控制)
3.DBUtils的事務操做
3.1 QueryRunner
有參構造方法:QueryRunner runner = new QueryRunner(DataSource dataSource);
有參構造方法將數據源(鏈接池)做爲參數傳入QueryRunner,QueryRunner會從鏈接池中得到一個數據庫鏈接資源connection來操做數據庫,因此直接使用無Connection參數的update方法便可操做數據庫。runner.update(String sql,.....)
無參構造方法:QueryRunner runner = new QueryRunner();
無參的構造方法沒有將數據源(鏈接池)做爲參數傳入QueryRunner,那麼咱們在使用QueryRunner對象操做數據庫時,要使用有Connection參數的方法。runner.update(Connection conn,String sql,......)
4.使用ThreadLocal綁定鏈接資源
a方法裏調用b方法,b方法又調用c方法,整個過程當中,都屬於一個線程,ThreadLocal是這個線程裏的公共區域,由於控制事務須要同一個connection,所以ThreadLocal裏存放connection來控制事務。ThreadLocal是一個Map類型的鍵值對
案例:轉帳
//封裝在工具類裏 private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>(); //...... // 獲取鏈接對象 public static Connection getConnection() throws SQLException { Connection con = tl.get();//得到當前線程 if (con == null) {//若是沒有connection,就從鏈接池得到get一個,並set放入當前線程 con = dataSource.getConnection(); tl.set(con); } return con; } // 開啓事務 public static void startTransaction() throws SQLException { Connection con = getConnection(); if (con != null) { con.setAutoCommit(false); } } // 事務回滾 public static void rollback() throws SQLException { Connection con = getConnection(); if (con != null) { con.rollback(); } } // 提交而且 關閉資源及從ThreadLocall中釋放 public static void commitAndRelease() throws SQLException { Connection con = getConnection(); if (con != null) { con.commit(); // 事務提交 con.close();// 關閉資源 tl.remove();// 從線程綁定中移除 } }
public class TransferService { public boolean transferMoney(String out, String in, double money) { boolean b=true; TransferDao dao=new TransferDao(); try { MyDataSourceUtils.startTransaction(); dao.out(out,money); // int i=1/0; dao.in(in,money); } catch (Exception e) { b=false; try { MyDataSourceUtils.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } e.printStackTrace(); }finally { try { MyDataSourceUtils.commitAndRelease(); } catch (SQLException e) { e.printStackTrace(); } } return b; } }
public class TransferDao { public void out(String out, double money) throws SQLException { QueryRunner runner=new QueryRunner(); String sql="update account set money=money-? where name=?"; Connection conn = MyDataSourceUtils.getConnection(); runner.update(conn, sql, money,out); } public void in(String in, double money) throws SQLException { QueryRunner runner=new QueryRunner(); //updata找不到參數不會報錯,而是返回int,因此name錯誤依然顯示轉帳成功 String sql="update account set money=money+? where name=?"; Connection conn = MyDataSourceUtils.getConnection(); runner.update(conn,sql, money,in); } }
5.事務的特性和隔離級別(概念性問題---面試可能會有)
5.1事務的特性:ACID
1)原子性(Atomicity):原子性是指事務是一個不可分割的工做單位,事務中的操做要麼都發生,要麼都不發生。
2)一致性(Consistency):一個事務中,事務先後數據的完整性必須保持一致。(數據未提交時查詢可能查詢結果不一樣)
3)隔離性(Isolation):多個事務,事務的隔離性是指多個用戶併發訪問數據庫時,一個用戶的事務不能被其它用戶的事務所幹擾,多個併發事務之間數據要相互隔離。
4)持久性(Durability):持久性是指一個事務一旦被提交,它對數據庫中數據的改變就是永久性的,接下來即便數據庫發生故障也不該該對其有任何影響。
5.2併發訪問問題----由隔離性引發
若是不考慮隔離性,事務存在三種併發訪問問題:
- 髒讀:B事務讀取到了A事務還沒有提交的數據 ------ 要求B事務要讀取A事務提交的數據
- 不可重複讀:一個事務中,兩次讀取的數據的內容不一致 ----- 要求的是一個事務中屢次讀取時數據是一致的 --- update
- 幻讀/虛讀:一個事務中,兩次讀取的數據的數量不一致 ----- 要求在一個事務中屢次讀取的數據的數量是一致的 --insert delet
5.3事務的隔離級別
- read uncommitted : 讀取還沒有提交的數據 :哪一個問題都不能解決
- read committed:讀取已經提交的數據 :能夠解決髒讀 ---- oracle默認的
- repeatable read:重讀讀取:能夠解決髒讀 和 不可重複讀 ---mysql默認的
- serializable:串行化:能夠解決 髒讀 不可重複讀 和 虛讀---至關於鎖表(多個訪問對數據庫的操做必須依次進行,(註冊)不多用)
注意:mysql數據庫默認的隔離級別
查看mysql數據庫默認的隔離級別:select @@tx_isolation
設置mysql的隔離級別:set session transaction isolation level 事務隔離級別(4種)
隔離級別的性能: read uncommitted>read committed>repeatable read>serialazable
安全性: read uncommitted<read committed<repeatable read<serialazable