java操作數據庫的事務支持


一、需求背景:

我們生活經常遇到一個情況:在購買商品的時候,已經支付的了,那么商品應該處於已購買訂單里。而不是付款之后,已購買商品沒有。

還有轉賬的時候,轉出方和轉入方都需要扣減相應的金額,而不是一方減少或者增加。

因為上面的例子都是對數據操作,所以需要我們操作數據庫的事務。

如何確定一個事務范圍?

事務是由一系列數據庫操作組成,他和業務場景有關。當操作完成的時候,如果操作過程沒有出現失敗,則這個事務產生的數據庫操作一並提交(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()


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM