批量保存JPA实体-TransactionRequiredException

时间:2018-08-27 15:35:27

标签: java spring-boot jpa entitymanager

我具有以下使用实体管理器批量保存实体的类:

@Repository
@Transactional
public class AbstractRepositoryAdapter
{
    private static final Logger logger  = LoggerFactory.getLogger(AbstractRepositoryAdapter.class);
    @PersistenceContext
    private EntityManager entityManager;

    @Transactional
    public <O extends GenericEntity> void save(List<O> entities){
        entities.forEach(entity->{
            entityManager.persist(entity);          
        }); 
        System.out.println("saved from save(List<O> entities)");
    }
    @Transactional
    public  <O extends GenericEntity> void write(List<O> entities)
    {
        if (!CollectionUtils.isEmpty(entities))
        {
            List<List<O>> listOfList = CollectionHelper.split(entities, 1000);

            ExecutorService executorService = Executors.newFixedThreadPool(10);
            List<Future<?>> futures = new ArrayList<Future<?>>();

            for (List<O> subEntities : listOfList)
            {
                Future<?> future = executorService.submit(new BatchExecutor<O>(entityManager, subEntities));
                futures.add(future);
            }

            ObjectHelper.wait(futures.toArray(new Future<?>[0]));// wait until all tasks are finished

            executorService.shutdown();
        }
    }
    @Transactional
    private static class BatchExecutor<O extends GenericEntity> implements Runnable
    {
        private EntityManager em    = null;
        private List<O>         entities    = null;


        public BatchExecutor(EntityManager em, List<O> entities)
        {
            this.em=em;
            this.entities = entities;
        }

        @Override
        @Transactional
        public void run()
        {
            String sql = "";
            try 
            {
                entities.forEach(entity->{
                    em.persist(entity);
                });

            }
            catch (Exception e)
            {
                logger.debug("Unable to save the entities for -> {} ", sql);
            }

        }
    }
}

如果我尝试使用 write 方法保存我的实体列表,则会收到以下异常:

javax.persistence.TransactionRequiredException: No transactional EntityManager available

但是,如果我使用 save 方法保存我的实体列表,则它可以正常工作并将实体保存到数据库中

但是,成批保存实体的代码又有什么问题呢?事务属性也存在于此。理想情况下,它应该可以工作。

注意:GenericEntity是我所有实体类都实现的接口。

我想写一种多线程的方法来保存实体,这是通用的。但是它不起作用。如果我遍历原始列表并尝试保存它,那么它将起作用。

3 个答案:

答案 0 :(得分:0)

您的BatchExecutor实例不是由Spring创建的,因此方法上的@Transactional注释无效。

答案 1 :(得分:0)

您的问题是您的BatchExecutor不是Spring Bean,因此Spring无法兑现那里的@Transactional批注。最简单的事情可能是添加一个Spring Batch bean,它是BatchExecutor工厂,这样您就可以使每个BatchExecutor都可以是Spring bean。像这样:

@Component
public class BatchExecutorFactory {
    @Lookup <O extends GenericEntity> BatchExecutor<O> getBatchExecutor(EntityManager em, List<O> entities) {
        return new BatchExecutor<O>(em, entities);  // for non-Spring use?
     }

    private class BatchExecutor<O extends GenericEntity> implements Runnable {
        private EntityManager em    = null;
        private List<O>         entities    = null;

        public BatchExecutor(EntityManager em, List<O> entities)
        {
            this.em=em;
            this.entities = entities;
        }

        @Override
        @Transactional
        public void run()
        {
            try 
            {
                entities.forEach(entity->{
                    em.persist(entity);
                });
            }
            catch (Exception e)
            {
                System.out.println("Unable to save the entities for -> {} ");
            }

        }
    }
}

Spring会将其实例化为bean,用调用构造函数的方法将getBatchExecutor()的实现替换为每次将其创建为Spring bean的新BatchExecutor的实现。

您需要使BatchExecutor成为原型作用域的bean。它已经实现了Runnable,因此Spring在代理实例时可以使用该接口。

答案 2 :(得分:0)

只需创建一个配置类

@Configuration
@EnableTransactionManagement
public class PersistenceJPAConfig{

@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(){
  //...}

@Bean
public PlatformTransactionManager transactionManager(){
  JpaTransactionManager transactionManager
    = new JpaTransactionManager();
  transactionManager.setEntityManagerFactory(
    entityManagerFactoryBean().getObject() );
  return transactionManager;}}