一、需求背景:
我們生活經常遇到一個情況:在購買商品的時候,已經支付的了,那么商品應該處於已購買訂單里。而不是付款之后,已購買商品沒有。
還有轉賬的時候,轉出方和轉入方都需要扣減相應的金額,而不是一方減少或者增加。
因為上面的例子都是對數據操作,所以需要我們操作數據庫的事務。
如何確定一個事務范圍?
事務是由一系列數據庫操作組成,他和業務場景有關。當操作完成的時候,如果操作過程沒有出現失敗,則這個事務產生的數據庫操作一並提交(commit)。反之,如果出現失敗,則一並回滾(rollback)。
所有事務和業務層(service)確定。
事務的特點:
ACID
1、原子性:事務里面的操作單元不可切割,要么全部成功,要么全部失敗
2、一致性:事務執行前后,業務狀態和其他業務狀態保持一致.
3、隔離性:一個事務執行的時候最好不要受到其他事務的影響
4、持久性:一旦事務提交或者回滾.這個狀態都要持久化到數據庫中
不考慮隔離性會出現的讀問題:
臟讀:在一個事務中讀取到另一個事務沒有提交的數據
不可重復讀:在一個事務中,兩次查詢的結果不一致(針對的update操作)
虛讀(幻讀):在一個事務中,兩次查詢的結果不一致(針對的insert操作)
通過設置數據庫的隔離級別來避免上面的問題(理解)
read uncommitted 讀未提交 上面的三個問題都會出現
read committed 讀已提交 可以避免臟讀的發生
repeatable read 可重復讀 可以避免臟讀和不可重復讀的發生
serializable 串行化 可以避免所有的問題
兩種方式:
1)數據庫上控制事務:
默認情況下,mysql數據庫中的事務是自動提交。
1 mysql> show variables like "%autocommit%"; 2 +---------------+-------+ 3 | Variable_name | Value | 4 +---------------+-------+ 5 | autocommit | ON | 6 +---------------+-------+ 7 1 row in set (0.00 sec
我們可以在數據庫上進行事務的開啟和關閉。但是這種方式顯然不好。
2)java代碼實現。
查看connection的方法:
1:void commit() 提交事務。Makes all changes made since the previous commit/rollback permanent and releases any database locks currently held by this Connection object.
2:void rollback()回滾事務.Undoes all changes made in the current transaction and releases any database locks currently held by this Connection object
3:void setAutocommit(boolean ) false 表示開啟事務,true表示事務關閉。Sets this connection's auto-commit mode to the given state.
注意:事務要作用同一個數據庫連接。
java代碼實現有兩種方式:
1、自上到下傳遞參數:
通過service傳遞到dao層的connection的變量,保證整個過程使用的是同一個數據連接。
2、當前線程綁定事務:
通過綁定當前線程一個變量(connection),在dao中獲取這個connection,保證整個事務的過程中,使用的是同一個連接。
其中threadLocal方法:ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID)
該字段在類中要以private static 來進行修飾。
1) T get() 返回當前線程綁定的值。
2)void remove() 從當前線程解綁。
3)void set(T value) 綁定當前線程。
內部實現原理就是map方法對currentThread 進行put 和set remove。
jdbc工具類:
1 package jd.com.tool_jdbc; 2 3 import org.apache.commons.dbcp2.BasicDataSource; 4 import org.apache.commons.dbcp2.BasicDataSourceFactory; 5 6 7 import java.io.InputStream; 8 import java.sql.Connection; 9 import java.sql.PreparedStatement; 10 import java.sql.SQLException; 11 import java.util.Properties; 12 13 14 15 public class ds_tool { 16 private static BasicDataSource ds=null; 17 private static Connection con=null; 18 private static ThreadLocal<Connection> threadLocal=new ThreadLocal<>(); 19 public static BasicDataSource getDs(){ 20 try { 21 InputStream inp= ds_tool.class.getClassLoader().getResourceAsStream("database.properties"); 22 Properties pro=new Properties(); 23 pro.load(inp); 24 ds=new BasicDataSourceFactory().createDataSource(pro); 25 ds.setInitialSize(4); 26 ds.setMaxTotal(8); 27 ds.setMaxIdle(2); 28 return ds; 29 }catch (Exception ex){ 30 throw new RuntimeException("初始化數據庫失敗"+ex); 31 } 32 33 } 34 //給當前線程綁定變量。 35 public static BasicDataSource setDs(){ 36 try { 37 if(con==null){ 38 ds=getDs(); 39 con=ds.getConnection(); 40 threadLocal.set(con); 41 }else{ 42 threadLocal.set(con); 43 } 44 return ds; 45 }catch (Exception ex){ 46 ex.printStackTrace(); 47 throw new RuntimeException("初始化錯誤"+ex); 48 } 49 50 } 51 //開啟事務 52 public static void openEven(){ 53 try { 54 setDs(); 55 con=threadLocal.get(); 56 con.setAutoCommit(false); 57 }catch (Exception ex){ 58 ex.printStackTrace(); 59 } 60 61 } 62 //提交事務 63 public static void commEven(){ 64 try { 65 con=threadLocal.get(); 66 con.commit(); 67 }catch (Exception ex){ 68 ex.printStackTrace(); 69 } 70 71 } 72 //回滾事務 73 public static void rollbackEvent(){ 74 try { 75 con=threadLocal.get(); 76 System.out.println(con); 77 con.rollback(); 78 }catch (Exception ex){ 79 ex.printStackTrace(); 80 } 81 82 } 83 public static void closedDs(BasicDataSource ds, PreparedStatement pre){ 84 try { 85 ds.close(); 86 pre.close(); 87 threadLocal.remove(); 88 }catch (Exception ex){ 89 ex.printStackTrace(); 90 } 91 92 } 93 94 }
然后在業務層上進行事務的控制。
3)1、queryRunner()初始化的時候,不帶參數,是默認是開啟事務。
在執行的時候,update(connection con sql object ...parm)傳入connection。需要關閉資源 connection。
2、如果quryRunner(Datasource ds)的時候,內部代碼會幫我們實現資源回收。不需要connection.close()
