Search code examples
hibernatemariadbnested-transactions

Hibernate REQUIRES_NEW or NESTED not working with MariaDB InnoDB


I have a potentially long-running transaction in JBoss-EAP 7.2.6 where I need to commit in several steps.

I understand that MariaDB has no nested, but chained transactions. This is my idea of how this should work:

public class MyEntityManager implements IMyEntityManager {
    @PersistenceContext(unitName = "org.x.y.z")
    protected EntityManager em;

    @Transactional(timeout = 900) // 15 minutes
    public void doTransaction(MyEntity me) {
        // Spring has a misfeature that @Transactional only applies when called from the outside.
        // Calling through an external reference works around this shortcoming. :-(
        MyEntityManager self = this;

        self.persist(me);
        publish(me.id); // Pseudocode, my read-only client eagerly waiting for id.

        // Fill me with more data
        self.flush();
        // Fill me with more data
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    private void persist(MyEntity me) {
        em.persist(me);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    private void flush() {
        em.flush();
    }
}

I tried going to NESTED, and upgrading driver from 2.4.3 to 2.5.4 to no avail.

How to get such a scenario to work with InnoDB? I'd be willing to issue a low level COMMIT AND CHAIN myself, as there seems to be no API for doing so in @Spring/Hibernate.

I tried

em.createNativeQuery("COMMIT AND CHAIN").executeUpdate();

but this leads to

XAER_RMFAIL: The command cannot be executed when global transaction is in the  ACTIVE state

while

em.unwrap(Session.class).createSQLQuery("COMMIT AND CHAIN").executeUpdate();

leads to

GenericJDBCException: could not execute statement

Solution

  • Just copying this to self (which I'd found elsewhere) is not enough. It needs self = applicationContext.getBean(IMyEntityManager.class). Then NESTED throws unsupported. Whereas REQUIRES_NEW does a complete transaction, after which me is no longer managed.

    I have xa-datasources, and XA transactions don't mix with normal commit. After I switched to a normal datasource, em.createNativeQuery("COMMIT AND CHAIN").executeUpdate() works like a charm.

    Btw. I found in the C++ source code of Arjuna a method chainTransaction which doesn't seem to be exported to Java. But I found it just does another BEGIN which seems to imply a COMMIT, so that might be the portable way to achieve this.