rose jade處理DELETE語句時吟税,偶爾報錯

背景

項目中使用了paoding-rose作為開發(fā)框架凹耙,該框架作為國產(chǎn)的一個十分優(yōu)秀的框架,在Jade方面處理的也非常好肠仪,但是在實際的使用過程中肖抱,發(fā)現(xiàn)了一個很有意思的問題,在使用Delete SQL語句批量刪除數(shù)據(jù)時异旧,當傳參進去只有一個參數(shù)時意述,會因為返回類型的不知道導致拋異常。

這里還是先把解決方案說了好了,升級到最新2.0以上版本即可我們使用的是比較老的1.2.2版本

然后下面為了備忘荤崇,特別記錄一下這個蠻有意思的問題拌屏。
Git上可以參考這個Issue


問題描述

問題主要體現(xiàn)為,偶然性會報ClassCastException錯誤术荤,具體表現(xiàn)形式如下:

java.lang.ClassCastException: [I cannot be cast to java.lang.Integer
    at com.sun.proxy.$Proxy51.deleteRecommendListByIDs(Unknown Source)
    at com.xx.service.xxxService.generateDefaultRecommendList(xxxService.java:214)
    at com.xx.service.xxxService.main(xxxService.java:951)

其錯誤來源于通過@SQL的注解實現(xiàn)了SQL語句的執(zhí)行地方倚喂,具體的SQL如下:

@SQL(" DELETE FROM videolist_home_recommend "
    +" WHERE ID IN ( :delRecords ) ")
public Integer deleteRecommendListByIDs(@SQLParam("delRecords") List<Integer> delRecords);

以上就是這個錯誤產(chǎn)生的基本現(xiàn)象,但是有意思的該問題并非一定會產(chǎn)生瓣戚,根據(jù)詳細的測試和分析發(fā)現(xiàn)端圈,只有在傳參List中元素個數(shù)大于1個以上時,會導致該錯誤的發(fā)生子库。


原因分析

  • 首先分析了這個現(xiàn)象以后覺得可能是傳參進入的問題舱权,導致了List轉化存在的問題,但是后來請教了同事仑嗅,其實rose這里和數(shù)據(jù)庫中一般SQL語句的返回是不一致的宴倍,數(shù)據(jù)庫中返回的是影響的行數(shù),但是Rose中是一個是否更新成功的int[]仓技,其中1代表處理成功啊楚,0代表處理失敗(具體為什么這么處理后面會分析到)浑彰。還有這里有個需要反省的就是恭理,對getName()這個方法不熟悉,其實JVM已經(jīng)告訴了我們問題點郭变,出問題的類型是"[I"颜价,只不過沒反應過來,括號哭~

String java.lang.Class.getName()
Returns the name of the entity (class, interface, array class, primitive type, or void) represented by this Class object, as a String.
……
If this class object represents a class of arrays, then the internal form of the name consists of the name of the element type preceded by one or more '[' characters representing the depth of the array nesting. The encoding of element type names is as follows:
……
Element Type
boolean ??Z
byte ???B
……
int????I
long???J
short ???S

  • 所以之后根據(jù)這個返回值將SQL方法的返回值替換成為int[]類型诉濒,然而有趣的是這次反而在參數(shù)list只有一個值時報錯了周伦,于是進一步debug到源碼中去看了一下(由于github上找不到1.2.2版本的內(nèi)容,又找了好久source文件)

  • 這里就發(fā)現(xiàn)rose jade中關于這個處理很有意思:

  1. 在rose中未荒,SQL語句只有兩種類型:READ和WRITE专挪,其中show、select等查詢類的屬于READ類型片排,而update寨腔、delete等則屬于WRITE類型。
  2. 在WRITE類型中率寡,會將ID IN (:list) 這種語句自動翻譯成Batch語句來處理迫卢。但是在轉換成Batch來處理時,在傳入的list中只有一個值時冶共,又會根據(jù)參數(shù)個數(shù)乾蛤,將Batch轉化成了SingleBatch每界,因此造成了這種不統(tǒng)一的情況(兩者的返回值不一樣,一個是Integer家卖,一個是int[])
   @Override
    public Object execute(SQLType sqlType, StatementRuntime... runtimes) {
        switch (runtimes.length) {
            case 0:
                return 0;
            case 1:
                return executeSingle(runtimes[0], returnType);
            default:
                return executeBatch(runtimes);
        }
    }
  1. 因此對照著源碼又看了一下新版本眨层,發(fā)現(xiàn)已經(jīng)對executeBatch做了修正,增加了對一般數(shù)據(jù)庫SQL返回影響行數(shù)的支持(有趣的是上荡,也并沒有放棄之前對int[]的支持趴樱,雖然可以理解為向下兼容,但是個人覺得其實也是想保持這種有趣的對數(shù)據(jù)處理的理解吧榛臼,挺有意思的)
  • 1.2.2的版本
    private Object executeBatch(StatementRuntime... runtimes) {
        int[] updatedArray = new int[runtimes.length];
        Map<String, List<StatementRuntime>> batchs = new HashMap<String, List<StatementRuntime>>();
        for (int i = 0; i < runtimes.length; i++) {
            StatementRuntime runtime = runtimes[i];
            List<StatementRuntime> batch = batchs.get(runtime.getSQL());
            if (batch == null) {
                batch = new LinkedList<StatementRuntime>();
                batchs.put(runtime.getSQL(), batch);
            }
            runtime.setProperty("_index_at_batch_", i); // 該runtime在batch中的位置
            batch.add(runtime);
        }
        // TODO: 多個真正的batch可以考慮并行執(zhí)行~待定
        for (Map.Entry<String, List<StatementRuntime>> batch : batchs.entrySet()) {
            String sql = batch.getKey();
            List<StatementRuntime> batchRuntimes = batch.getValue();
            StatementRuntime runtime = batchRuntimes.get(0);
            DataAccess dataAccess = dataAccessProvider.getDataAccess(//
                    runtime.getMetaData(), runtime.getProperties());
            List<Object[]> argsList = new ArrayList<Object[]>(batchRuntimes.size());
            for (StatementRuntime batchRuntime : batchRuntimes) {
                argsList.add(batchRuntime.getArgs());
            }
            int[] batchResult = dataAccess.batchUpdate(sql, argsList);
            if (batchs.size() == 1) {
                updatedArray = batchResult;
            } else {
                int index_at_sub_batch = 0;
                for (StatementRuntime batchRuntime : batchRuntimes) {
                    Integer _index_at_batch_ = batchRuntime.getProperty("_index_at_batch_");
                    updatedArray[_index_at_batch_] = batchResult[index_at_sub_batch++];
                }
            }
        }
        return updatedArray;
    }
  • 2.0u8的版本
    private Object executeBatch(StatementRuntime... runtimes) {
        int[] updatedArray = new int[runtimes.length];
        Map<String, List<StatementRuntime>> batchs = new HashMap<String, List<StatementRuntime>>();
        for (int i = 0; i < runtimes.length; i++) {
            StatementRuntime runtime = runtimes[i];
            List<StatementRuntime> batch = batchs.get(runtime.getSQL());
            if (batch == null) {
                batch = new ArrayList<StatementRuntime>(runtimes.length);
                batchs.put(runtime.getSQL(), batch);
            }
            runtime.setAttribute("_index_at_batch_", i); // 該runtime在batch中的位置
            batch.add(runtime);
        }
        // TODO: 多個真正的batch可以考慮并行執(zhí)行(而非順序執(zhí)行)~待定
        for (Map.Entry<String, List<StatementRuntime>> batch : batchs.entrySet()) {
            String sql = batch.getKey();
            List<StatementRuntime> batchRuntimes = batch.getValue();
            StatementRuntime runtime = batchRuntimes.get(0);
            DataAccess dataAccess = dataAccessFactory.getDataAccess(//
                runtime.getMetaData(), runtime.getAttributes());
            List<Object[]> argsList = new ArrayList<Object[]>(batchRuntimes.size());
            for (StatementRuntime batchRuntime : batchRuntimes) {
                argsList.add(batchRuntime.getArgs());
            }
            int[] batchResult = dataAccess.batchUpdate(sql, argsList);
            if (batchs.size() == 1) {
                updatedArray = batchResult;
            } else {
                int index_at_sub_batch = 0;
                for (StatementRuntime batchRuntime : batchRuntimes) {
                    Integer _index_at_batch_ = batchRuntime.getAttribute("_index_at_batch_");
                    updatedArray[_index_at_batch_] = batchResult[index_at_sub_batch++];
                }
            }
        }
        if (returnType == void.class) {
            return null;
        }
        if (returnType == int[].class) {
            return updatedArray;
        }
        if (returnType == Integer.class || returnType == Boolean.class) {
            int updated = 0;
            for (int value : updatedArray) {
                updated += value;
            }
            return returnType == Boolean.class ? updated > 0 : updated;
        }
        throw new InvalidDataAccessApiUsageException(
            "bad return type for batch update: " + runtimes[0].getMetaData().getMethod());
    }

小結

雖然可以理解為是框架本身的一個問題,但是發(fā)現(xiàn)對本身框架使用中的一些細節(jié)了解的不夠深刻窜司,已經(jīng)對一些不太常見但很有用的信息熟悉不足沛善,導致了還是花費了一些時間在這個問題上。
但總體來說還是蠻有意思的一個問題塞祈,一是記錄下來給自己做一個回顧金刁,二是如果能幫助到遇到同樣問題的同學就太好了~

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市议薪,隨后出現(xiàn)的幾起案子尤蛮,更是在濱河造成了極大的恐慌,老刑警劉巖斯议,帶你破解...
    沈念sama閱讀 222,627評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件产捞,死亡現(xiàn)場離奇詭異,居然都是意外死亡哼御,警方通過查閱死者的電腦和手機坯临,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來恋昼,“玉大人看靠,你說我怎么就攤上這事∫杭。” “怎么了挟炬?”我有些...
    開封第一講書人閱讀 169,346評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長嗦哆。 經(jīng)常有香客問我谤祖,道長,這世上最難降的妖魔是什么老速? 我笑而不...
    開封第一講書人閱讀 60,097評論 1 300
  • 正文 為了忘掉前任泊脐,我火速辦了婚禮,結果婚禮上烁峭,老公的妹妹穿的比我還像新娘容客。我一直安慰自己秕铛,他們只是感情好,可當我...
    茶點故事閱讀 69,100評論 6 398
  • 文/花漫 我一把揭開白布缩挑。 她就那樣靜靜地躺著但两,像睡著了一般。 火紅的嫁衣襯著肌膚如雪供置。 梳的紋絲不亂的頭發(fā)上谨湘,一...
    開封第一講書人閱讀 52,696評論 1 312
  • 那天,我揣著相機與錄音芥丧,去河邊找鬼紧阔。 笑死,一個胖子當著我的面吹牛续担,可吹牛的內(nèi)容都是我干的擅耽。 我是一名探鬼主播,決...
    沈念sama閱讀 41,165評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼物遇,長吁一口氣:“原來是場噩夢啊……” “哼乖仇!你這毒婦竟也來了?” 一聲冷哼從身側響起询兴,我...
    開封第一講書人閱讀 40,108評論 0 277
  • 序言:老撾萬榮一對情侶失蹤乃沙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后诗舰,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體警儒,經(jīng)...
    沈念sama閱讀 46,646評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,709評論 3 342
  • 正文 我和宋清朗相戀三年眶根,在試婚紗的時候發(fā)現(xiàn)自己被綠了冷蚂。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,861評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡汛闸,死狀恐怖蝙茶,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情诸老,我是刑警寧澤隆夯,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站别伏,受9級特大地震影響蹄衷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜厘肮,卻給世界環(huán)境...
    茶點故事閱讀 42,196評論 3 336
  • 文/蒙蒙 一愧口、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧类茂,春花似錦耍属、人聲如沸托嚣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽示启。三九已至,卻和暖如春领舰,著一層夾襖步出監(jiān)牢的瞬間夫嗓,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評論 1 274
  • 我被黑心中介騙來泰國打工冲秽, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留舍咖,地道東北人。 一個月前我還...
    沈念sama閱讀 49,287評論 3 379
  • 正文 我出身青樓锉桑,卻偏偏與公主長得像排霉,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子刨仑,可洞房花燭夜當晚...
    茶點故事閱讀 45,860評論 2 361

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

  • 背景 一年多以前我在知乎上答了有關LeetCode的問題, 分享了一些自己做題目的經(jīng)驗郑诺。 張土汪:刷leetcod...
    土汪閱讀 12,748評論 0 33
  • 轉至元數(shù)據(jù)結尾創(chuàng)建: 董瀟偉夹姥,最新修改于: 十二月 23, 2016 轉至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,732評論 0 9
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理杉武,服務發(fā)現(xiàn),斷路器辙售,智...
    卡卡羅2017閱讀 134,715評論 18 139
  • Spark SQL, DataFrames and Datasets Guide Overview SQL Dat...
    Joyyx閱讀 8,328評論 0 16
  • 二寶快八個月了~四月份產(chǎn)假休完轻抱,因為沒人帶寶,我也只能辭了工作再次成為全職媽媽旦部!之前因為大寶我也做了十年的全職媽媽...
    buibui_fly閱讀 376評論 2 2