Нет доступного транзакционного EntityManager Исключение - PullRequest
0 голосов
/ 16 июня 2020

У меня проблема со вставкой Spring Data JPA. У меня есть планировщик, который запускается через неделю. Он выбирает n записей из исходной таблицы на основе определенных критериев, а затем проверяет, существует ли целевая таблица x. Если нет, таблица будет создана и выбранные данные вставлены. Данные, которые были успешно вставлены в целевую таблицу, удаляются из исходной таблицы.

@Component
public class ArchiveService {

    // Some dependencies here!!!

    @Transactional
    public int archiveSmsEntries(Month month, Year year) {
        // ...
        this.createNewTable(suffix);
        // ...
        // LocalDateTime start;
        // LocalDateTime end;

        int size = 200;
        boolean running = true;
        while (running) {
            Page<SmsEntry> entries = smsService.findAllForArchiving(start, end, PageRequest.of(0, size, Sort.Direction.ASC, "created"));
            result += this.saveAndDelete(entries.getContent());
            running = entries.getTotalElements() > 0;
        }

    }


    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void createNewTable(String suffix) {
        if (repo.tableExists(ArchiverUtils.table + suffix)) {
            log.warn("Archive already exists.");
        } else {
            Session session = em.unwrap(Session.class);
            session.doWork(connection -> {
                //// ...
                // Create table
                //// ...
            });
        }
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public int saveAndDelete(List<SmsEntry> entries) {

        if (entries.isEmpty())
            return 0;

        AtomicInteger r = new AtomicInteger(0);
        try {
            Session session = em.unwrap(Session.class);
            session.doWork(connection -> {
                boolean autoCommit = connection.getAutoCommit();
                connection.setAutoCommit(true);
                PreparedStatement ps = connection.prepareStatement("INSERT STATEMENT");

                for (SmsEntry entry : entries) {
                    ps.setInt(1, entry.getId());
                    ps.setDouble(2, entry.getCost());

                    ps.addBatch();
                }

                r.set(Arrays.stream(ps.executeBatch()).sum());
                if (r.get() > 0) {
                    entryService.deleteAllById(entries.stream().map(SmsEntry::getId).collect(Collectors.toList()));
                }

                ps.close();
                connection.setAutoCommit(autoCommit);
            });
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return r.get();
    }

}

Приведенное выше работает сразу при вызове, но через некоторое время выдает следующее исключение:

org.springframework.dao.InvalidDataAccessApiUsageException: No transactional EntityManager available; nested exception is java.lang.IllegalStateException: No transactional EntityManager available
    at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:371)
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:257)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:528)
    at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
    at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:153)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691)
...

Так что я действительно не знаю, почему это было брошено и как это решить. Я видел аналогичный вопрос в Интернете, например Spring - PersistenceContext - Нет доступного транзакционного EntityManager и Нет доступного транзакционного EntityManager Ошибка . Моя основная задача заключалась в том, чтобы получить 200 записей, обработать и немедленно сохранить (немедленно зафиксировать в БД). Буду безмерно благодарен за любую помощь. Спасибо.

1 Ответ

1 голос
/ 13 июля 2020

Есть кое-что, что вы не учли, весеннее проксирование. как и в вашем случае, скорее всего, CGlib выполняет прокси-сервер вашего объекта, поскольку вы не объявили свою службу на основе интерфейса. вы используете транзакционные методы, но транзакционный метод будет транзакционным, только если он имеет значение publi c и вызов выполняется «вне» класса. Зачем? поскольку Spring прокси-серверы вашего класса, он вызывает методы ur, но если у вас есть вызовы методов внутри транзакции, Spring не может знать, действительно ли этот метод транзакционный. напишите метод создания таблицы в другом классе и повторите попытку.

...