JDBC——编程式事务的实现逻辑
引言
數據庫事務的概念和基礎,總結在《MySQL 基礎 ————事務與隔離級別總結》。
本篇博客通過“JDBC + 純編碼”方式實現事務控制,完成一個 A 給 B 轉賬的小功能,在進一步熟練JDBC的編程流程的同時,重點關注 Java 語言如何操作和控制事務。
一、事務自動提交的三種情況
事務默認自動提交的三種情況:
1、DDL操作執行后,會自動提交事務,SET autocommit=false 對該類語句不管用。不過,在DDL語言上一般不考慮事務。
2、DML(增、刪、改)默認情況下,執行后會自動提交,不過可以通過 set autocommit=false;取消 DML的自動提交。
3、數據庫連接關閉時,默認會自動提交事務。因此,如果兩次DML操作在同一事務中,則事務中間不可以關閉連接。
一般的事務控制只需要考慮后兩種情況,我們需要做的就是在當前連接中,取消自動提交,在整體邏輯完成后,提交事務或異常回滾,最后恢復自動提交。
二、邏輯介紹、庫表及實體類
如下圖,兩條用戶信息,完成 Morty 轉賬 100 塊給 Jack 的轉賬功能。涉及兩步操作:1、Morty 減少 100? ?2、Jack 增加 100
User 實體類:
public class User {private Integer id;private String name;private Date birthDay;private Integer balance;public User() {}// ... getter setter... }三、數據庫連接獲取與資源關閉工具類
該工具類使用 JDBC API 完成對數據庫連接的獲取,及關閉操作,代碼邏輯與注意事項參考《JDBC——概述與JDBC的使用》
import java.io.InputStream; import java.sql.*; import java.util.Properties;public class JDBCUtils {public static Connection getConnection() {Connection connection = null;try {// 默認的識別路徑就是 src 目錄下InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");Properties props = new Properties();props.load(is);String url = props.getProperty("url");String username = props.getProperty("username");String password = props.getProperty("password");String driverName = props.getProperty("driverName");// 加載驅動類Class.forName(driverName);// 獲取連接connection = DriverManager.getConnection(url, username, password);} catch (Exception e) {e.printStackTrace();}return connection;}public static void closeResource(Connection conn, Statement statement, ResultSet rs) {try {if (rs != null) {rs.close();}} catch (SQLException e) {e.printStackTrace();}try {if (statement != null) {statement.close();}} catch (SQLException e) {e.printStackTrace();}try {if (conn != null) {conn.close();}} catch (SQLException e) {e.printStackTrace();}} }四、轉賬操作邏輯實現
在《JDBC——概述與JDBC的使用》中,展示了簡單的入庫操作,這里可以將其優化為通用的更新操作:
/*** 數據庫更新操作*/private static int update(Connection connection, String sql, Object... args) throws Exception {PreparedStatement ps = null;try {ps = connection.prepareStatement(sql);// 填充屬性for (int i = 0; i < args.length; i++) {ps.setObject(i + 1, args[i]);}int rows = ps.executeUpdate();return rows;} finally {JDBCUtils.closeResource(null, ps, null);}}上述代碼中,方法參數接收一個連接Connection 對象,這是因為,在執行完更新操作后,不可以直接關閉連接,否則會自動提交事務,在最后的finally塊中,也要注意,不要將 Connection對象傳入關閉資源的方法中,以免誤關Connection。并且如果發生異常,直接拋出即可,這是因為如果在此過程中發生異常,既可以將情況反映給調用方,也可以在 finally 塊中關閉必要的資源。
/*** 轉賬(事務控制)*/private static void transferAccountsTx() {Connection connection = null;try {connection = JDBCUtils.getConnection();// 關閉自動提交connection.setAutoCommit(false);// 轉賬金額Integer amount = 100;// 減少金額String sql1 = "UPDATE user SET balance = balance - ? WHERE name = ?";update(connection, sql1, amount, "Morty");// 模擬異常System.out.println(10 / 0);// 增加金額String sql2 = "UPDATE user SET balance = balance + ? WHERE name = ?";update(connection, sql2, amount, "Jack");// 執行提交connection.commit();System.out.println("轉賬成功!");} catch (Exception e) {e.printStackTrace();try {// 異常回滾connection.rollback();} catch (SQLException ex) {ex.printStackTrace();}} finally {try {// 恢復事務自動提交,針對連接池的情況需要額外注意該操作connection.setAutoCommit(true);} catch (SQLException e) {e.printStackTrace();}JDBCUtils.closeResource(connection, null, null);}}上述代碼中,通過 .setAutoCommit(false) 關閉自動提交,開啟事務,?并通過 10 ÷ 0 的操作,模擬了一個轉賬過程中的異常,捕獲異常時,我們要執行回滾操作。如果邏輯可以正常執行,就使用? commit(); 完成提交操作。最后在整體邏輯后面,使用 finally 塊恢復事務自動提交(實際上,如果是關閉Connection,并不需要恢復自動提交,但如果是連接池的情況,重復利用連接就需要這么做),并關閉 Connection 。
五、通過代碼設置數據庫事務隔離級別
一般情況,不需要通過代碼層面更改數據庫事務,不過可以了解,事務隔離級別非常簡單,具體概念可以參考《MySQL 基礎 ————事務與隔離級別總結》。
使用 Connection 對象可以設置和獲取隔離級別:
// 獲取事務隔離級別 connection.getTransactionIsolation();// 設置事務隔離級別 connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);Connection 中定義了 4 種事務隔離級別的常量,從低到高分別是:
int TRANSACTION_READ_UNCOMMITTED = 1; int TRANSACTION_READ_COMMITTED = 2; int TRANSACTION_REPEATABLE_READ = 4; int TRANSACTION_SERIALIZABLE = 8;總結
需要考慮事務自動提交的兩種情況:
1、DML語句會自動提交事務,需要 set autocommit=false 關閉自動提交,對應 JDBC的 API 就是?
connection.setAutoCommit(false);2、關閉連接會自動提交事務,一定要在完成全部事務操作后才可以關閉 Connection 。代碼中一定要注意? connection.close() 的位置!
三個事務控制的新方法:
// 關閉自動提交 connection.setAutoCommit(false); ... // 手動提交事務 connection.commit(); ... // 異常時回滾事務 connection.rollback();另外,每條SQL執行時也可能發生異常導致操作失敗,一定要將異常拋給包含事務的調用方,以免調用方以為SQL執行成功,從而破壞了事務的一致性。
具體點說,比如上述案例中,Morty 賬戶減100 時在 update() 執行過程中發生了未知異常,但是如果update(..) 方法內部捕獲了異常,方法正常退出,而沒有拋給 transferAccountsTx(),那么transferAccountsTx() 就不會進入 catch() 塊并回滾事務。這也是一個需要注意的點,即,我們要保證事務中的每一處發生異常時,都可以成功回滾。
?
總結
以上是生活随笔為你收集整理的JDBC——编程式事务的实现逻辑的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 不超过20位的小数正则_意甲身价最贵的2
- 下一篇: C++期末实践程序设计与数组作为参数的注