1

ファイルから読み取り、JTA/EclipseLink 2.3.x JPA とコンテナー管理トランザクションを使用して Oracle 11g データベースに行ごとに挿入するプログラムに取り組んでいます。

以下のコードを開発しましたが、失敗した行を認識して手動で修正する必要があるという事実に悩まされています。

public class CreateAccount {
    @PersistenceContext(unitName="filereader")
    private EntityManager em;
    private ArrayList<String> unprocessed;

    public void upload(){
        //reading the file into unprocessed
        for (String s : unprocessed) {
            this.process(s);
        }
    }

    private void process(String s){
        //Setting the entity with appropriate properties.
        //Validate the entity
        em.persist(account);
    }
}

この最初のバージョンでは、5000 行をデータベースにコミットするのに数秒かかります。これは、準備されたステートメントのキャッシュを利用しているようです。永続化するすべてのエンティティが有効な場合、これは正常に機能します。ただし、エンティティを検証しても、さまざまな予期しない理由で失敗する可能性があり、エンティティがコミット中に例外をスローすると、それを引き起こした特定のレコードが見つからず、すべてのエンティティが検証されていないことが懸念されます。ロールバックしました。

次のコード in process(String s) を使用して、管理されたトランザクションを使用せずに、新しいトランザクションを開始して行ごとにコミットする別のアプローチを試しました。

    for (String s : unprocessedLines) {
        try {
            em.getTransaction().begin();
            this.process(s);
            em.getTransaction().commit();
        } catch (Exception e) {
            // Any exception that a line caused can be caught here
            e.printStackTrace();
        }
    }

2 番目のバージョンは、個々の行に起因する例外がキャッチされて処理されるため、誤った行をログに記録するのに適していますが、同じ 5000 行をデータベースにコミットするには 300 秒以上かかります。大きなファイルが処理されている場合、かかる時間は合理的ではありません。

レコードをすばやく確認して挿入し、同時に失敗した行が通知される回避策はありますか?

4

2 に答える 2

0

これは推測の可能性が高いですが、トランザクションを保持してバッチでコミットしようとしないのはなぜですか。その後、同時にロールバック例外を保持して速度を維持します。

try {
 em.getTransaction().begin();
 for (String s : unprocessedLines) {
            this.process(s);
    }
 em.getTransaction().commit();
} catch (RollbackException exc) { 
 // here you have your rollback reason 
} finally {
  if(em.getTransaction.isActive()) {
      em.getTransaction.rollback(); // well of course you should declare em.getTransaction as a varaible above instead of constantly invoking it as I do :-)
   }
}
于 2013-08-10T20:19:06.660 に答える
0

私の解決策は二分探索であることが判明し、ツリーの深さを最小限に抑えるために、たとえば last = first + 1023 などの妥当な数のブロックから始めました。

ただし、これはエラーが決定論的である場合にのみ機能し、エラー率が非常に高い場合に各レコードを一度コミットするよりも悪いことに注意してください。

private boolean batchProcess(int first, int last){
    try {
        em.getTransaction().begin();
        for (String s : unprocessedLines.size(); i++) {
            this.process(s);
        }
        em.getTransaction().commit();
    } catch (Exception e) {
        e.printStackTrace();
        if(em.getTransaction.isActive()) {
            em.getTransaction.rollback();
        }
        if( first == last ){
            failedLine.add(unprocessedLines(first));
        } else {
            int mid = (first + last)/2+1
            batchProcess(first, mid-1);
            batchProcess(mid, last);
        }
    }
}

コンテナー管理トランザクションの場合、トランザクションのコンテキストからバイナリ検索を実行する必要がある場合があります。そうしないとRollbackException、コンテナーがこのトランザクションをロールバックすることを既に決定しているためです。

于 2015-08-29T11:14:39.537 に答える