rose jadeがDELETE文を処理する場合、たまにエラーが発生します

7762 ワード

背景
プロジェクトでは開発フレームワークとしてpaoding-roseを使用しており、このフレームワークは国産の非常に優れたフレームワークとしてJade側でもよく処理されているが、実際の使用過程で興味深い問題が発見され、Delete SQL文を使用してデータを一括削除する際に、パラメータが1つしか伝わらない場合、戻りタイプが分からないために投げ異常になります.
ここではまずソリューションを説明して、最新2.0以上のバージョンにアップグレードすればいいです(古い1.2.2バージョンを使用しています)
そして忘れないように、面白い質問をメモしておきます.GitではこのIssueを参考にすることができます
問題の説明
問題は主に、偶然にClassCastExceptionエラーを報告し、具体的な表現形式は以下の通りである.
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の注釈によってSQL文が実行された場所に由来します.具体的なSQLは次のとおりです.
@SQL(" DELETE FROM videolist_home_recommend "
    +" WHERE ID IN ( :delRecords ) ")
public Integer deleteRecommendListByIDs(@SQLParam("delRecords") List delRecords);

以上がこのエラー発生の基本的な現象であるが,興味深いこの問題は必ずしも発生するわけではないが,詳細なテストや解析から,参照リストにおける要素の個数が1以上ある場合にのみ,そのエラーが発生することが分かった.
原因分析
  • まず、この現象を分析した後、パスインの問題かもしれないと感じ、リスト変換に問題があったが、同僚に聞いてみると、roseここではデータベースの一般的なSQL文の戻りと一致せず、データベースでは影響する行数を返しているが、Roseでは更新に成功したかどうかのint[]であり、そのうち1は処理に成功したことを表している.0は処理に失敗したことを表します(具体的には、このような処理は後で分析されます).またここで反省しなければならないのは、getName()という方法に慣れていないことですが、実はJVMはすでに問題点を教えてくれていて、問題のタイプは「[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
  • ですのでこの戻り値によってSQLメソッドの戻り値をint[]タイプに置き換えますが、興味深いことに今回は逆にパラメータリストに1つの値しかないので時報が間違っていたので、さらにdebugをソースコードに見てみました(githubでは1.2.2バージョンの内容が見つからないので、長い間sourceファイルが見つかりました)
  • ここでrose jadeの中でこの処理についてとても面白いことを発見します:
  • roseでは、SQL文はREADとWRITEの2つのタイプしかありません.show、selectなどのクエリークラスはREADタイプに属し、update、deleteなどはWRITEタイプに属します.
  • WRITEタイプでは、ID IN(:list)という文を自動的にBatch文に翻訳して処理します.しかし、Batchに変換して処理すると、入ってきたlistに1つの値しかない場合、パラメータの個数に応じてBatchをSingleBatchに変換するため、このような不統一な状況(両者の戻り値が異なり、1つはInteger、1つは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);
            }
        }
    
  • それでソースコードに照らしてまた新しいバージョンを見て、すでにexecuteBatchに対して修正を行ったことを発見して、普通のデータベースSQLに対して返して行数に影響する支持を増加しました(興味深いことに、同じく放棄する前にint[]に対してのサポートは、下向き対応と理解できますが、個人的にはこのような面白いデータ処理の理解を維持したいと思っています.面白いですね)
  • 1.2.2のバージョン
  •     private Object executeBatch(StatementRuntime... runtimes) {
            int[] updatedArray = new int[runtimes.length];
            Map> batchs = new HashMap>();
            for (int i = 0; i < runtimes.length; i++) {
                StatementRuntime runtime = runtimes[i];
                List batch = batchs.get(runtime.getSQL());
                if (batch == null) {
                    batch = new LinkedList();
                    batchs.put(runtime.getSQL(), batch);
                }
                runtime.setProperty("_index_at_batch_", i); //  runtime batch    
                batch.add(runtime);
            }
            // TODO:      batch        ~  
            for (Map.Entry> batch : batchs.entrySet()) {
                String sql = batch.getKey();
                List batchRuntimes = batch.getValue();
                StatementRuntime runtime = batchRuntimes.get(0);
                DataAccess dataAccess = dataAccessProvider.getDataAccess(//
                        runtime.getMetaData(), runtime.getProperties());
                List argsList = new ArrayList(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.0 u 8のバージョン
  •     private Object executeBatch(StatementRuntime... runtimes) {
            int[] updatedArray = new int[runtimes.length];
            Map> batchs = new HashMap>();
            for (int i = 0; i < runtimes.length; i++) {
                StatementRuntime runtime = runtimes[i];
                List batch = batchs.get(runtime.getSQL());
                if (batch == null) {
                    batch = new ArrayList(runtimes.length);
                    batchs.put(runtime.getSQL(), batch);
                }
                runtime.setAttribute("_index_at_batch_", i); //  runtime batch    
                batch.add(runtime);
            }
            // TODO:      batch        (      )~  
            for (Map.Entry> batch : batchs.entrySet()) {
                String sql = batch.getKey();
                List batchRuntimes = batch.getValue();
                StatementRuntime runtime = batchRuntimes.get(0);
                DataAccess dataAccess = dataAccessFactory.getDataAccess(//
                    runtime.getMetaData(), runtime.getAttributes());
                List argsList = new ArrayList(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());
        }
    

    小結
    フレームワーク自体の問題として理解できるが、自分のフレームワークの使用中の細部について深く理解していないことに気づき、あまりよく見られないが役に立つ情報に慣れていないことに気づき、この問題に時間を費やした.しかし、総体的には興味深い問題である.一つは記録して自分に振り返り、二つは同じ問題に遭遇した同級生を助けてくれればよかったのに~