一 、事務的幾個重要特性
1. 原子性
事務內的每個內容不可分割,是一個統一的整體。或同時進行或同時消亡。
2.一致性
事務執行前和事務執行后,狀態都是統一的。如A轉B 100元,A和B數據總額度沒有在這個轉賬過程中增加或者減小。
3.隔離性【isolation】
事務的隔離性指的是幾個事務同時執行,事務a不應該干擾到事務b內的操作(在並發過程中很有可能會發生事務間的影響,例如臟讀、不可重復讀等。需要在編程的時候選擇適當的方式進行選擇)
3.可持久性
事務執行后的結果可以存儲(序列化)到硬盤上,形成一個固定的內容存儲起來。
二、事務的使用
事務的生命周期有2個,1開啟事務 2提交或者回滾事務。事務開始后一定要提交或者回滾,以免引起數據庫內存泄漏
使用Mysql開啟事務及使用。
表內數據如下,現需要從a賬號轉100money到b賬號。

具體步驟如下:

Mysql 中與事務使用有關的三個關鍵語句:
(1) 開啟事務 : start transaction
(2)回滾事務 :rollback , 提交事務:commit
三、使用JDBC對事務進行調用
△注意: 事務只能對DML語句進行操作,對數據定義類語句DDL無法操作,例如建表、建立索引、建立分區等。
JDBC使用事務時,需要使用Java語言中的Connection對事務進行操作,具體的事務對應一個數據庫連接。
1 package com.scl.test.transcaction; 2 3 import java.sql.Connection; 4 import java.sql.Statement; 5 6 import org.junit.Test; 7 8 public class TestTranscaction 9 { 10 11 @Test 12 public void testTransaction() throws Exception 13 { 14 Connection conn = null; 15 Statement statement = null; 16 17 try 18 { 19 conn = JDBCHelper.getConnection(); 20 21 conn.setAutoCommit(false); //開啟事務,禁止自動提交 22 String sql1 = "update t_account t set t.money=t.money-100 where t.name ='a'"; 23 String sql2 = "update t_account t set t.money=t.money+100 where t.name ='b'"; 24 statement = conn.createStatement(); 25 // statement.setString(1, "a"); 26 27 statement.addBatch(sql1); 28 statement.addBatch(sql2); 29 30 31 statement.executeBatch(); 32 conn.commit(); //執行成功,提交事務 33 34 } 35 catch (Exception e) 36 { 37 conn.rollback(); //發生異常,事務回滾 38 } 39 finally 40 { 41 JDBCHelper.disposeConnect(statement, conn); 42 } 43 } 44 }
最后進行一個知識點補漏:JDBC執行多條sql腳本。
JDBC提供了執行多條sql語句的方法,使用PrepareStatement或Statement實例調用addBatch方法。目前發現該方法有幾個缺點:
1. 無法通過PrepareStatement實例對多條不同的sql腳本進行參數化設置。因為PrepareStatement是由一個鏈接產生的,不能同時用一個prepareStatement實例同時對應兩個不同的預處理文件。
如需求:需要插入更新一張表里面的兩行不同的數據。
sql1: update t_account t set t.money=t.money-100 where t.name ='a';
sql2: update t_account t set t.money=t.money-100 where t.name ='b';
這時候沒辦法使用prepareStatement的addBatch方法執行操作。見代碼:
1 @Test 2 public void testTransaction() throws Exception 3 { 4 Connection conn = null; 5 Statement st = null; 6 PreparedStatement pst = null; 7 try 8 { 9 conn = JDBCHelper.getConnection(); 10 11 conn.setAutoCommit(false); 12 String sql1 = "update t_account t set t.money=t.money-100 where t.name =?"; 13 String sql2 = "update t_account t set t.money=t.money+100 where t.name =?"; 14 pst = conn.prepareStatement(sql1); 15 // 只能批量執行某一條固定的sql語句,並且進行參數化設置 16 pst.setString(1, "a"); // 為name 為 a的用戶減少100元 17 pst.addBatch(); 18 pst.setString(1, "b"); // 為name 為b的用戶減少100元 19 pst.addBatch(); 20 pst.executeBatch(); // 只能在執行完成以后提交一次,然后改成新的sql腳本。但容易引發內存泄漏 21 22 pst = conn.prepareStatement(sql2); // 內存泄漏,存在未關閉的鏈接 23 pst.setString(1, "c"); 24 pst.addBatch(); 25 pst.executeBatch(); 26 conn.commit(); 27 28 } 29 catch (Exception e) 30 { 31 conn.rollback(); 32 } 33 finally 34 { 35 JDBCHelper.disposeConnect(pst, conn); 36 } 37 }
2. 使用statement實例對多條腳本進行提交(只提交一次,但無法進行預編譯sql腳本)
1 @Test 2 public void testTransaction() throws Exception 3 { 4 Connection conn = null; 5 Statement st = null; 6 PreparedStatement pst = null; 7 try 8 { 9 conn = JDBCHelper.getConnection(); 10 11 conn.setAutoCommit(false); 12 String sql1 = "update t_account t set t.money=t.money-100 where t.name =?"; 13 String sql2 = "update t_account t set t.money=t.money+100 where t.name =?"; 14 pst = conn.prepareStatement(sql1); 15 // 只能批量執行某一條固定的sql語句,並且進行參數化設置 16 pst.setString(1, "a"); // 為name 為 a的用戶減少100元 17 pst.addBatch(); 18 pst.setString(1, "b"); // 為name 為b的用戶減少100元 19 pst.addBatch(); 20 pst.executeBatch(); // 只能在執行完成以后提交一次,然后改成新的sql腳本。但容易引發內存泄漏 21 22 pst = conn.prepareStatement(sql2); // 內存泄漏,存在未關閉的鏈接 23 pst.setString(1, "c"); 24 pst.addBatch(); 25 pst.executeBatch(); 26 conn.commit(); 27 28 } 29 catch (Exception e) 30 { 31 conn.rollback(); 32 } 33 finally 34 { 35 JDBCHelper.disposeConnect(pst, conn); 36 } 37 }
