事務(操作數據庫,判斷SQL語句是提交還是回滾)


事務

要么都成功,要么都失敗。

在一個事件中的事:從通知開啟事務,到提交事務之間,任何一句語句有錯誤,則哪一句語句都不應該提交。

1、事務的幾個狀態

1、開啟事務

2、事務提交 commit()。在做事務提交和回滾前,關鍵的頭尾是:(1)通知開啟事務,false是開啟(connection.setAutoCommit(false);) (2)提交或回滾(提交寫在try部分,回滾寫在catch中)

3、事務回滾 rollback()。事務一旦提交(commit)了,就無法回滾(rollback)了。所以,應該在提交之前判斷,如果崩了就不要提交,直接回滾。

4、關閉事務

轉賬:

A:1千塊

B:1千塊

A --> B: A(900)、B(1100)

不能出現A轉給了B100塊錢,A成為900,但是系統崩潰了,B還沒收到100塊錢,出現了A900塊錢,B1000塊錢的情況。

1、搭建環境,進行測試

1 Create TABLE account(
2     id INT PRIMARY KEY auto_increment,
3     `name` VARCHAR(40),
4     money FLOAT
5 );
6 INSERT INTO account(`name`,money) VALUES ('A',1000);
7 INSERT INTO account(`name`,money) VALUES ('B',1000);
8 INSERT INTO account(`name`,money) VALUES ('C',1000);
View Code

2、項目構成

1、建的普通Maven項目(不用模板)

2、在java下建con.wang.utils文件夾,並在該文件夾下建兩個class文件。

3、兩個class文件中,一個是JDBC的工具類(JdbcUtils),公共的資源配置放在這;另一個是具體的操作數據庫的類(Test_jdbc)

4、連接數據庫的用戶名,密碼,數據庫驅動和url都放在resources下的db.properties文件夾中。

 

driver = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3306/jdbc_example?useUnicode=true&characterEncoding=utf8&useSSL=true
username = root
password = 123456
View Code

5、導入mysql依賴。此外,為實現上一節提到的junit單元測試,還要導入junit依賴

    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
View Code

3、寫JDBC的工具類(JdbcUtils)

package com.wang.utils;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class JdbcUtils {
    private static String driver = null;
    private static String url = null;
    private static String username = null;
    private static String password = null;
    static {
        try {
            InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
            Properties properties = new Properties();
            properties.load(in);

            driver = properties.getProperty("driver");
            url = properties.getProperty("url");
            username = properties.getProperty("username");
            password = properties.getProperty("password");

            //驅動只用加載一次
            Class.forName(driver);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //獲取連接
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url, username, password);
    }
    //釋放連接資源
    public static void release(Connection conn, Statement st, ResultSet rs){
        if(rs!=null){
            try {
                rs.close();
            }catch (SQLException e){
                e.printStackTrace();
            }
        }
        if(st!=null){
            try {
                st.close();
            }catch (SQLException e){
                e.printStackTrace();
            }
        }
        if(conn!=null){
            try {
                conn.close();
            }catch (SQLException e){
                e.printStackTrace();
            }
        }

    }

}
View Code

4、寫具體的操作數據庫的類(Test_jdbc)

      —— 模擬轉賬,並測試失敗情況下的回滾和成功情況下的事務提交

情況1:當寫第4步,故意制造錯誤的時候,看看第3步的SQL語句,提交還是回滾

 1 package com.wang.utils;
 2 import org.junit.Test;
 3 
 4 import java.sql.Connection;
 5 import java.sql.DriverManager;
 6 import java.sql.SQLException;
 7 public class Test_jdbc {
 8     @Test
 9     public void test() throws Exception {
10         //配置信息
11         //String driver = "com.mysql.jdbc.Driver";
12         String url = "jdbc:mysql://localhost:3306/jdbc_example?useUnicode=true&characterEncoding=utf8&useSSL=true";
13         String username = "root";
14         String password = "123456";
15         Connection connection = null;
16         try {
17             //第一步,加載驅動
18             Class.forName("com.mysql.jdbc.Driver");
19             //第二步,連接數據庫
20             connection = DriverManager.getConnection(url, username, password);
21             //第三步,通知開啟事務。false是開啟
22             connection.setAutoCommit(false);
23             //第四步,執行SQL語句
24             String sql = "update account set money = money - 100 where name = 'A'";
25             connection.prepareStatement(sql).executeUpdate();
26             //第五步,制造錯誤
27             int i = 1/0;
28             //第六步,寫第二句SQL語句。理論上,第五步出現錯誤,所以這一句應該也不執行,不應該提交,應該回滾
29             String sql2 = "update account set money = money + 100 where name = 'B'";
30             connection.prepareStatement(sql2).executeUpdate();
31             //第七步,提交
32             connection.commit();
33             //第八步,若上面兩天SQl都提交成功,則輸出“提交成功”
34             System.out.println("提交成功!");
35         } catch (Exception e) {
36             try {
37                 //如果try中的方法有錯誤,就走catch,通知數據庫執行回滾事務
38                 connection.rollback();
39             } catch (SQLException e1) {
40                 e1.printStackTrace();
41             }
42             e.printStackTrace();
43         } finally {
44             //釋放資源
45             connection.close();
46         }
47     }
48 }
View Code

2、當注釋掉第五步,代碼沒有錯誤,則提交事務

package com.wang.utils;
import org.junit.Test;
import java.sql.*;
public class Test_jdbc {
    @Test
    public void test() throws Exception {
        Connection connection = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            //第一步,連接數據庫
            connection = JdbcUtils.getConnection();
            //第二步,通知開啟事務。false是開啟
            connection.setAutoCommit(false);
            //第三步,執行SQL語句
            String sql = "update account set money = money - 100 where name = 'A'";
            connection.prepareStatement(sql).executeUpdate();
            //第四步,故意制造錯誤
            //int i = 1/0;
            //第五步,寫第二句SQL語句。
            //注釋掉第四步錯誤,第三步和第五步的sql和sql2語句都提交
            String sql2 = "update account set money = money + 100 where name = 'B'";
            connection.prepareStatement(sql2).executeUpdate();
            //第六步,提交
            connection.commit();
            //第七步,若上面兩條SQl都提交成功,則輸出“提交成功”
            System.out.println("提交成功!");
        } catch (Exception e) {
            try {
                //如果try中的方法有錯誤,就走catch,通知數據庫執行回滾事務
                connection.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            //釋放資源
            JdbcUtils.release(connection,st,rs);
        }
    }
}
View Code

5、上述的關鍵在第3步,通知開啟事務:connection.setAutoCommit(false);    false是開啟

若不通知開啟事務,即使有錯誤,也不會回滾,直接就提交了。

 


免責聲明!

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



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