JDBC控制事務

概念

事務(Transaction)是訪問並可能更新數據庫中各類 數據項的一個程序執行單元(unit)。事務一般由 高級數據庫操縱語言或編程語言(如SQL,C++或Java)書寫的 用戶程序的執行所引發,並用形如begin transaction和end transaction語句(或 函數調用)來界定。事務由事務開始(begin transaction)和事務結束(end transaction)之間執行的全體操做組成。
例如:在 關係數據庫中,一個事務能夠是一條SQL語句,一組SQL語句或整個程序。

特性

事務是恢復和 併發控制的基本單位。
事務應該具備4個屬性:原子性、一致性、隔離性、持久性。這四個屬性一般稱爲ACID特性。
原子性(atomicity)。一個事務是一個不可分割的工做單位,事務中包括的諸操做要麼都作,要麼都不作。
一致性(consistency)。事務必須是使數據庫從一個一致性狀態變到另外一個一致性狀態。一致性與原子性是密切相關的。
隔離性(isolation)。一個事務的執行不能被其餘事務干擾。即一個事務內部的操做及使用的數據對併發的其餘事務是隔離的,併發執行的各個事務之間不能互相干擾。
持久性(durability)。持久性也稱永久性(permanence),指一個事務一旦提交,它對數據庫中數據的改變就應該是永久性的。接下來的其餘操做或故障不該該對其有任何影響。
 

定義

在數據庫操做中,爲了有效保證併發讀取數據的正確性,提出的 事務隔離級別

問題的提出

數據庫是要被廣大客戶所共享訪問的,那麼在數據庫操做過程當中極可能出現如下幾種不肯定狀況。

更新丟失

兩個事務都同時更新一行數據,一個事務對數據的更新把另外一個事務對數據的更新覆蓋了。這是由於系統沒有執行任何的鎖操做,所以併發事務並無被隔離開來。

髒讀

一個事務讀取到了另外一個事務未提交的數據操做結果。這是至關危險的,由於極可能全部的操做都被 回滾

不可重複讀

不可重複讀(Non-repeatable Reads):一個事務對同一行數據重複讀取兩次,可是卻獲得了不一樣的結果。
包括如下狀況:
(1) 虛讀:事務T1讀取某一數據後,事務T2對其作了修改,當事務T1再次讀該數據時獲得與前一次不一樣的值。
(2)  幻讀(Phantom Reads):事務在操做過程當中進行兩次查詢,第二次查詢的結果包含了第一次查詢中未出現的數據或者缺乏了第一次查詢中出現的數據(這裏並不要求兩次查詢的 SQL語句相同)。這是由於在兩次查詢過程當中有另一個事務插入數據形成的。
 

解決方案

爲了不上面出現的幾種狀況,在標準 SQL規範中,定義了4個事務隔離級別,不一樣的隔離級別對事務的處理不一樣。

未受權讀取

也稱爲讀未提交(Read Uncommitted):容許髒讀取,但不容許更新丟失。若是一個事務已經開始寫數據,則另一個事務則不容許同時進行寫操做,但容許其餘事務讀此行數據。該隔離級別能夠經過「排他寫鎖」實現。

受權讀取

也稱爲讀提交(Read Committed):容許 不可重複讀取,但不容許髒讀取。這能夠經過「瞬間共享讀鎖」和「排他寫鎖」實現。讀取數據的事務容許其餘事務繼續訪問該行數據,可是未提交的寫事務將會禁止其餘事務訪問該行。

可重複讀取(Repeatable Read)

可重複讀取(Repeatable Read):禁止 不可重複讀取和髒讀取,可是有時可能出現幻讀數據。這能夠經過「共享讀鎖」和「排他寫鎖」實現。讀取數據的事務將會禁止寫事務(但容許讀事務),寫事務則禁止任何其餘事務。

序列化(Serializable)

序列化(Serializable):提供嚴格的事務隔離。它要求事務 序列化執行,事務只能一個接着一個地執行,不能併發執行。僅僅經過「行級鎖」是沒法實現事務序列化的,必須經過其餘機制保證新插入的數據不會被剛執行查詢操做的事務訪問到。
隔離級別越高,越能保證數據的完整性和一致性,可是對併發性能的影響也越大。對於多數應用程序,能夠優先考慮把 數據庫系統的隔離級別設爲Read Committed。它可以避免髒讀取,並且具備較好的併發性能。儘管它會致使 不可重複讀、幻讀和第二類丟失更新這些併發問題,在可能出現這類問題的個別場合,能夠由應用程序採用 悲觀鎖樂觀鎖來控制。
 
mysql中默認事務的隔離級別是Repeatable Read,事務的隔離級別越高,數據庫性能越差。
 

JDBC中事務的使用

conn.setAutoCommit(0);//修改系統非自動提交。

conn.commit();//事務提交

conn.rollback();//事務回滾

SavePoint sp=con.setSavePoint();//設置保存點

conn.rollback(sp);//返回保存點

conn.setTransactionIsolation();//設置隔離級別

conn.getTransactionIsolation();//獲取隔離級別

其中隔離級別的設置以下:

設定事務的隔離級別:con.setTransactionIsolation(Connection.isolationLevel);
四種隔離級別: 
con.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);//最底級別:只保證不會讀到非法數據,上述3個問題有可能發生 
con.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); //默認級別:能夠防止髒讀 
con.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);//能夠防止髒讀和不可重複讀取 
con.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); //最高級別:防止上述3種狀況,事務串行執行

 

 簡單的用例:java

package com.netease.class1;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import org.apache.commons.dbcp.BasicDataSource;

import com.mysql.jdbc.PreparedStatement;

public class work_4 {
    private static String JDBC_DRIVER = "com.mysql.jdbc.Driver";
    private static String DB_URL = "jdbc:mysql://10.120.177.10:3306/datebaseclass";
    private static String USER = "test";
    private static String PASS = "test";

    public static void main(String[] args) throws ClassNotFoundException {
        work4();
    }

    public static void work4() {
        Connection conn = null;
        PreparedStatement ptmt = null;
        ResultSet rs = null;
        try {
            // 1
            Class.forName(JDBC_DRIVER);
            // 2
            conn = (Connection) DriverManager.getConnection(DB_URL, USER, PASS);
            // 3
            conn.setAutoCommit(false);
            ptmt = (PreparedStatement) conn
                    .prepareStatement("update Product set Inventory=Inventory-1 where ProductName = 'bag'");
            ptmt.execute();
            ptmt = (PreparedStatement) conn
                    .prepareStatement("INSERT INTO `Order` (buyer, ProductName) VALUES ('XiaoMing', 'bag')");
            ptmt.execute();
            conn.commit();

        } catch (ClassNotFoundException e) {
            // Class沒有發現異常
            System.out.println(e.toString());
        } catch (SQLException e) {
            // Class沒有發現異常
            if (conn != null) {
                try {
                    conn.rollback();
                } catch (SQLException e1) {

                    System.out.println(e.toString());
                }
            }
            System.out.println(e.toString());
        } finally {

            try {
                if (conn != null) {
                    conn.close();
                }
                if (ptmt != null) {
                    ptmt.close();
                }
                if (rs != null) {
                    rs.close();
                }
            } catch (SQLException e) {
                System.out.println(e.toString());
            }
        }

    }
}
相關文章
相關標籤/搜索