Reputation: 131
I have a problem with hibernate and jpa.
I have a method that is called regularly to retrieve data from a table in database and to send it to another server.
Here is the code of the method :
protected List<MessagingMessage> getNewMessages() throws NoMessageAvailableException {
return messagingPublisher.getNewMessages(application);
}
Here is the code from the publisher :
@Override
@Transactional(noRollbackFor = {ObjectOptimisticLockingFailureException.class})
public List<MessagingMessage> getNewMessages(String application) throws NoMessageAvailableException {
List<GpsDataToSend> allGpsDatas = gpsDataToSendService.findByapplicationIgnoreCase(application);
List<MessagingMessage> allMessages = new ArrayList<>();
for (GpsDataToSend gpsDataToSend : allGpsDatas) {
MessagingMessage message = convertToMessagingMessage(gpsDataToSend);
// Ajout dans les messages à envoyer au TMS
allMessages.add(message);
try {
// Suppression des données de la base
gpsDataToSendService.deleteGpsDataToSend(gpsDataToSend.getId());
} catch (ObjectOptimisticLockingFailureException oolfa) {
// Si l'exception est levée, c'est que la donnée a été supprimée, donc déjà été envoyée au TMS
allMessages.remove(message);
Long idGpsDataToSend = ((GpsDataToSend) message.getObject()).getId();
log.info("The GPS message was already sent because the data doesn't exists anymore in database (id : {})", idGpsDataToSend);
}
}
return allMessages;
}
The publisher retrieves the data, convert it in a message, and the data is deleted from the database, to be sure that it is only send once.
The problem is that I have 2 servers in loadbalancing, executing the same code, so potentially the 2 servers are retrieving the same data at the same time. Sometimes, on the delete, I had an ObjectOptimisticLockingFailureException that occured, telling me that there was no data to delete. I just handled this Exception to make sure it didn't mess with the rest of the execution.
But now, when an ObjectOptimisticLockingFailureException occurs, at the next execution of :
protected List<MessagingMessage> getNewMessages() throws NoMessageAvailableException {
return messagingPublisher.getNewMessages(application);
}
I get this exception :
2018-04-24 15:58:09 ERROR [pool-3-thread-1] c.g.m.m.w.WepSphereMqQueuePusher [WepSphereMqQueuePusher.java:76] Error getting new message to send org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:526) ~[spring-orm-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:761) ~[spring-tx-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:730) ~[spring-tx-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:518) ~[spring-tx-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:292) ~[spring-tx-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) ~[spring-tx-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at com.sun.proxy.$Proxy75.getNewMessages(Unknown Source) ~[na:na]
at com.geodisbm.mobility.messaging.websphere.WepSphereMqQueuePusher.getNewMessages(WepSphereMqQueuePusher.java:101) [messaging-1.0-SNAPSHOT.jar:na]
at com.geodisbm.mobility.messaging.websphere.WepSphereMqQueuePusher.sendAllMessages(WepSphereMqQueuePusher.java:71) [messaging-1.0-SNAPSHOT.jar:na]
at com.geodisbm.mobility.messaging.websphere.WepSphereMqQueuePusher.access$000(WepSphereMqQueuePusher.java:16) [messaging-1.0-SNAPSHOT.jar:na]
at com.geodisbm.mobility.messaging.websphere.WepSphereMqQueuePusher$1.run(WepSphereMqQueuePusher.java:63) [messaging-1.0-SNAPSHOT.jar:na]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [na:1.8.0_45]
at java.util.concurrent.FutureTask.runAndReset$$$capture(FutureTask.java:308) [na:1.8.0_45]
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java) [na:1.8.0_45]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180) [na:1.8.0_45]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294) [na:1.8.0_45]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_45]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_45]
at java.lang.Thread.run(Thread.java:745) [na:1.8.0_45]
Caused by: javax.persistence.RollbackException: Transaction marked as rollbackOnly
at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:58) ~[hibernate-entitymanager-5.1.12.Final.jar:5.1.12.Final]
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:517) ~[spring-orm-4.3.14.RELEASE.jar:4.3.14.RELEASE]
... 20 common frames omitted
It appears that if the ObjectOptimisticLockingFailureException occurs, the transaction is marked as rollbackonly. So I tried to say the transaction to not rollback for this exception (via the annotation), but it doesn't work.
Do you have any idea why it's not working ?
Thanks.
Upvotes: 2
Views: 2756
Reputation: 15861
Most probably this problem happens because getNewMessages
invokes some method that is transactional and transaction manager's property globalRollbackOnParticipationFailure
is set to true
(which is the default).
Here's the relevant piece of javadoc:
Set whether to globally mark an existing transaction as rollback-only
after a participating transaction failed.
<p>Default is "true": If a participating transaction (e.g. with
PROPAGATION_REQUIRES or PROPAGATION_SUPPORTS encountering an existing
transaction) fails, the transaction will be globally marked as rollback-only.
The only possible outcome of such a transaction is a rollback: The
transaction originator <i>cannot</i> make the transaction commit anymore.
<p>Switch this to "false" to let the transaction originator make the rollback
decision. If a participating transaction fails with an exception, the caller
can still decide to continue with a different path within the transaction.
However, note that this will only work as long as all participating resources
are capable of continuing towards a transaction commit even after a data access
failure: This is generally not the case for a Hibernate Session, for example;
neither is it for a sequence of JDBC insert/update/delete operations.
You have set noRollbackFor
on the outer transaction but
getNewMessages
never throws ObjectOptimisticLockingFailureException
deleteGpsDataToSend
is annotated with @Transactional
) throws that exception.Upvotes: 3