插入多个记录的好设计

时间:2013-08-08 17:52:21

标签: jpa bulkinsert

我正在开发一个程序,该程序从文件读取并使用带有容器管理事务的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行提交到数据库,因为它似乎正在利用缓存预准备语句。当要保留的所有实体都有效时,这可以正常工作。但是,我担心即使我验证实体,它仍然可能由于各种意外原因而失败,并且当任何实体在提交期间抛出异常时,我找不到导致它的特定记录,并且所有实体都已经回滚。

我尝试过另一种方法,使用进程中的以下代码(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();
        }
    }

第二个版本适用于记录错误行,因为捕获和处理各个行导致的异常,但是将相同的5000行提交到数据库需要300多秒。处理大文件时所花费的时间不合理。

是否有任何解决方法可以快速检查并插入记录,同时收到任何失败行的通知?

2 个答案:

答案 0 :(得分: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 :-)
   }
}

答案 1 :(得分: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,因为容器已经决定回滚此事务。