Search code examples
javaspringerror-handlingtransactional

Spring Catch JpaSystemException in @Transactional Method and Roll Back Transaction


I have a method that is annotated with

@Transactional(isolation = Isolation.SERIALIZABLE, propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)

and calls several repository methods. Now when one repository tries to alter a DB-row that is locked and not rolled back by another instance of the method, spring correctly throws

 org.springframework.orm.jpa.JpaSystemException: could not execute statement [...] Caused by: java.sql.SQLException: transaction could not be serialized and rolls back the failed transaction.

Now I want to keep all this behaviour but additionally handle the exception and start a retry. Here's a code snippet:

@Transactional(isolation = Isolation.SERIALIZABLE, propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    @Lock(LockModeType.PESSIMISTIC_WRITE)
    public void messageReceived(Card card) {
        this.request = request;
        this.numberOfAttempts = reset ? 0 : this.numberOfAttempts++;
        this.reset = true;
        LOGGER.info("Message received from Queue: " + card);
        TransactionDebugUtils.transactionRequired("MessageReceivedController.messageReceived");

        try {
            [...]
            repository1.createKonto(card);
            repository2.doStuff(card);

        } catch (JpaSystemException e) {
            //This is obviously never invoked
            LOGGER.error("TRANSACTION FAILED!!!");
        } catch (Exception e) {
            LOGGER.error("Error mapping json request to data model", message, e);
        }
    }

    @ExceptionHandler(JpaSystemException.class)
    //This is also never invoked
    public void handleJpaSystemException(JpaSystemException ex) {
        this.messageReceived(this.request);
        this.reset = false;
    }

Solution

  • I had this issue recently. As it is a method level @Transactional annotation, Transaction commit occurs after finishing method execution. When you are using this annotation 2 concepts should be considered

    1. persistence context
    2. database transaction

    After messageReceived() method is executed, those 2 things will happen and JPA exception is thrown at @Transactional level which means you need to handle this exception from where you are calling this method(controller; if you are calling from a controller). More regarding @Transactional can be found in this link.