1

I'm facing a problem with Spring Data JPA insert. I've a scheduler that runs after a week. It selects n records from a source table based on certain criteria and then check if a target table, x, exists. If not, the table is created and the selected data inserted. The data that were successfully inserted into the target table are deleted from the source table.

@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();
    }

}

The above runs well immediately when called, but after a while, it throws this exception:

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)
...

So I do not really know why this was thrown and how to solve it. I saw similar question online like Spring - PersistenceContext - No transactional EntityManager available and No transactional EntityManager available Error. My main task was to get 200 records, manipulate and save immediately (commit to DB immediately). I'll be extremely grateful for any assistance. Thanks.

3
  • 2
    there is something you did not consider, the spring proxying. as in your case, most likley CGlib is doing your object proxy since you didn't declare your service based on an interface. you are using transactional methods but a transactional method, is going to be transactioanl only if it is public and the call is made 'outside' of the class. why? since spring proxies your class, it calls ur methods but if you have method calls inside a transactional, spring can't know if that method is really transactional as well. write the create table method in another class and try again. Commented Jun 17, 2020 at 7:43
  • Thanks for your response. I've just implemented what you mentioned above. I extracted the saveAndDelete() and createNewTable() methods into a new component. I'll run a test and if I'll share the result. Thanks. Commented Jun 17, 2020 at 10:58
  • Hi @Guardian I wish to inform you that it worked. Thanks. Commented Jul 3, 2020 at 14:07

1 Answer 1

1

There is something you did not consider, the spring proxying. as in your case, most likley CGlib is doing your object proxy since you didn't declare your service based on an interface. you are using transactional methods but a transactional method, is going to be transactioanl only if it is public and the call is made 'outside' of the class. why? since spring proxies your class, it calls ur methods but if you have method calls inside a transactional, spring can't know if that method is really transactional as well. write the create table method in another class and try again.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.