二.JDBC事務(wù)

1.事務(wù)特性(ACID)

ACID辛蚊,指數(shù)據(jù)庫事務(wù)正確執(zhí)行的四個基本要素的縮寫。包含:原子性(Atomicity)求妹、一致性(Consistency)揩懒、隔離性(Isolation)、持久性(Durability)被辑。一個支持事務(wù)(Transaction)的數(shù)據(jù)庫燎悍,必須要具有這四種,否則在事務(wù)過程當(dāng)中無法保證數(shù)據(jù)的正確性盼理,交易過程極可能達(dá)不到交易方的要求谈山。

  • 原子性(Atomicity):整個事務(wù)中的所有操作,要么全部完成宏怔,要么全部不完成奏路,不可能停滯在中間某個環(huán)節(jié)。事務(wù)在執(zhí)行過程中發(fā)生錯誤臊诊,會被
    回滾(Rollback)到事務(wù)開始前的狀態(tài)鸽粉,就像這個事務(wù)從來沒有執(zhí)行過一樣。

  • 一致性(Consistency):事務(wù)前數(shù)據(jù)的狀態(tài)和事務(wù)后數(shù)據(jù)的狀態(tài)保持一致

  • 持久性(Durability):在事務(wù)完成以后抓艳,該事務(wù)對數(shù)據(jù)庫所作的更改便持久的保存在數(shù)據(jù)庫之中触机,并不會被回滾。

  • 隔離性(Isolation 重點):針對多線程并發(fā)操作數(shù)據(jù)資源的時候,每個事務(wù)應(yīng)該是獨立的儡首,不會受到其他事務(wù)的打擾销斟,如果說數(shù)據(jù)庫軟件不支持事務(wù)的隔離性,多個線程訪問數(shù)據(jù)的時候椒舵,會受到相互的打擾蚂踊。這種情況不是正確的。

    1.png

1.1隔離級別

1.Serializable (串行化):可避免臟讀笔宿、不可重復(fù)讀犁钟、幻讀的發(fā)生。
2.Repeatable read (可重復(fù)讀):可避免臟讀泼橘、不可重復(fù)讀的發(fā)生(mysql默認(rèn)的隔離級別)涝动,有可能發(fā)生幻讀。
3.Read committed (讀已提交):可避免臟讀的發(fā)生(oralce默認(rèn)的隔離級別)炬灭,不可重復(fù)讀還有幻讀有可能發(fā)生醋粟。
4.Read uncommitted (讀未提交):最低級別,任何情況都無法保證重归。

1.2如果沒有隔離性而言米愿,會出現(xiàn)哪些問題?

1.臟讀:兩個事務(wù)(t1,t2),t1事務(wù)開啟鼻吮,修改了李四的賬戶的錢加上1000育苟,并沒有提交事務(wù),t2,查詢到了李四已經(jīng)修改后的數(shù)據(jù)椎木。這種數(shù)據(jù)就是臟讀违柏。(t2讀到了t1沒有提交的數(shù)據(jù))。


臟讀.png

2.不可重復(fù)讀:兩個事務(wù)(t1,t2),t1事務(wù)開啟香椎,t2事務(wù)開啟漱竖,t2查詢一次李四的賬戶的資金,t1修改李四的錢加上1000畜伐,t1提交是事務(wù)馍惹。t2在沒有提交事務(wù)的前提下,執(zhí)行了相同的查詢烤礁,出現(xiàn)了本次查詢的數(shù)據(jù)發(fā)生變化讼积。(t2本次事務(wù)中部可以能重復(fù)讀數(shù)據(jù))


不可重復(fù)讀.png

3.幻讀(虛讀):兩個事務(wù)(t1,t2)肥照,一般針對的是數(shù)據(jù)的添加操作脚仔,t1開啟事務(wù),t2開啟事務(wù)舆绎,t1查詢賬戶表有5個用戶鲤脏,t2,在賬戶表中添加了一個用戶,t2提交事務(wù)。t1又查詢賬戶表發(fā)現(xiàn)會6個用戶猎醇。這就出現(xiàn)了幻讀窥突。


幻讀.png

2.TCL語句

  • SQL語句分類
1. DDL 數(shù)據(jù)定義語句 (create 、drop.....sql語句)
2. DML 數(shù)據(jù)操作語句(updat硫嘶、insert into 阻问、 delete.... sql 語句)
3. DQL 數(shù)據(jù)查詢語句 (select ...  語句  )
4. TCL 事務(wù)控制語句 
     start transaction  開啟事務(wù)
     rollback  事務(wù)回滾,本次事務(wù)就結(jié)束
     commit 事務(wù)提交沦疾,本次事務(wù)就結(jié)束
     select @@tx_isolation  針對mysql查看隔離級別
     set tx_isolation ='隔離級別'   設(shè)置隔離級別

3.編程式事務(wù)管理

編碼方式實現(xiàn)事務(wù)管理(JDBC編程來管理事務(wù))
聲明式事務(wù)管理:可知編程式事務(wù)每次實現(xiàn)都要單獨實現(xiàn)称近,但業(yè)務(wù)量大功能復(fù)雜時,使用編程式事務(wù)無疑是痛苦的哮塞,而聲明式事務(wù)不同刨秆,聲明式事務(wù)屬于無侵入式,不會影響業(yè)務(wù)邏輯的實現(xiàn)(通過配置方案就實現(xiàn)了事務(wù)的管理)

3.1 JDBC編程式管理

  public void update(UserModel entity) {

        // 1.創(chuàng)建三大對象引用
        Connection conn = null;
        PreparedStatement pstmt = null;
        // 2.初始化引用對象
        // 2.1 try-catch-finally
        try {
            conn = DBUtil.getConn();
            // 關(guān)閉了事務(wù)的自動提交忆畅,關(guān)閉自動事務(wù)提交也就意味著手動開啟了事務(wù)衡未。
            // 開啟事務(wù)
            conn.setAutoCommit(false);
            pstmt = conn.prepareStatement(UPDATE_SQL);
            pstmt.setObject(1, entity.getName());
            pstmt.setObject(2, entity.getPassword());
            pstmt.setObject(3, entity.getEmail());
            pstmt.setObject(4, entity.getBirthday());
            pstmt.setObject(5, entity.getId());
            pstmt.executeUpdate();
            int i = 1 / 0;
            // 提交事務(wù)
            conn.commit();
        } catch (Exception e) {
            try {
                // 回滾事務(wù)
                conn.rollback();
            } catch (SQLException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            DBUtil.replease(conn, pstmt, null);
        }
    }

注意:以上的編程事務(wù)管理方式是有問題的,由于把事務(wù)的管理操作加入到數(shù)據(jù)訪問層 Dao層,在實際開發(fā)過程中事務(wù)應(yīng)該加到業(yè)務(wù)層家凯。

4.數(shù)據(jù)源(連接池) JDBC規(guī)范中的DataSource 接口

數(shù)據(jù)源提供了一種簡單獲取數(shù)據(jù)庫連接的方式缓醋,并能在內(nèi)部通過一個池的機制來復(fù)用數(shù)據(jù)庫連接,這樣就大大減少創(chuàng)建數(shù)據(jù)庫連接的次數(shù)绊诲,提高了系統(tǒng)性能改衩。

數(shù)據(jù)源連接池的圖.png
  • sun公司并沒有對DataSource進(jìn)行實現(xiàn)

  • 市面上對數(shù)據(jù)源主要的實現(xiàn)工具類

    1.apache組織開發(fā)了一個 DBCP數(shù)據(jù)源
    2.c3p0組織開發(fā)的c3p0數(shù)據(jù)源
    3.alibaba溫少開發(fā)的druid德魯伊數(shù)據(jù)源(國產(chǎn)貨)

4.1開發(fā)druid數(shù)據(jù)源工具類

1.導(dǎo)入alibaba的druid數(shù)據(jù)源依賴jar包

2.編寫DruidUtil工具類

public class DruidUtil {
    
    private static final DruidDataSource DATASOURCE = new DruidDataSource();
    
    private static final String URL;
    private static final String USER;
    private static final String PASSWORD;
    private static final String DRIVER;

    static {
        try {
            InputStream inStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("jdbc.properties");
            Properties p = new Properties();
            p.load(inStream);
            URL = p.getProperty("jdbc.url");
            USER = p.getProperty("jdbc.user");
            PASSWORD = p.getProperty("jdbc.password");
            DRIVER = p.getProperty("jdbc.driver");
            DATASOURCE.setDriverClassName(DRIVER);
            DATASOURCE.setPassword(PASSWORD);
            DATASOURCE.setUrl(URL);
            DATASOURCE.setUsername(USER);
        } catch (Exception e) {
            throw new ExceptionInInitializerError("數(shù)據(jù)源配置出錯!");
        }
    }

    private DruidUtil() {

    }

    /**
     * 獲取連接對象
     */
    public static Connection getConn() {
        try {
            return DATASOURCE.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        
        return null;

    }

    /**
     * 釋放資源
     * 
     */

    public static void release(Connection conn, Statement stmt, ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            rs = null;
        }
        if (stmt != null) {
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            stmt = null;
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            conn = null;
        }

    }

    /**
     * 得到數(shù)據(jù)源對象
     * 
     */
    public static DataSource getDataSource() {

        return DATASOURCE;

    }
}

5.ThreadLocal事務(wù)控制

早在JDK 1.2的版本中就提供java.lang.ThreadLocal驯镊,ThreadLocal為解決多線程程序的并發(fā)問題提供了一種新的思路葫督。使用這個工具類可以很簡潔地編寫出優(yōu)美的多線程程序。
當(dāng)使用ThreadLocal維護變量時板惑,ThreadLocal為每個使用該變量的線程提供獨立的變量副本橄镜,所以每一個線程都可以獨立地改變自己的副本,而不會影響其它線程所對應(yīng)的副本冯乘。
從線程的角度看洽胶,目標(biāo)變量就象是線程的本地變量,這也是類名中“Local”所要表達(dá)的意思裆馒。
所以姊氓,在Java中編寫線程局部變量的代碼相對來說要笨拙一些,因此造成線程局部變量沒有在Java開發(fā)者中得到很好的普及喷好。

5.1ThreadLocal接口 api介紹:

  • get() 從線程中取變量
  • remove() 從線程中移除變量
  • set(T value) 向線程中設(shè)置變量

5.2 修改DruidUtil工具類

public class DruidUtil {
    
    private static final DruidDataSource DATASOURCE = new DruidDataSource();
    // 創(chuàng)建線程局部變量操作對象
    private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
    
    private static final String URL;
    private static final String USER;
    private static final String PASSWORD;
    private static final String DRIVER;

    static {
        try {
            InputStream inStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("jdbc.properties");
            Properties p = new Properties();
            p.load(inStream);
            URL = p.getProperty("jdbc.url");
            USER = p.getProperty("jdbc.user");
            PASSWORD = p.getProperty("jdbc.password");
            DRIVER = p.getProperty("jdbc.driver");
            DATASOURCE.setDriverClassName(DRIVER);
            DATASOURCE.setPassword(PASSWORD);
            DATASOURCE.setUrl(URL);
            DATASOURCE.setUsername(USER);
        } catch (Exception e) {
            throw new ExceptionInInitializerError("數(shù)據(jù)源配置出錯翔横!");
        }
    }

    private DruidUtil() {

    }

    /**
     * 獲取連接對象
     */
    public static Connection getConn() {
        try {
            Connection conn = null;
            // 先從線程中取Connection對象
            if (tl.get() == null) {
                conn = DATASOURCE.getConnection();
                // 放入到當(dāng)前線程中
                tl.set(conn);
            }
            return tl.get();
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException("連接對象獲取失敗梗搅!");
        }
    }

    /**
     * 釋放資源
     * 
     */
    @Deprecated
    public static void release(Connection conn, Statement stmt, ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            rs = null;
        }
        if (stmt != null) {
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            stmt = null;
        }
        if (conn != null) {
            try {
                conn.close();
                // 從線程中移除掉Connection對象
                tl.remove();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            conn = null;
        }

    }

    /**
     * 得到數(shù)據(jù)源對象
     * 
     */
    public static DataSource getDataSource() {

        return DATASOURCE;

    }

    /**
     * 開啟事務(wù)
     */
    public static void startTransaction() {
        Connection conn = getConn();
        try {
            conn.setAutoCommit(false);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 提交事務(wù)
     */
    public static void commit() {
        try {
            Connection conn = getConn();
            conn.commit();
            conn.close();
            tl.remove();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 事務(wù)回滾
     */
    public static void rollback() {
        try {
            Connection conn = getConn();
            conn.rollback();
            conn.close();
            tl.remove();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

}

5.3 業(yè)務(wù)層代碼添加事務(wù)

public class AccountServiceImpl implements IAccountService {

    private IAccoutDao accountDao = new AccountDaoImpl();

    public void transferAccount(Account srcAccout, Account targAccount, double cash) {
        srcAccout.setMoney(srcAccout.getMoney() - cash);
        targAccount.setMoney(targAccount.getMoney() + cash);
        try {
            DruidUtil.startTransaction();
            accountDao.modifyAccountMoney(srcAccout);
            // 測試異常禾唁,事務(wù)回滾
            int i = 1 / 0;
            accountDao.modifyAccountMoney(targAccount);
            DruidUtil.commit();
        } catch (Exception e) {
            e.printStackTrace();
            DruidUtil.rollback();
        }
    }

}

5.4 數(shù)據(jù)訪問層代碼簡化為

public class AccountDaoImpl implements IAccoutDao {

    private static final String MODIFY_SQL = "UPDATE ACCOUNT SET MONEY = ? WHERE NAME = ?";

    public void modifyAccountMoney(Account srcAccout) throws Exception {

        Connection conn = null;
        PreparedStatement stmt = null;

        conn = DruidUtil.getConn();
        stmt = conn.prepareStatement(MODIFY_SQL);
        stmt.setObject(1, srcAccout.getMoney());
        stmt.setObject(2, srcAccout.getName());
        stmt.executeUpdate();
    }

}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末效览,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子荡短,更是在濱河造成了極大的恐慌丐枉,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件掘托,死亡現(xiàn)場離奇詭異瘦锹,居然都是意外死亡,警方通過查閱死者的電腦和手機闪盔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門沼本,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人锭沟,你說我怎么就攤上這事抽兆。” “怎么了族淮?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵辫红,是天一觀的道長。 經(jīng)常有香客問我祝辣,道長贴妻,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任蝙斜,我火速辦了婚禮名惩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘孕荠。我一直安慰自己娩鹉,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布稚伍。 她就那樣靜靜地躺著弯予,像睡著了一般。 火紅的嫁衣襯著肌膚如雪个曙。 梳的紋絲不亂的頭發(fā)上锈嫩,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機與錄音垦搬,去河邊找鬼呼寸。 笑死,一個胖子當(dāng)著我的面吹牛猴贰,可吹牛的內(nèi)容都是我干的对雪。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼糟趾,長吁一口氣:“原來是場噩夢啊……” “哼慌植!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起义郑,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤蝶柿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后非驮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體交汤,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年劫笙,在試婚紗的時候發(fā)現(xiàn)自己被綠了芙扎。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡填大,死狀恐怖戒洼,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情允华,我是刑警寧澤圈浇,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站靴寂,受9級特大地震影響磷蜀,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜百炬,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一褐隆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧剖踊,春花似錦庶弃、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至圃验,卻和暖如春掉伏,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背澳窑。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工斧散, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人摊聋。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓鸡捐,卻偏偏與公主長得像,于是被迫代替她去往敵國和親麻裁。 傳聞我的和親對象是個殘疾皇子箍镜,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 1源祈、面向?qū)ο蟮奶卣饔心男┓矫? 答:面向?qū)ο蟮奶卣髦饕幸韵聨讉€方面: -- 抽象:抽象是將一類對象的共同特征總結(jié)...
    ccc_74bd閱讀 973評論 0 1
  • 導(dǎo)語:計算機硬件在飛速發(fā)展,數(shù)據(jù)規(guī)模在急速膨脹色迂,但是數(shù)據(jù)庫仍然使用是十年以前的架構(gòu)體系香缺,WiredTiger 嘗試...
    isgiker閱讀 3,397評論 0 7
  • 1.數(shù)據(jù)庫事務(wù)基礎(chǔ)知識 1.1.何為數(shù)據(jù)庫事務(wù) 數(shù)據(jù)庫事務(wù)的4個特性 原子性:組成一個事務(wù)的多個數(shù)據(jù)庫操作是一個不...
    小螺釘12138閱讀 1,556評論 1 18
  • Java SE 基礎(chǔ): 封裝、繼承歇僧、多態(tài) 封裝: 概念:就是把對象的屬性和操作(或服務(wù))結(jié)合為一個獨立的整體图张,并盡...
    Jayden_Cao閱讀 2,099評論 0 8
  • 孫家兒郎出仕強, 唐門姑娘難玄郎诈悍。 丁酉司晨穿紅妝祸轮, 戊戌韓盧慶成雙。 何日擺酒慶回鄉(xiāng)侥钳, 但飲醇釀不懼涼适袜。
    濁酒一壺慰風(fēng)塵閱讀 215評論 0 0