JDBC事務自動提交機制
首先我們先來看一段代碼:
package com.guisha.JDBC; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Properties; /** * 方法描述 * @since: 1.0.0 * @param: * @return: * @author: Mr.cheng * @date: 2021年1月6日 21點48分 */ public class JDBCTest_03 { public static void main(String[] args) { Connection conn = null; PreparedStatement ps = null; try { InputStream input = JDBCTest_03.class.getClassLoader().getResourceAsStream("db.properties"); Properties property = new Properties(); try { //配置文件取值復制給新變量 property.load(input); String driver = property.getProperty("driver"); String url = property.getProperty("url"); String user = property.getProperty("user"); String password = property.getProperty("password"); //注冊驅動 Class.forName(driver); try { //獲取鏈接 conn = DriverManager.getConnection(url, user, password); String sql = "UPDATE LOGINNAME SET NAMEPASS = ? WHERE NAME = ? "; //獲取數據庫操作對象 ps = conn.prepareStatement(sql); //添加sql數據 ps.setString(1,"guisha"); ps.setString(2,"Mrcheng"); //執行SQL語句,返回條數 int i = ps.executeUpdate(); System.out.println(i); ps = conn.prepareStatement(sql); ps.setString(1,"wuxin"); ps.setString(2,"wuxin"); int i1 = ps.executeUpdate(); System.out.println(i1); } catch (SQLException e) { e.printStackTrace(); } } catch (IOException e) { e.printStackTrace(); } } catch (ClassNotFoundException e) { e.printStackTrace(); }finally{ //關閉資源 try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
代碼中,我們更新2條數據,但是在 int i = ps.executeUpdate(); 這里會自動提交事務,可以理解為提交SQL語句對數據庫修改
我們需要了解JDBC的事務是自動提交的,什么是自動提交?
1)只執行任意一條DML語句,則提交一次。JDBC默認的提交方式是自動提交,是JDBC默認的事務行為
2)在實際開發業務中,通常都是N條DML語句共同聯合才能完成的,必須保證DML語句在一個事務中同時成功或者同時失敗
我們來看一個小例子來驗證以下JDBC的事務是否是自動提交機制!
兩個人轉賬的小案例:
package com.guisha.JDBC; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Properties; /** * 方法描述 * @since: 1.0.0 * @param: * @return: * @author: Mr.cheng * @date: 2021年1月6日 21點48分 */ public class JDBCTest_04 { public static void main(String[] args) { Connection conn = null; PreparedStatement ps = null; try { InputStream input = JDBCTest_04.class.getClassLoader().getResourceAsStream("db.properties"); Properties property = new Properties(); try { //配置文件取值復制給新變量 property.load(input); String driver = property.getProperty("driver"); String url = property.getProperty("url"); String user = property.getProperty("user"); String password = property.getProperty("password"); //注冊驅動 Class.forName(driver); try { //獲取鏈接 conn = DriverManager.getConnection(url, user, password); String sql = "UPDATE center_cat SET user_baln = ? WHERE USER_NAME = ? "; //獲取數據庫操作對象 ps = conn.prepareStatement(sql); //添加sql數據 ps.setDouble(1,10000); ps.setString(2,"小明"); //執行SQL語句,返回條數 int i = ps.executeUpdate(); System.out.println(i); //自定義NullPointerException String s = null; s.toString(); ps.setString(1,"10000"); ps.setString(2,"王老頭"); i += ps.executeUpdate(); System.out.println(i); } catch (SQLException e) { e.printStackTrace(); } } catch (IOException e) { e.printStackTrace(); } } catch (ClassNotFoundException e) { e.printStackTrace(); }finally{ //關閉資源 try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
運行之后會出錯,我們程序中定義的 NullPointerException
再查看數據庫,數據庫執行一半出現異常終止,小明資金共20000,現在能夠看出來 小明明學轉出的10000元消失不見了,這里就是每執行一條DML語句,事務會自動提交一次
現在我們設置是為手動開啟和手動提交,如果中間出現錯誤,我們回滾數據
代碼如下:
package com.guisha.JDBC; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Properties; /** * 方法描述 * @since: 1.0.0 * @param: * @return: * @author: Mr.cheng * @date: 2021年1月6日 21點48分 */ public class JDBCTest_04 { public static void main(String[] args) { Connection conn = null; PreparedStatement ps = null; try { InputStream input = JDBCTest_04.class.getClassLoader().getResourceAsStream("db.properties"); Properties property = new Properties(); try { //配置文件取值復制給新變量 property.load(input); String driver = property.getProperty("driver"); String url = property.getProperty("url"); String user = property.getProperty("user"); String password = property.getProperty("password"); //注冊驅動 Class.forName(driver); try { //獲取鏈接 conn = DriverManager.getConnection(url, user, password); //開啟事務 conn.setAutoCommit(false); String sql = "UPDATE center_cat SET user_baln = ? WHERE USER_NAME = ? "; //獲取數據庫操作對象 ps = conn.prepareStatement(sql); //第一次給?傳值 ps.setDouble(1,10000); ps.setString(2,"小明"); //執行SQL語句,返回條數 int i = ps.executeUpdate(); System.out.println(i); // 自定義NullPointerException 用來終止正在執行的程序,用於演示JDBC事務自動提交機制的問題 // 想正常實執行代碼,請刪除自定義自定義NullPointerException異常 String s = null; s.toString(); //第二次給?傳值 ps.setString(1,"10000"); ps.setString(2,"王老頭"); i += ps.executeUpdate(); System.out.println(i == 2 ? "轉賬成功!": "轉賬失敗!"); //提交事務 conn.commit(); } catch (SQLException e) { / //數據回滾 if (conn !=null){ try { conn.rollback(); } catch (SQLException throwables) { throwables.printStackTrace(); } } e.printStackTrace(); } } catch (IOException e) { e.printStackTrace(); } } catch (ClassNotFoundException e) { e.printStackTrace(); }finally{ //關閉資源 try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
以上代碼就算出錯,數據會進行回滾,重點使用了conn.setAutoCommit(false)(手動開啟事務)、conn.commit()(手動提交事務)和conn.rollback(手動回滾數據)
正常執行如下:
package com.guisha.JDBC; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Properties; /** * 方法描述 * @since: 1.0.0 * @param: * @return: * @author: Mr.cheng * @date: 2021年1月6日 21點48分 */ public class JDBCTest_04 { public static void main(String[] args) { Connection conn = null; PreparedStatement ps = null; try { InputStream input = JDBCTest_04.class.getClassLoader().getResourceAsStream("db.properties"); Properties property = new Properties(); try { //配置文件取值復制給新變量 property.load(input); String driver = property.getProperty("driver"); String url = property.getProperty("url"); String user = property.getProperty("user"); String password = property.getProperty("password"); //注冊驅動 Class.forName(driver); try { //獲取鏈接 conn = DriverManager.getConnection(url, user, password); //開啟事務 conn.setAutoCommit(false); String sql = "UPDATE center_cat SET user_baln = ? WHERE USER_NAME = ? "; //獲取數據庫操作對象 ps = conn.prepareStatement(sql); //第一次給?傳值 ps.setDouble(1,10000); ps.setString(2,"小明"); //執行SQL語句,返回條數 int i = ps.executeUpdate(); System.out.println(i); //第二次給?傳值 ps.setString(1,"10000"); ps.setString(2,"王老頭"); i += ps.executeUpdate(); System.out.println(i == 2 ? "轉賬成功!": "轉賬失敗!"); //提交事務 conn.commit(); } catch (SQLException e) { if (conn !=null){ try { conn.rollback(); } catch (SQLException throwables) { throwables.printStackTrace(); } } e.printStackTrace(); } } catch (IOException e) { e.printStackTrace(); } } catch (ClassNotFoundException e) { e.printStackTrace(); }finally{ //關閉資源 try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }