JDBC事務控制管理(轉載)

JDBC事務控制管理

轉載於 2018年01月26日 15:46:11

一、事務java

(1)事務的概念mysql

事務指邏輯上的一組操做,組成這組操做的各個單元,要不所有成功,要不所有不成功。web

例如:A——B轉賬,對應於以下兩條sql語句sql

update account set money=money-100 where name=‘a’數據庫

update account set money=money+100 where name=‘b’;安全

數據庫默認事務是自動提交的,也就是發一條sql它就執行一條。若是想多條sql放在一個事務中執行,則須要使用以下語句。session

(2)數據庫開啓事務命令併發

方式一:利用SQL語句管理事務數據庫設計

start transaction;--開啓事務,這條語句以後的sql語句將處在一個事務當中,這些sql語句並不會當即執行post

Commit--提交事務,一旦提交事務,事務中的全部sql語句纔會執行。

Rollback -- 回滾事務,將以前全部的sql取消。

 

方式二:在數據庫中存在一個自動提交變量,經過show variables like '%commit%'-----autocommit 值是on,說明開啓了事務自動提交。

能夠 set autocommint = off(set autocommint=0),關閉自動提交,此時輸入的sql語句就不會自動提交了,須要手動roolback或commit

二、使用事務

(1)當Jdbc程序向數據庫得到一個Connection對象時,默認狀況下這個Connection對象會自動向數據庫提交在它上面發送的SQL語句。若想關閉這種默認提交方式,讓多條SQL在一個事務中執行,可以使用下列語句:

(2)JDBC控制事務語句

Connection.setAutoCommit(false); //  至關於start transaction

Connection.rollback();  rollback

Connection.commit();  commit

三、演示銀行轉賬案例

(1)在JDBC代碼中使以下轉賬操做在同一事務中執行。

  update from account set money=money-100 where name=‘a’;

  update from account set money=money+100 where name=‘b’;

(2)設置事務回滾點

Savepoint sp = conn.setSavepoint();

Conn.rollback(sp);

Conn.commit();   //回滾後必需要提交

package com.itheima.transaction;

 

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.PreparedStatement;

import java.sql.SQLException;

import java.sql.Savepoint;

 

import org.junit.Test;

 

import com.itheima.util.DaoUtil;

 

public class Demo1 {

@Test

public void test1(){

Connection conn = null;

PreparedStatement ps1 = null;

PreparedStatement ps2 = null;

Savepoint sp = null;

try{

Class.forName("com.mysql.jdbc.Driver");

conn = DriverManager.getConnection("jdbc:mysql:///day11", "root", "root");

conn.setAutoCommit(false);

 

ps1 = conn.prepareStatement("update account set money = money+100 where name=?");

ps1.setString(1, "b");

ps1.executeUpdate();

 

//int i = 1/0;

 

ps2 = conn.prepareStatement("update account set money = money-100 where name=?");

ps2.setString(1, "a");

ps2.executeUpdate();

 

sp = conn.setSavepoint();

//-----------------------------------

ps1 = conn.prepareStatement("update account set money = money+100 where name=?");

ps1.setString(1, "b");

ps1.executeUpdate();

 

int i = 1/0;

 

ps2 = conn.prepareStatement("update account set money = money-100 where name=?");

ps2.setString(1, "a");

ps2.executeUpdate();

 

conn.commit();

 

}catch (Exception e) {

e.printStackTrace();

try {

if(sp !=null){

conn.rollback(sp);

conn.commit();

}else{

conn.rollback();

}

} catch (SQLException e1) {

// TODO Auto-generated catch block

e1.printStackTrace();

}

}finally{

DaoUtil.close(conn, ps1, null);

DaoUtil.close(conn, ps2, null);

}

}

}

 

 

JDBC API支持事務對數據庫的加鎖,而且提供了5種操做支持,2種加鎖密度。

5種加鎖支持爲:

static int TRANSACTION_NONE = 0;

static int TRANSACTION_READ_UNCOMMITTED = 1;

static int TRANSACTION_READ_COMMITTED = 2;

static int TRANSACTION_REPEATABLE_READ = 4;

static int TRANSACTION_SERIALIZABLE = 8;

具體的說明見表4-2。

2種加鎖密度:

最後一項爲表加鎖,其他3~4項爲行加鎖。

 

JDBC根據數據庫提供的默認值來設置事務支持及其加鎖,固然,也能夠手工設置:

setTransactionIsolation(TRANSACTION_READ_UNCOMMITTED);

能夠查看數據庫的當前設置:

getTransactionIsolation ()

須要注意的是,在進行手動設置時,數據庫及其驅動程序必須得支持相應的事務操做操做才行。

上述設置隨着值的增長,其事務的獨立性增長,更能有效地防止事務操做之間的衝突,同時也增長了加鎖的開銷,下降了用戶之間訪問數據庫的併發性,程序的運行效率也會隨之下降。所以得平衡程序運行效率和數據一致性之間的衝突。通常來講,對於只涉及到數據庫的查詢操做時,能夠採用TRANSACTION_READ_UNCOMMITTED方式;對於數據查詢遠多於更新的操做,能夠採用TRANSACTION_READ_COMMITTED方式;對於更新操做較多的,能夠採用TRANSACTION_REPEATABLE_READ;在數據一致性要求更高的場合再考慮最後一項,因爲涉及到表加鎖,所以會對程序運行效率產生較大的影響。

另外,在Oracle中數據庫驅動對事務處理的默認值是TRANSACTION_NONE,即不支持事務操做,因此須要在程序中手動進行設置。總之,JDBC提供的對數據庫事務操做的支持是比較完整的,經過事務操做能夠提升程序的運行效率,保持數據的一致性。


四、事務的特性(ACID)

(1)原子性(Atomicity)
原子性是指事務是一個不可分割的工做單位,事務中的操做要麼都發生,要麼都不發生。 

(2)一致性(Consistency)
事務先後數據的完整性必須保持一致。

(3)隔離性(Isolation)
事務的隔離性是指多個用戶併發訪問數據庫時,一個用戶的事務不能被其它用戶的事務所幹擾,多個併發事務之間數據要相互隔離。

(4)持久性(Durability)
持久性是指一個事務一旦被提交,它對數據庫中數據的改變就是永久性的,接下來即便數據庫發生故障也不該該對其有任何影響

五、事務的隔離級別

(1)多個線程開啓各自事務操做數據庫中數據時,數據庫系統要負責隔離操做,以保證各個線程在獲取數據時的準確性。

(2)若是不考慮隔離性,可能會引起以下問題:

 

髒讀(dirty reads) 

一個事務讀取了另外一個未提交的並行事務寫的數據。 

不可重複讀(non-repeatable reads) 

一個事務從新讀取前面讀取過的數據, 發現該數據已經被另外一個已提交的事務修改過。 

幻讀(phantom read) 

一個事務從新執行一個查詢,返回一套符合查詢條件的行, 發現這些行由於其餘最近提交的事務而發生了改變。

六、事務的隔離性

(1)髒讀:

指一個事務讀取了另一個事務未提交的數據。

這是很是危險的,假設A向B轉賬100元,對應sql語句以下所示

1.update account set money=money+100 while name=‘b’;

2.update account set money=money-100 while name=‘a’;

當第1條sql執行完,第2條還沒執行(A未提交時),若是此時B查詢本身的賬戶,就會發現本身多了100元錢。若是A等B走後再回滾,B就會損失100元。

        a 1000

b 1000

 

a:

start transaction;

update account set money=money-100 where name='a';

update account set money=money+100 where name='b';

 

 

b:

start transaction;

select * from account where name='b';

 

a:

rollback;

 

b:

strat transaction;

select * from account where name='b';

(2)不可重複讀:

在一個事務內讀取表中的某一行數據,屢次讀取結果不一樣。

例如銀行想查詢A賬戶餘額,第一次查詢A賬戶爲200元,此時A向賬戶存了100元並提交了,銀行接着又進行了一次查詢,此時A賬戶爲300元了。銀行兩次查詢不一致,可能就會很困惑,不知道哪次查詢是準的。

和髒讀的區別是,髒讀是讀取前一事務未提交的髒數據,不可重複讀是從新讀取了前一事務已提交的數據。

不少人認爲這種狀況就對了,無須困惑,固然是後面的爲準。咱們能夠考慮這樣一種狀況,好比銀行程序須要將查詢結果分別輸出到電腦屏幕和寫到文件中,結果在一個事務中針對輸出的目的地,進行的兩次查詢不一致,致使文件和屏幕中的結果不一致,銀行工做人員就不知道以哪一個爲準了。

        a 1000

b 1000

 

 

start transaction;

select sum(money) from account; ---- 總存款:2000

select count(*) from account; ---- 總帳戶數:2

-------------------------

b:

start transaction;

update account set money = money-1000 where name='b';

commit;

-------------------------

select avg(money) from account; ---- 帳戶平均金額:500

 

 

(3)虛讀(幻讀)

是指在一個事務內讀取到了別的事務插入的數據,致使先後讀取不一致。

如丙存款100元未提交,這時銀行作報表統計account表中全部用戶的總額爲500元,而後丙提交了,這時銀行再統計發現賬戶爲600元了,形成虛讀一樣會使銀行不知所措,到底以哪一個爲準。

            a 1000

b 1000

c 1000

start transaction;

select sum(money) from account; ---- 總存款:2000

-------------------------

c:

start transaction;

insert into account values(null,'c',1000);

commit;

-------------------------

select count(*) from account; ---- 總帳戶數:3

七、事務隔離性的設置語句

數據庫共定義了四種隔離級別:

Serializable:可避免髒讀、不可重複讀、虛讀狀況的發生。(串行化)

Repeatable read:可避免髒讀、不可重複讀狀況的發生。(可重複讀)不能夠避免虛讀

Read committed:可避免髒讀狀況發生(讀已提交)

Read uncommitted:最低級別,以上狀況均沒法保證。(讀未提交)

set [global/session]  transaction isolation level 設置事務隔離級別

select @@tx_isolation查詢當前事務隔離級別

安全性來講:Serializable>Repeatable read>Read committed>Read uncommitted

效率來講:Serializable<Repeatable read<Read committed<Read uncommitted

一般來講,通常的應用都會選擇Repeatable read或Read committed做爲數據庫隔離級別來使用。

mysql默認的數據庫隔離級別爲:REPEATABLE-READ

如何查詢當前數據庫的隔離級別?select @@tx_isolation;

如何設置當前數據庫的隔離級別?set [global/session] transaction isolation level ...;

~此種方式設置的隔離級別只對當前鏈接起做用。

set transaction isolation level read uncommitted;

set session transaction isolation level read uncommitted;

~此種方式設置的隔離級別是設置數據庫默認的隔離級別

set global transaction isolation level read uncommitted;

 

八、事務的丟失更新問題(lost update )

(1)兩個或多個事務更新同一行,但這些事務彼此之間都不知道其它事務進行的修改,所以第二個更改覆蓋了第一個修改 

(2)共享鎖:共享鎖和共享鎖能夠共存。共享鎖和排他鎖不能共存。在Serializable隔離級別下一個事務進行查詢操做將會加上共享鎖。

(3)排他鎖:排他鎖和全部鎖都不能共存。不管什麼隔離級別執行增刪改操做時,會加上排他鎖

(4).數據庫設計爲Serializable隔離級別,就能夠防止更新丟失問題。

 樂觀鎖和悲觀鎖並非數據庫中真實存在的鎖,是咱們如何利用共享和排他鎖解決更新丟失問題的兩種解決方案,體現了人們看待事務的態度:

悲觀鎖:悲觀的認爲大部分狀況下進行操做都會出現更新丟失問題。

在每次進行查詢的時候,都手動的加上一個排他鎖。

select * from table lock in share mode(讀鎖、共享鎖)

            select * from table for update (寫鎖、排它鎖)

樂觀鎖:樂觀的認爲大部分的狀況下都不會有更新丟失問題。經過時間戳字段,

在表中設計一個版本字段version,當每次對數據庫中的數據進行修改操做時,版本號都要進行增長。

(5)若是個人程序修改比較少查詢比較多:樂觀鎖

若是個人程序查詢比較少修改比較多:悲觀鎖

相關文章
相關標籤/搜索