5
votes

I have a Spring application that uses Hibernate and PostgreSQL. It also uses Spring AMQP (RabbitMQ).

I am using the Hibernate Transaction manager configured as follows:

<bean id="transactionManager"
    class="org.springframework.orm.hibernate3.HibernateTransactionManager"
    p:sessionFactory-ref="sessionFactory" p:dataSource-ref="dataSource" />

I'm using the SimpleMessageListenerContainer for asynchronous message reception configured as:

@Resource(name="transactionManager")
private PlatformTransactionManager txManager;

@Autowired
private MyListener messageListener;

@Bean
public SimpleMessageListenerContainer mySMLC()
{
    final SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
    container.setConnectionFactory(rabbitConnectionFactory);
    container.setQueueNames("myQueue");

    final MessageListenerAdapter adapter = new MessageListenerAdapter(messageListener);
    adapter.setMessageConverter(converter);
    container.setMessageListener(adapter);
    container.setChannelTransacted(true);
    container.setTransactionManager(txManager);
    return container;
}

So basically I have specified that the reception of messages needs to be transactional. The message listener calls a service that can have methods annotated with @Transactional and possibly do CRUD operations on the DB.

My question is, is there a problem using the HibernateTransactionManager to manage the transaction at the SimpleMessageListenerContainer level? Will there be any issues using a DB transaction manager to wrap the receiving of messages from RabbitMQ?

I am not expecting XA here. I just want to ensure that if any operations on the DB by the service fails, the message does not get acked to the RabbitMQ broker.

1

1 Answers

1
votes

According to the Spring sources, main purpose of MessageListenerContainer's transactionManager property is to start transaction on message received before listener call, and to commit or rollback transaction after listener returns or throws an exception. So there is no need to make listener methods @Transactional as the transaction will be already started before listener method called.

In case of error in the listener, exception will be thrown, DB transaction will rollback, and no ack sent to the message broker (jms transaction rollback). But without XA there can be duplicate messages. For example after DB transaction successfully commits, connection to message broker reset, and ack could not be sent to the broker. After reconnect broker could deliver duplicate message. If you admit this, there is no need to deal with XA.