許多數據庫的auto-commit默認是ON的,比如MySQL,PostgresSQL等。當然也有默認是OFF的,比如Oracle(Oracle里面執行DML語句是需要手動commit的)。
這里我們以MySQL為例,先寫一個基本的JDBC連接的例子:
package com.mycloud.demo.connection; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; public class ConncetionTest1 { public static void main(String[] args) { String jdbcUrl = "jdbc:mysql://localhost:3306/test_db"; String username = "xxx"; String password = "xxx"; // Basic sample // Connection -> Statement -> ResultSet: try with resource try (Connection con = DriverManager.getConnection(jdbcUrl, username, password); Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery("select id, account from test")) { String result = getResultSetAsString(rs); System.out.println(result); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException("Error occurred!"); } } private static String getResultSetAsString(ResultSet rs) throws SQLException { ResultSetMetaData rsmd = rs.getMetaData(); int numCols = rsmd.getColumnCount(); StringBuilder sb = new StringBuilder(); while (rs.next()) { for (int i = 1; i <= numCols; i++) { String elem = rs.getString(i); if (rs.wasNull()) sb.append("NULL"); else sb.append(elem); if (i != numCols) sb.append("|"); } sb.append(System.lineSeparator()); } return sb.toString(); } }
在auto-commit默認是ON的情況下,每一條sql都是一個獨立的事務,運行完直接commit。但是如果需要實現事務,比如我們執行一組DML,如果某一條失敗,就全部rollback。這種方式就不行了:
try (Connection conn = DriverManager.getConnection(jdbcUrl, username, password); Statement stmt = conn.createStatement()) { stmt.execute("INSERT INTO test(id, account) VALUES (1, 100)"); stmt.execute("INSERT INTO test(id, account) VALUES (2, 200)"); stmt.execute("INSERT INTO test(id, account) VALUES (2, 201)"); // Exception: duplicate pk } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException("Error occurred!"); }
我們查詢數據庫發現,成功插入了兩條數據:
id | account |
1 | 100 |
2 | 200 |
在這種情況下,我們首先要通過設置connection.setAutoCommit為OFF來開啟事務,再通過connection.commit/connection.rollback來提交/回滾事務。
try (Connection conn = DriverManager.getConnection(jdbcUrl, username, password); Statement stmt = conn.createStatement()) { conn.setAutoCommit(false); try { stmt.execute("INSERT INTO test(id, account) VALUES (1, 100)"); stmt.execute("INSERT INTO test(id, account) VALUES (2, 200)"); stmt.execute("INSERT INTO test(id, account) VALUES (3, 300)"); } catch (SQLException e) { e.printStackTrace(); conn.rollback(); // rollback throw e; } conn.commit(); // commit } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException("Error occurred!"); }
我們查詢數據庫發現,成功插入了3條數據:
id | account |
1 | 100 |
2 | 200 |
3 | 300 |
當然,如果我們去掉conn.commit()這一句,數據就不會插入了。
如果這一組DML中某一條失敗,則會被SQLException捕獲,從而拋出異常並回滾。
try (Connection conn = DriverManager.getConnection(jdbcUrl, username, password); Statement stmt = conn.createStatement()) { conn.setAutoCommit(false); try { stmt.execute("INSERT INTO test(id, account) VALUES (1, 100)"); stmt.execute("INSERT INTO test(id, account) VALUES (2, 200)"); stmt.execute("INSERT INTO test(id, account) VALUES (2, 201)"); // Exception: duplicate pk stmt.execute("INSERT INTO test(id, account) VALUES (3, 300)"); } catch (SQLException e) { e.printStackTrace(); conn.rollback(); // rollback throw e; } conn.commit(); // commit } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException("Error occurred!"); }
我們再查詢數據庫發現,並沒有數據插入。