Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring synchronising Hibernate and JMS transactions

I am working on a stand-alone application that uses both JMS and Hibernate.

The documentation suggests JTA has to be used if I want to have transactions across both resources.

However, right now with a @Transaction annotated DAO method (and HibernateTransactionManager), this already seems to work. When I call send() on the JmsTemplate, the message is not immediately sent, but rather the JMS session is committed with the Hibernate session as the method returns.

I didn't know how this is possible without the JtaTransactionManager, so I checked the source code. It turns out both the wrapper for Hibernate and JmsTemplate registers the sessions with TransactionSynchronizationManager and the JMS session will be committed when the Hibernate session commits.

What's the different between this and a JTA transaction. Can I use this to replace the latter??

like image 757
billc.cn Avatar asked May 29 '12 13:05

billc.cn


2 Answers

In short no, you can't get support for 2-phase commit without a JTATransactionManager and XA aware datasources.

What you are witnessing is a co-ordination of two Local Transactions supporting 1-phase commit only. Roughly performing this sequence of events...

  1. Start JMS Transaction
  2. Read JMS message
  3. Start JDBC Transaction
  4. Write to database
  5. Commit JDBC Transaction
  6. Commit/Acknowledge JMS

The JMS transaction will be started first wrapping the nested JDBC transaction, so that the JMS queue will rollback if the Hibernate/JDBC commit fails. Your JMS Listener Container should be setup not to acknowledge="auto" and instead wait for the Hibernate transaction to complete before sending the acknowledgement.

If you only have these two resources then the issue you will have to consider is when Hibernate succeeds in persiting then you get an Exception before you can acknowledge the JMS server. Not a big issue as the JMS message is not lost and you will read it again.

However

  1. You must write your MessageListener to handle duplicate messages from the server

  2. You must also handle a message that cannot be processed due to bad data and ending up in an infinite loop of trying to comsume it. In this case the server may be configured to move the message to a "dead message queue", or you deal with this yourself in the MessageListener

Other options and further reading

If your JMS server does not support XA (global) transactions this is pretty much your only solution.

If JMS server does support XA transactions but JDBC doesn't then you can use a JTATransactionManager and use the LastResourceCommitOptimisation. There are open source JTATransactionManagers you can use like JOTM

This JavaWorld article goes into more detail on your problem space.

like image 163
Brad Avatar answered Oct 02 '22 17:10

Brad


Although this has been answered in detail by Brad, i would like to address a very specific part of your query:-

I didn't know how this is possible without the JtaTransactionManager

From the spring documentation:- When a JTA environment is detected, Spring’s JtaTransactionManager will be used to manage transactions

https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-jta.html

like image 40
Varun Avatar answered Oct 02 '22 18:10

Varun