JDBC【事務(wù)失仁、元數(shù)據(jù)尸曼、改造JDBC工具類】

1.事務(wù)

一個SESSION所進行的所有更新操作要么一起成功,要么一起失敗

舉個例子:A向B轉(zhuǎn)賬萄焦,轉(zhuǎn)賬這個流程中如果出現(xiàn)問題控轿,事務(wù)可以讓數(shù)據(jù)恢復(fù)成原來一樣【A賬戶的錢沒變,B賬戶的錢也沒變】拂封。

事例說明:


        /*
        * 我們來模擬A向B賬號轉(zhuǎn)賬的場景
        *   A和B賬戶都有1000塊茬射,現(xiàn)在我讓A賬戶向B賬號轉(zhuǎn)500塊錢
        *
        * */
            //JDBC默認的情況下是關(guān)閉事務(wù)的,下面我們看看關(guān)閉事務(wù)去操作轉(zhuǎn)賬操作有什么問題

            //A賬戶減去500塊
            String sql = "UPDATE a SET money=money-500 ";
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.executeUpdate();

            //B賬戶多了500塊
            String sql2 = "UPDATE b SET money=money+500";
            preparedStatement = connection.prepareStatement(sql2);
            preparedStatement.executeUpdate();

從上面看冒签,我們的確可以發(fā)現(xiàn)A向B轉(zhuǎn)賬在抛,成功了∠羲。可是如果A向B轉(zhuǎn)賬的過程中出現(xiàn)了問題呢刚梭?下面模擬一下


            //A賬戶減去500塊
            String sql = "UPDATE a SET money=money-500 ";
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.executeUpdate();

            //這里模擬出現(xiàn)問題
            int a = 3 / 0;

            String sql2 = "UPDATE b SET money=money+500";
            preparedStatement = connection.prepareStatement(sql2);
            preparedStatement.executeUpdate();

顯然肠阱,上面代碼是會拋出異常的,我們再來查詢一下數(shù)據(jù)朴读。A賬戶少了500塊錢屹徘,B賬戶的錢沒有增加這明顯是不合理的磨德。


我們可以通過事務(wù)來解決上面出現(xiàn)的問題


            //開啟事務(wù),對數(shù)據(jù)的操作就不會立即生效缘回。
            connection.setAutoCommit(false);

            //A賬戶減去500塊
            String sql = "UPDATE a SET money=money-500 ";
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.executeUpdate();

            //在轉(zhuǎn)賬過程中出現(xiàn)問題
            int a = 3 / 0;

            //B賬戶多500塊
            String sql2 = "UPDATE b SET money=money+500";
            preparedStatement = connection.prepareStatement(sql2);
            preparedStatement.executeUpdate();

            //如果程序能執(zhí)行到這里,沒有拋出異常典挑,我們就提交數(shù)據(jù)
            connection.commit();

            //關(guān)閉事務(wù)【自動提交】
            connection.setAutoCommit(true);

        } catch (SQLException e) {
            try {
                //如果出現(xiàn)了異常,就會進到這里來啦吧,我們就把事務(wù)回滾【將數(shù)據(jù)變成原來那樣】
                connection.rollback();

                //關(guān)閉事務(wù)【自動提交】
                connection.setAutoCommit(true);
            } catch (SQLException e1) {
                e1.printStackTrace();
            }

上面的程序也一樣拋出了異常您觉,A賬戶錢沒有減少,B賬戶的錢也沒有增加授滓。

注意:當Connection遇到一個未處理的SQLException時琳水,系統(tǒng)會非正常退出,事務(wù)也會自動回滾般堆,但如果程序捕獲到了異常在孝,是需要在catch中顯式回滾事務(wù)的。


savapoint

我們還可以使用savepoint設(shè)置中間點淮摔。如果在某地方出錯了私沮,我們設(shè)置中間點,回滾到出錯之前即可和橙。

應(yīng)用場景:現(xiàn)在我們要算一道數(shù)學(xué)題仔燕,算到后面發(fā)現(xiàn)算錯數(shù)了。前面的運算都是正確的魔招,我們不可能重頭再算【直接rollback】晰搀,最好的做法就是在保證前面算對的情況下,設(shè)置一個保存點办斑。從保存點開始重新算外恕。

注意:savepoint不會結(jié)束當前事務(wù),普通提交和回滾都會結(jié)束當前事務(wù)的


事務(wù)的隔離級別

數(shù)據(jù)庫定義了4個隔離級別:

  1. Serializable【可避免臟讀乡翅,不可重復(fù)讀鳞疲,虛讀】
  2. Repeatable read【可避免臟讀,不可重復(fù)讀】
  3. Read committed【可避免臟讀】
  4. Read uncommitted【級別最低峦朗,什么都避免不了】

分別對應(yīng)Connection類中的4個常量

  1. TRANSACTION_READ_UNCOMMITTED
  2. TRANSACTION_READ_COMMITTED
  3. TRANSACTION_REPEATABLE_READ
  4. TRANSACTION_SERIALIZABLE

臟讀:一個事務(wù)讀取到另外一個事務(wù)未提交的數(shù)據(jù)

例子:A向B轉(zhuǎn)賬建丧,A執(zhí)行了轉(zhuǎn)賬語句,但A還沒有提交事務(wù)波势,B讀取數(shù)據(jù)翎朱,發(fā)現(xiàn)自己賬戶錢變多了橄维!B跟A說,我已經(jīng)收到錢了拴曲。A回滾事務(wù)【rollback】争舞,等B再查看賬戶的錢時,發(fā)現(xiàn)錢并沒有多澈灼。


不可重復(fù)讀:一個事務(wù)讀取到另外一個事務(wù)已經(jīng)提交的數(shù)據(jù)竞川,也就是說一個事務(wù)可以看到其他事務(wù)所做的修改

注:A查詢數(shù)據(jù)庫得到數(shù)據(jù),B去修改數(shù)據(jù)庫的數(shù)據(jù)叁熔,導(dǎo)致A多次查詢數(shù)據(jù)庫的結(jié)果都不一樣【危害:A每次查詢的結(jié)果都是受B的影響的委乌,那么A查詢出來的信息就沒有意思了】


虛讀(幻讀):是指在一個事務(wù)內(nèi)讀取到了別的事務(wù)插入的數(shù)據(jù),導(dǎo)致前后讀取不一致荣回。

注:和不可重復(fù)讀類似遭贸,但虛讀(幻讀)會讀到其他事務(wù)的插入的數(shù)據(jù),導(dǎo)致前后讀取不一致


簡單總結(jié):臟讀是不可容忍的心软,不可重復(fù)讀和虛讀在一定的情況下是可以的【做統(tǒng)計的肯定就不行】壕吹。


2.元數(shù)據(jù)

什么是元數(shù)據(jù)

元數(shù)據(jù)其實就是數(shù)據(jù)庫,表删铃,列的定義信息

為什么我們要用元數(shù)據(jù)

即使我們寫了一個簡單工具類耳贬,我們的代碼還是非常冗余。對于增刪改而言猎唁,只有SQL和參數(shù)是不同的咒劲,我們?yōu)楹尾话堰@些相同的代碼抽取成一個方法?對于查詢而言胖秒,不同的實體查詢出來的結(jié)果集是不一樣的缎患。我們要使用元數(shù)據(jù)獲取結(jié)果集的信息,才能對結(jié)果集進行操作阎肝。

  • ParameterMetaData --參數(shù)的元數(shù)據(jù)
  • ResultSetMetaData --結(jié)果集的元數(shù)據(jù)
  • DataBaseMetaData --數(shù)據(jù)庫的元數(shù)據(jù)

3.改造JDBC工具類

問題:我們對數(shù)據(jù)庫的增刪改查都要連接數(shù)據(jù)庫挤渔,關(guān)閉資源,獲取PreparedSteatment對象风题,獲取Connection對象此類的操作判导,這樣的代碼重復(fù)率是極高的,所以我們要對工具類進行增強

增刪改


    //我們發(fā)現(xiàn)沛硅,增刪改只有SQL語句和傳入的參數(shù)是不知道的而已眼刃,所以讓調(diào)用該方法的人傳遞進來

    //由于傳遞進來的參數(shù)是各種類型的,而且數(shù)目是不確定的摇肌,所以使用Object[]

    public static void update(String sql, Object[] objects) {

        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {
            connection = getConnection();
            preparedStatement = connection.prepareStatement(sql);

            //根據(jù)傳遞進來的參數(shù)擂红,設(shè)置SQL占位符的值
            for (int i = 0; i < objects.length; i++) {
                preparedStatement.setObject(i + 1, objects[i]);
            }

            //執(zhí)行SQL語句
            preparedStatement.executeUpdate();

        } catch (Exception e) {
            e.printStackTrace();

查詢


    /*
        1:對于查詢語句來說,我們不知道對結(jié)果集進行什么操作【常用的就是把數(shù)據(jù)封裝成一個Bean對象围小,封裝成一個List集合】
        2:我們可以定義一個接口昵骤,讓調(diào)用者把接口的實現(xiàn)類傳遞進來
        3:這樣接口調(diào)用的方法就是調(diào)用者傳遞進來實現(xiàn)類的方法树碱。【策略模式】

    */
    //這個方法的返回值是任意類型的变秦,所以定義為Object成榜。
    public static Object query(String sql, Object[] objects, ResultSetHandler rsh) {

        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {
            connection = getConnection();
            preparedStatement = connection.prepareStatement(sql);

            //根據(jù)傳遞進來的參數(shù),設(shè)置SQL占位符的值
            if (objects != null) {
                for (int i = 0; i < objects.length; i++) {
                    preparedStatement.setObject(i + 1, objects[i]);
                }
            }

            resultSet = preparedStatement.executeQuery();

            //調(diào)用調(diào)用者傳遞進來實現(xiàn)類的方法蹦玫,對結(jié)果集進行操作
            return rsh.hanlder(resultSet);
    }

接口:


    /*
    * 定義對結(jié)果集操作的接口赎婚,調(diào)用者想要對結(jié)果集進行什么操作,只要實現(xiàn)這個接口即可
    * */
    public interface ResultSetHandler {
         Object hanlder(ResultSet resultSet);

    }

實現(xiàn)類:


//接口實現(xiàn)類樱溉,對結(jié)果集封裝成一個Bean對象
public class BeanHandler implements ResultSetHandler {

    //要封裝成一個Bean對象挣输,首先要知道Bean是什么,這個也是調(diào)用者傳遞進來的饺窿。
    private Class clazz;

    public BeanHandler(Class clazz) {
        this.clazz = clazz;
    }

    @Override
    public Object hanlder(ResultSet resultSet) {

        try {

            //創(chuàng)建傳進對象的實例化
            Object bean = clazz.newInstance();

            if (resultSet.next()) {

                //拿到結(jié)果集元數(shù)據(jù)
                ResultSetMetaData resultSetMetaData = resultSet.getMetaData();

                for (int i = 0; i < resultSetMetaData.getColumnCount(); i++) {

                    //獲取到每列的列名
                    String columnName = resultSetMetaData.getColumnName(i+1);

                    //獲取到每列的數(shù)據(jù)
                    String columnData = resultSet.getString(i+1);

                    //設(shè)置Bean屬性
                    Field field = clazz.getDeclaredField(columnName);
                    field.setAccessible(true);
                    field.set(bean,columnData);
                }

                //返回Bean對象
                return bean;
            }

【策略模式】簡單理解:

  • 我們并不知道調(diào)用者想對結(jié)果集進行怎么樣的操作歧焦,于是讓調(diào)用者把想要做的操作對象傳遞過來
  • 我們只要用傳遞過來的對象對結(jié)果集進行封裝就好了。
    • 至于調(diào)用者會傳遞什么對象過來肚医,該對象要實現(xiàn)什么方法。我們可以使用接口來對其規(guī)范對我個人理解向瓷,策略模式就是我們在使用別人API時肠套,可以使用匿名內(nèi)部類的時候。別人用的就是策略模式猖任。

如果文章有錯的地方歡迎指正你稚,大家互相交流。習慣在微信看技術(shù)文章的同學(xué)朱躺,可以關(guān)注微信公眾號:Java3y

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末刁赖,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子长搀,更是在濱河造成了極大的恐慌宇弛,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件源请,死亡現(xiàn)場離奇詭異枪芒,居然都是意外死亡,警方通過查閱死者的電腦和手機谁尸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進店門舅踪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人良蛮,你說我怎么就攤上這事抽碌。” “怎么了决瞳?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵货徙,是天一觀的道長左权。 經(jīng)常有香客問我,道長破婆,這世上最難降的妖魔是什么涮总? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮祷舀,結(jié)果婚禮上瀑梗,老公的妹妹穿的比我還像新娘。我一直安慰自己裳扯,他們只是感情好抛丽,可當我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著饰豺,像睡著了一般亿鲜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上冤吨,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天蒿柳,我揣著相機與錄音,去河邊找鬼漩蟆。 笑死垒探,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的怠李。 我是一名探鬼主播圾叼,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼捺癞!你這毒婦竟也來了夷蚊?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤髓介,失蹤者是張志新(化名)和其女友劉穎惕鼓,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體版保,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡呜笑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了彻犁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片叫胁。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖汞幢,靈堂內(nèi)的尸體忽然破棺而出驼鹅,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布输钩,位于F島的核電站豺型,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏买乃。R本人自食惡果不足惜姻氨,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望剪验。 院中可真熱鬧肴焊,春花似錦、人聲如沸功戚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽啸臀。三九已至届宠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間乘粒,已是汗流浹背豌注。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留灯萍,地道東北人幌羞。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像竟稳,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子熊痴,可洞房花燭夜當晚...
    茶點故事閱讀 45,086評論 2 355

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

  • 什么是事務(wù)(Transaction)他爸? 是指作為單個邏輯工作單元執(zhí)行的一系列操作,要么完全地執(zhí)行果善,要么完全地不執(zhí)行...
    aluomaidi閱讀 11,612評論 9 44
  • 數(shù)據(jù)庫范式 https://www.zhihu.com/question/24696366 索引 索引可以加快數(shù)據(jù)...
    EakonZhao閱讀 3,213評論 3 23
  • 什么是事務(wù) 事務(wù)是并發(fā)控制的基本單位诊笤。所謂的事務(wù),是一個操作序列巾陕,這些操作要么都執(zhí)行讨跟,要么都不執(zhí)行,是一個不可分割...
    是夏莞也是CiCi閱讀 536評論 1 4
  • Just Do IT By:有信仰的應(yīng)屆汪 ——那天走在新天地南里鄙煤,女朋友突然說起了“Just do it”的笑話...
    寫程序就好閱讀 543評論 0 0
  • 人的成熟有三種境界:發(fā)現(xiàn)自己不是世界的中心梯刚,在最無望的時候仍然拼命努力凉馆,成為了世界的中心卻不想做中心。 青春的歲月...
    二楠閱讀 136評論 0 0