【问题标题】:Batch Insert with JTA (EntityManager cannot use getTransaction)使用 JTA 批量插入(EntityManager 不能使用 getTransaction)
【发布时间】:2013-11-13 21:42:12
【问题描述】:

我正在使用 Spring Data 和 Apache Camel 进行项目,我们有 2 个数据库,Sql Server 和 Oracle 以及 JTA。出现问题是因为我需要从一个大文件(大约 10000000 条记录)中插入数据,所以我决定使用批量插入作为:

@PersistenceContext(unitName="persistenceUnitSql")
EntityManager em;

public void insertBatch() {
    em.getTransaction().begin();
    for (int i = 1; i <= 1000000; i++) {
      Point point = new Point(i, i);
      em.persist(point);
      if ((i % 10000) == 0) {
          em.getTransaction().commit();
          em.clear();          
          em.getTransaction().begin();
      }
    }
    em.getTransaction().commit();
}

但是这个问题发生了:

    A JTA EntityManager cannot use getTransaction()

任何帮助...

【问题讨论】:

  • 为什么需要多笔交易?通常,flush 后跟 clear 就足够了。然后简单地用@Transactional 标记insertBatch 方法,你应该会很好。或者甚至更好地使用 Spring Batch 而不是滚动您自己的批处理解决方案......
  • 我们正在使用带有2个数据库的spring数据,负责对对象进行持久化的类扩展自CrudRepository。此类具有同时插入单个记录的方法。我必须使用 apache camel 从 txt 文件中读取每一行,然后将这一行插入数据库。对于大文件 (300MB),该过程需要很长时间。
  • 既然可以使用弹簧批,为什么还要自己动手做呢?但正如提到的flushclear 以及一般工作中的单个事务。对于一般的 JTA,尤其是在具有多个事务资源的情况下,希望使用容器管理的事务而不是自己做。

标签: spring apache-camel spring-data batch-processing entitymanager


【解决方案1】:

自我控制 JTA 事务似乎比预期的要困难得多。我通常使用的一种解决方法是拥有一个单独的“服务”来执行一批插入,然后在该方法上设置 REQUIRES_NEW 事务传播策略,所以它看起来像:

class FooService {

    private PointPersister pointPersister;

    @Transactional(propagation=REQUIRED)
    public void insertBatch() {
        List<Point> points = new ArrayList<Point>(10000);
        for (int i = 1; i <= 1000000; i++) {
            points.add(new Point(i,1));
            if ((i % 10000) == 0) {
                pointPersister.insertPointBatch(points);
            }
        }
    }
}

class PointPersisterImpl implements PointPersister {
    @PersistenceContext
    private EntityManager em;

    @Transactional(propagation=REQUIRES_NEW)    // in a separate txn
    public void insertPointBatch(List<Point> points) {
        // persist points
    }
}

您还可以做出其他选择来避免处理麻烦且容易出错的手动事务处理。 Spring Batch 是可能的解决方案之一。

【讨论】:

    【解决方案2】:

    我通过以下方式解决了这个问题:

    @PersistenceUnit(unitName="persistenceUnitSql") 
    // from persistence xml <persistence-unit name="persistenceUnitSql" transaction-type="JTA"> 
    private EntityManagerFactory emf;
    
    @Autowired
    private JtaTransactionManager transactionManagerSqlServer;
    // from application-context.xml
    //<bean class="org.springframework.transaction.jta.JtaTransactionManager" id="transactionManagerSqlServer" />
    
    public List<Point> insertBatch(List<Point> datos) {
    
        try {
            UserTransaction transaction = transactionManagerSqlServer.getUserTransaction();     
            transaction.begin();
            EntityManager em = emf.createEntityManager();
    
            for (Point punto : datos) {
                em.persist(punto);
            }
            transaction.commit();
            em.close();
        } catch (NotSupportedException e) {
            LOGGER.error(e);
        } catch (SystemException e) {
            LOGGER.error(e);
        } catch (SecurityException e) {
            LOGGER.error(e);
        } catch (IllegalStateException e) {
            LOGGER.error(e);
        } catch (RollbackException e) {
            LOGGER.error(e);
        } catch (HeuristicMixedException e) {
            LOGGER.error(e);
        } catch (HeuristicRollbackException e) {
            LOGGER.error(e);
        }       
    
        return datos;
    }
    

    【讨论】:

      猜你喜欢
      • 2012-06-13
      • 2012-05-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-30
      • 1970-01-01
      • 1970-01-01
      • 2016-07-18
      相关资源
      最近更新 更多