Сохранение объектов JPA в пакетах - TransactionRequiredException - PullRequest
0 голосов
/ 27 августа 2018

У меня есть следующий класс для сохранения сущностей в пакетах с помощью диспетчера сущностей:

@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 метод для сохранения моего списка сущностей, он работает нормально, и сущности сохраняются в базе данных

Но тогда в чем проблема с кодом, который сохраняет объекты в пакетах. Атрибут Transactional также присутствует там. В идеале это должно работать.

Примечание : GenericEntity - это интерфейс, который реализуют все мои классы сущностей.

Я хотел написать многопоточный способ сохранения сущностей, который будет общим. Но как-то это не работает. Если я переберу исходный список и попытаюсь сохранить его, он сработает.

Ответы [ 3 ]

0 голосов
/ 27 августа 2018

Ваша проблема в том, что ваш BatchExecutor не является bean-компонентом Spring, поэтому Spring не может выполнить ту аннотацию @Transactional, которая у вас есть. Возможно, проще всего добавить bean-компонент Spring, который является фабрикой BatchExecutor, чтобы вы могли получить каждый BatchExecutor может быть bean-компонентом Spring. Что-то вроде:

@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 создаст экземпляр этого объекта в виде компонента, заменив реализацию getBatchExecutor () на тот, который создает новый компонент BatchExecutor в виде компонента Spring каждый раз, когда вы вызываете его, вызывая конструктор.

Вы должны сделать свой BatchExecutor bean-объектом с прототипом. Он уже реализует Runnable, поэтому Spring может использовать этот интерфейс при проксировании ваших экземпляров.

0 голосов
/ 27 августа 2018

Просто создайте класс конфигурации

@Configuration
@EnableTransactionManagement
public class PersistenceJPAConfig{

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

@Bean
public PlatformTransactionManager transactionManager(){
  JpaTransactionManager transactionManager
    = new JpaTransactionManager();
  transactionManager.setEntityManagerFactory(
    entityManagerFactoryBean().getObject() );
  return transactionManager;}}
0 голосов
/ 27 августа 2018

Ваши экземпляры BatchExecutor не созданы Spring, поэтому аннотация @Transactional для этого метода не действует.

...