1. 程式人生 > >JDBC學習(六、事務處理操作)

JDBC學習(六、事務處理操作)

案例:銀行轉賬:從張無忌賬戶上給趙敏轉1000塊。
準備:account(賬戶表):
---------------------------------------------------------------
id            name(賬號,唯一)           balance(餘額)
1             張無忌                             20000
2             趙敏                                  0
---------------------------------------------------------------
操作步驟:
           1):檢查張無忌的賬戶餘額是否大於等於1000.
           SELECT * FROM account WHERE  name = '張無忌' AND balance >= 1000;
           2):從張無忌的賬戶餘額中減少1000.
           UPDATE account SET balance = balance - 1000 WHERE name = '張無忌';
           3):再在趙敏的賬戶餘額中增加1000.
           UPDATE account SET balance = balance + 1000 WHERE name = ' 趙敏';
如果:在第二步和第三步之間如果程式中斷,怎麼辦? (通過異常來模擬)

程式碼演示:

public void test1(){
		String sql = "select * from account where name=? AND balance>?";
		try {
			Class.forName("com.mysql.jdbc.Driver");
			conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","rootroot");
			System.out.println(conn);
			//1.查詢張無忌賬戶餘額是否大於1000
			PreparedStatement ps = conn.prepareStatement(sql);
			ps.setString(1, "張無忌");
			ps.setDouble(2, 1000.0);
			ResultSet rs = ps.executeQuery();
			if(!rs.next()){
				throw new RuntimeException("賬戶餘額不足");
			}
			//2.從張無忌賬戶中轉出1000
			sql = "update account set balance=balance-? where name=?";
			ps = conn.prepareStatement(sql);
			ps.setDouble(1, 1000.0);
			ps.setString(2, "張無忌");
			ps.executeUpdate();
			//---------模擬停電了--------------
			//int a = 1/0;
			//--------------------------
			//3.從趙敏賬戶中增加1000
			sql = "update account set balance=balance+? where name=?";
			ps = conn.prepareStatement(sql);
			ps.setDouble(1, 1000.0);
			ps.setString(2, "趙敏");
			ps.executeUpdate();
		} catch (ClassNotFoundException |SQLException |RuntimeException ex) {
			ex.printStackTrace();
		}
	}
事務(Transaction,簡寫為tx):
     在資料庫中,所謂事務是指一組邏輯操作單元,使資料從一種狀態變換到另一種狀態。
     我們把多個密不可分的操作看做是一個整體,那麼該整體就稱之為一個事務.
--------------------------------------------------
事務的ACID屬性:
1. 原子性(Atomicity)原子性是指事務是一個不可分割的工作單位,事務中的操作要麼都發生,要麼都不發生。
2. 一致性(Consistency)事務必須使資料庫從一個一致性狀態變換到另外一個一致性狀態,但是不最終資料不能被破壞,兩個賬戶的總餘額是不能改變的.
3. 隔離性(Isolation)
:MySQL再講
事務的隔離性是指一個事務的執行不能被其他事務干擾,即一個事務內部的操作及使用的資料對併發的其他事務是隔離的,併發執行的各個事務之間不能互相干擾。
4. 永續性(Durability)永續性是指一個事務一旦被提交,它對資料庫中資料的改變就是永久性的,接下來的其他操作和資料庫故障不應該對其有任何影響
--------------------------------------------------
事務:指構成單個邏輯工作單元的操作集合
事務處理:保證所有事務都作為一個工作單元來執行,即使出現了故障,都不能改變這種執行方式。當在一個事務中執行多個操作時,要麼所有的事務都被提交(commit),要麼整個事務回滾(rollback)到最初狀態
處理事務的兩個動作:
       提交:commit:   當整個事務中,所有的邏輯單元都正常執行成功.  ---->提交事務.---資料已經提交,不能更改.
       回滾:rollback:  當整個事務中,有一個邏輯單元執行失敗,              ---->回滾事務.  
                      撤銷該事務中的所有操作,釋放鎖--->恢復到最初的狀態.


    1):預設情況下,在JDBC中執行DML操作就會自動提交事務,此時我們得設定事務的手動提交機制(取消事務的自動提交).
    2):查詢操作,不涉及資料的更改,所以不需要事務.
    3):MySQL中InnoDB儲存引擎支援事務,MyISAM不支援.
     alter table account engine = 'MyISAM';   ---> "InnoDB"
意識:如果是DML操作時,沒有異常,程式碼也正確,但是資料改變不了,首先去想到事務沒有提交。

操作事務的模板:

try{
   //取消事務自動提交:
   connection物件.setAutoCommit(false);
   操作1
   操作2
   操作3     
   //提交事務
   Connection物件.commit();
}catch(Exception e){
     //處理異常
     //回滾事務
    Connection物件.rollback();
}finally{
    釋放資源
}

上述例子修改後程式碼演示:

@Test
	public void test2() throws ArithmeticException{
		String sql = "select * from account where name=? AND balance>?";
		try {
			Class.forName("com.mysql.jdbc.Driver");
			conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","rootroot");
			//取消事務自動提交
			conn.setAutoCommit(false);
			System.out.println(conn);
			//1.查詢張無忌賬戶餘額是否大於1000
			PreparedStatement ps = conn.prepareStatement(sql);
			ps.setString(1, "張無忌");
			ps.setDouble(2, 1000.0);
			ResultSet rs = ps.executeQuery();
			if(!rs.next()){
				throw new RuntimeException("賬戶餘額不足");
			}
			//2.從張無忌賬戶中轉出1000
			sql = "update account set balance=balance-? where name=?";
			ps = conn.prepareStatement(sql);
			ps.setDouble(1, 1000.0);
			ps.setString(2, "張無忌");
			ps.executeUpdate();
			//---------模擬停電了--------------
			int a = 1/0;
			//--------------------------
			//3.從趙敏賬戶中增加1000
			sql = "update account set balance=balance+? where name=?";
			ps = conn.prepareStatement(sql);
			ps.setDouble(1, 1000.0);
			ps.setString(2, "趙敏");
			ps.executeUpdate();
			conn.commit();
		} catch ( Exception ex) {
			ex.printStackTrace();
			try {
				conn.rollback();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}