0

I get the exception in the logs:

javax.persistence.TransactionRequiredException: Executing an update/delete query

This is in the isNew = false branch in updateSomething()-method. I didn't spot exceptions from other branch.

I found something on stackoverflow about using entityManager.joinTransaction();, but when I do this, I get another exception:

java.lang.IllegalStateException: Not allowed to join transaction on shared EntityManager

I would like to handle this correctly so that the updateSomething method works. I'd also would like to know whether having nested @Transactional annotation is a good idea and what the effect of calling a transactional method from a transactional method is - I believe the inner transactional runs in its own transaction and commits to database.

Note: The HACK doesn't work I left it in as an example of what I've tried.

Idea: Would it help to do a entityManager.flush() after every update (an update using native SQL)?

@Transactional
public boolean generateLogsComposite() {
    Connection conn = getConnection();
    writeSomeData(conn);//write some data using a separate connection
    boolean isNew = checkSomeData();//The check works with the data from writeSomeData although not using he connection conn
    if(isNew) {
        generateEmail(conn);//Calls a stored procedure on connection conn.
        updateRunEnvOnEmail(emailId, runEnv);
        //Now the data from generateEmail and updateRunEnvOnEmail is present
    } else {
        updateSomething();//has a javax.persistence.TransactionRequiredException: Executing an update/delete query
    }
}

private void joinTransaction() {
    try {
        //HACK vs javax.persistence.TransactionRequiredException: Executing an update/delete query
        entityManager.joinTransaction();
        logger.debug("Joined transaction successfully");
    } catch(Throwable t) {
        //java.lang.IllegalStateException: Not allowed to join transaction on shared EntityManager
    }
}

@Transactional
public void updateRunEnvOnEmail(Long emailId, String runEnv)
{
    try
    {
        //HACK vs javax.persistence.TransactionRequiredException: Executing an update/delete query
        joinTransaction();

        Query query = entityManager
                .createNativeQuery("update XXX.EMAIL e set e.RUN_ENVIRONMENT = :runEnv where e.EMAIL_ID = :emailId");
        query.setParameter("runEnv", runEnv);
        query.setParameter("emailId", emailId);
        query.executeUpdate();
    } catch (Exception e)
    {
        logger.debug("Error while updating RUN_ENVIRONMENT on Email : "
                + emailId + " ::: " + e);
    }

}

@Transactional
public void updateSomething()
{
    try
    {
        //HACK vs javax.persistence.TransactionRequiredException: Executing an update/delete query
        joinTransaction();

        Query query = entityManager
                .createNativeQuery("update something");
        query.executeUpdate();
    } catch (Exception e)
    {
        logger.debug("Error while updating something",e);
    }

}
Adder
  • 5,708
  • 1
  • 28
  • 56
  • Why `@Transactional` if you manage transactions yourself? Either define beans for `TransactionManager`s, use `@Transactional(transactionManager="TMBean")` and separate service calls for different connections, or get raw connections, start and commit the transactions manually. Otherwise you'll never get the code straight. – Dariusz Jun 18 '19 at 11:51
  • Your "nested" `@Transactional` are useless, they do nothing. The method is called from inside the proxy for the main `@Transactional` method. There is just one large transaction. You aren't allowed to use `joinTransaction` so remove that code. – M. Deinum Jun 18 '19 at 12:38

1 Answers1

1

Generally, JPA doesn't like updates to be called outside of transactions unless explicitly configured to do so. The proper way to do this would be the following:

entityManager.getTransaction().begin();
// do your updates
entityManager.getTransaction().commit();

However, if you are want the transactions managed by Spring (your IllegalStateException suggests so), then you might want to look into @Transactional (but make sure you provide the proper transaction manager, you might want to look into the answers on TransactionRequiredException Executing an update/delete query for insights).

Piotr Wilkin
  • 3,446
  • 10
  • 18