What precisely means setSessionTransacted in JMSTemplate? - spring

Please explain me if I understood correctly Spring documentation.
Spring docs states: https://docs.spring.io/spring/docs/current/spring-framework-reference/integration.html#jms-tx
(...)When you use the JmsTemplate in an unmanaged environment, you can specify these values (transaction and acknowledgment modes) through the use of the properties sessionTransacted and sessionAcknowledgeMode.
When you use a PlatformTransactionManager with JmsTemplate, the template is always given a transactional JMS Session.(..)
(BTW, that is true - session is transactional)
Javadoc states : https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jms/core/JmsTemplate.html
Default settings for JMS Sessions are "not transacted" and "auto-acknowledge". As defined by the Java EE specification, the transaction and acknowledgement parameters are ignored when a JMS Session is created inside an active transaction, no matter if a JTA transaction or a Spring-managed transaction.
I understood that if transaction is active, JMS Template session transaction settings are ignored - that is true - and the session should participate active transaction - that is not true.
I debug why it is not true and I found: https://github.com/spring-projects/spring-framework/blame/master/spring-jms/src/main/java/org/springframework/jms/connection/ConnectionFactoryUtils.java#L353
if (resourceHolderToUse != resourceHolder) {
TransactionSynchronizationManager.registerSynchronization(
new JmsResourceSynchronization(resourceHolderToUse, connectionFactory,
resourceFactory.isSynchedLocalTransactionAllowed()));
resourceHolderToUse.setSynchronizedWithTransaction(true);
TransactionSynchronizationManager.bindResource(connectionFactory, resourceHolderToUse);
}
The line resourceHolderToUse.setSynchronizedWithTransaction(true) is align the documentation.
The issue here: resourceFactory.isSynchedLocalTransactionAllowed()
Because resourceFactory is org.springframework.jms.core.JmsTemplate.JmsTemplateResourceFactory#isSynchedLocalTransactionAllowed which points to JmsTemplate#sessionTransacted.
Conclusion:
According to documentation, if transaction is active, JmsTemplate#sessionTransacted should be ignored. But it is not true - although session is transactional, cannot not participate in commit.
JmsTemplate#sessionTransacted is finally mapped to ConnectionFactoryUtils.JmsResourceSynchronization#transacted and default=false prevents commit being called at the end of transaction (JmsResourceSynchronization "thinks" that it does not participate transaction)
Do I understand documentation right and there is really bug here?

Guided by M. Deinum, I made more experiments and it seems I wrongly understood term Spring-managed transaction
I simply thought that Spring managed transaction is started by platformTransactionManager. But:
If platformTransactionManager is JtaTransactionManager and transaction is started, it IS Spring managed transaction; JMS template attribute sessionTransacted is ignored and JMS template is part of transaction
if platformTransactionManager is DataSourceTransactionManager or JpaTransactionManager then
if sessionTransacted is false, JMS template is not in transaction
if sessionTransacted is true, JMS template is synchronized with transaction: after callback/rollback on JDBC/JPA transaction correspondent commit/rollback is called on JMS transaction

Related

Spring Data JPA with Hikari: Why is hikari auto-commit property set to 'true'?

Spring Data JPA or Hibernate have by default auto-commit set to false. This sounds reasonable as these frameworks work with entities and an update to an entity could involve updates to multiple tables through multiple SQL queries. So by setting auto-commit to false and explicitly controlling a transaction these frameworks ensure that a change to entity is atomic and consistent.
But now Hikari is the default connection pool provider for spring data jpa, and on looking at application logs I see hikari sets auto-commit to true for the connection pool.
2021-10-24 11:30:07.815 DEBUG [restartedMain] com.zaxxer.hikari.HikariConfig||HikariConfig.logConfiguration:1135: autoCommit................................true
Any explanation of why this is set so and does this effect transactions(I don't think it effects transactions as each transaction might again set auto-commit to false and thus take over when to commit the transaction.)
Edit - following #ken-chan answer and discussion.
For projects using spring data jpa with #Transactional (with 100% Hiberante), changing hikaris connection pool settings to auto-commit=false should give performance benefit. Please see answer and subsequent discussion for more details.
I think Hikari just follows the default auto-commit value (i.e. true) defined by JDBC in order to align with its default behaviour .(See this)
And your guess is correct , the framework will take care to configure the necessary auto-commit value of the JDBC Connection such that it can do the transactional stuff over multiple JDBC Statement.
For example in Spring #Transactionl using JDBC , the following codes show that if the auto-commit is enabled , it will disable it before executing any transactional codes. It also will re-enable it after completing the transaction.
// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
// so we don't want to do it unnecessarily (for example if we've explicitly
// configured the connection pool to set it already).
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false);
}

TransactionManagementType.CONTAINER vs TransactionManagementType.BEAN

what is the difference between TransactionManagementType.CONTAINER and TransactionManagementType.BEAN
as Im using TransactionManagementType.CONTAINER in all of my EJBs and when ever the multiple instances of database is used, It throws an error which gets resolved if i change it to TransactionManagementType.BEAN
I want to know what are the advantages and disadvantages and how it gets effected if I change it to TransactionManagementType.BEAN
ERROR:
Error updating database. Cause: java.sql.SQLException: javax.resource.ResourceException:
IJ000457: Unchecked throwable in managedConnectionReconnected() cl=org.jboss.jca.core.
connectionmanager.listener.TxConnectionListener#680f2be0[state=NORMAL managed
connection=org.jboss.jca.adapters.jdbc.local.LocalManagedConnection#7ba33a94 connection
handles=0 lastReturned=1495691675021 lastValidated=1495690817487
lastCheckedOut=1495691675018 trackByTx=false pool=org.jboss.jca.core.connectionmanager.
pool.strategy.OnePool#efd42c4 mcp=SemaphoreConcurrentLinkedQueueManagedConnectionPool
#71656eec[pool=FAQuery] xaResource=LocalXAResourceImpl#4c786e85
[connectionListener=680f2be0 connectionManager=5c3b98bc warned=false
currentXid=null productName=Oracle productVersion=Oracle Database 12c
Enterprise Edition Release 12.1.0.2.0 - 64bit Production
With the Partitioning, OLAP, Advanced Analytics and Real Application Testing options
jndiName=java:/FAQuery] txSync=null]
TransactionManagementType.CONTAINER
You let the container itself manage transactions(container will commit and rollback). You can control behavior of your transactions (more precisely transaction propagation) by annotating methods with #TransactionManagementAttribute and specifying one of the attributes from TransactionAttribute Types.
TransactionManagementType.BEAN
You have to do transaction demarcation (start, commit, rollback) explicitly yourself, by obtaining UserTransaction interface.
#Resource
UserTransaction ut;
public void method(){
ut.begin();
... // your business logic here
ut.commit(); // or ut.rollback();
}
Note that you have to either commit and rollback before you exit the same method which stated the transaction for Stateless and Message Driven Beans, but it is not required for Stateful bean.
Regarding your question, advantage of BMT is that scope of the transaction can be less than scope of the method itself, i.e explicit control of the transaction.
You would most probably use CMT, BMT is required only in some narrow corner cases to support specific business logic.
Another advantage or use case of BMT is if you need to use Extended Persistence Context Type, which can be supported in BMT with Stateful Session Beans.
Regarding your specific problem, without seeing any bean code or error messages, this is probably what is happening: if you have more databases then each database and its corresponding driver must be able to join an existing transaction, otherwise you will get a specific detailed error.
If you use TransactionManagementType.BEAN the bean is required to start a brand new transaction. Which means you are not joining an existing transaction, and each database operations begin and commit independently from each others.
You can achieve the same effect by leaving TransactionManagementType.CONTAINER and annotating your method with REQUIRES_NEW, provide of course you are calling each EJB trough the corresponding proxy/interface.
So it is not correct to put it like BEAN vs CONTAINER, but rather you have to made some design choice and annotate your methods accordingly.
As requested for a method marked with one of the following:
REQUIRES_NEW existing transactions gets suspended, and the method runs under a brand new transaction
REQUIRED is the default behavior, if a transaction exists it is reused by the method, otherwise gets created and the method runs in it

Prevent use of CachingConnectionFactory with DefaultJmsListenerContainerFactory

I am working on a brand new project in which I need to have listeners that will consume messages from several queues (no need to have producer for now).
Starting from scratch, I am using the last Spring JMS version (4.1.2).
Here is an extract of my configuration file:
<bean id="cachedConnectionFactory"
class="org.springframework.jms.connection.CachingConnectionFactory"
p:targetConnectionFactory-ref="jmsConnectionFactory"
p:sessionCacheSize="3" />
<bean id="jmsListenerContainerFactory"
class="org.springframework.jms.config.DefaultJmsListenerContainerFactory"
p:connectionFactory-ref="cachedConnectionFactory"
p:destinationResolver-ref="jndiDestinationResolver"
p:concurrency="3-5"
p:receiveTimeout="5000" />
But I think I may be wrong since DefaultJmsListenerContainerFactory will build regular DefaultMessageListenerContainerS. And, as stated in the doc, CachingConnectionFactory should not be used with a message listener container...
Even if I am using the new Spring 4.1 DefaultJmsListenerContainerFactory class the answer from post is still valid (cacheConsumers = true can be an issue + don't need to cache sessions for listener containers because the sessions are long lived) , right?
Instead of using the CachingConnectionFactory, I should use the SingleConnectionFactory (and not directly the broker implementation one)?
If the SingleConnectionFactory class should indeed be used, is the "reconnectOnException" property should be set to true (as it is done in the CachingConnectionFactory) or does the new "setBackOff" method (from DefaultJmsListenerContainerFactory) deals with the same kind of issues?
Thanks for any tips
Correct.
There's not really much benefit in using a SingleConnectionFactory unless you want to share a single connection across multiple containers; the DMLC will use a single connection from the vendor factory by default for all consumer threads (cacheLevel >= CACHE_CONNECTION), unless a TransactionManager is configured.
The container(s) will handle reconnection - even before the 'new' backOff property - backOff just adds more sophistication to the reconnection algorithm - it used to just retry every n seconds (5 by default).
As stated in the answer you cited, it's ok to use a CCF as long as you disable consumer caching.
Correction: Yes, when using the SingleConnectionFactory, you do need to set reconnectOnException to true in order for the container to properly recover its connection. Otherwise, it simply hands out the stale connection.

How to handle transaction involving Spring message/JMS and Database

I have a method that get an invoice and it creates XML and send that XML to a JMS queue and then save the invoice to DB with updated status like 'invoiced'. Below is pseudo code that involves Spring and Hibernate. My question is: Is the failure in hibernate save rollsback Jms sending.or if JMS send failed, how can I roll back saving invoice status? is this comes under distributed transaction management. What are the transactional cases involved here. Thanks.
#Transactional(propagation=Propagation.Required)
void processInvoices(invoice ){
String xml = createXML(invoice);
messageService.sendInvoice(xml );
invoice.setStatus("invoiced");
save(invoice);
}
As per my knowledge and what I understand from your question you want to synchronize hibernate and JMS transaction, For doing this you will need to use JTA to manage transactions across the the both Hibernate and JMS
Read More # Spring synchronising Hibernate and JMS transactions

XA transactions and message bus

In our new project we would like to achieve transactions that involve jpa (mysql) and a message bus (rabbitmq)
We started building our infrastructure with spring data using mysql and rabbitmq (via spring amqp module). Since rabbitMq is not XA-transactional we configured the neo4j chainedTransactionManager as our main transactionManager. This manager takes as argument the jpa txManager and the rabbitTransactionManager.
Now, I do get the ability to annotate a service with #Transacitonal and use both the jpa and rabbit inside it. If I throw an exception within the service then none of the actions actually occur.
Here are my questions:
Is this configuration really gives me an atomic transaction?
I've heard that the chained tx manager is not using a 2 phase commit but a "best effort", is this best effort less reliable? if so how?
What the ChainedTransactionManager does is basically start and commit transactions in reverse order. So if you have a JpaTransactionManager and a RabbitTransactionManager and configured it like so.
#Bean
public PlatformTransactionManager transactionManager() {
return new ChainedTransactionManager(rabbitTransactionManager(), jpaTransactionManager());
}
Now if tha JPA commit succeeds but your commit to rabbitMQ fails your database changes will still be persisted as those are already committed.
To answer your first question it doesn't give you a real atomic transaction, everything that has been committed prior to the occurence of the Exception (on committing) will remain committed.
See http://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/transaction/ChainedTransactionManager.html

Resources