Java – Jpa transaction javax.persistence.RollbackException: Transaction marked as rollbackOnly

javajpaspring

I've got an application that's doing a bunch of writes to various database tables via jpa. One of those writes can cause an optimistic lock exception. If one is thrown, it's not a big deal and I want the rest of the transaction to commit.

I took a look at the no-rollback functionality on the spring transaction via:

<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
    <constructor-arg ref="transactionManager"/>
    <constructor-arg ref="ignoreOptimisticLockingExceptionRule"/>
</bean>
<bean id="ignoreOptimisticLockingExceptionRule" class="org.springframework.transaction.interceptor.RuleBasedTransactionAttribute">
    <property name="rollbackRules">
        <list>
            <bean class="org.springframework.transaction.interceptor.NoRollbackRuleAttribute">
                <constructor-arg value="javax.persistence.OptimisticLockException"/>
            </bean>
        </list>
    </property>
</bean>
<bean class="org.springframework.orm.jpa.JpaTransactionManager"
    id="transactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

My app catches the OLException around the merge method of the entity that will throw this exception, but the transaction still gets rolled back. I did some digging around to see what was up, and in the doCommit method of JpaTransactionManager is where javax.persistence.RollbackException: Transaction marked as rollbackOnly gets thrown. It gets thrown because the rollbackOnly flag (in TransactionImpl) was marked as true.

Digging deeper into that, i see that merge method in AbstractEntityMangerImpl ultimately marks the transaction as rollbackonly which then triggers the exception further up. I don't see where the RuleBasedTransactionAttributes get applied though. I don't know if i have that setup properly.

Thanks!

Best Answer

The JPA specification mandates that the transaction is marked for rollback when an OptimisticLockException occurs. And I don't know which JPA engine you use, but at least for Hibernate (and I would expect the same thing for other engines), the documentation says:

If the Session throws an exception, including any SQLException, immediately rollback the database transaction, call Session.close() and discard the Session instance. Certain methods of Session will not leave the session in a consistent state. No exception thrown by Hibernate can be treated as recoverable. Ensure that the Session will be closed by calling close() in a finally block.

So if you get such an exception, your best bet is to let the transaction rollback, and retry.