How application can be notified when Spring Transaction Manager rollbacks a SQL transaction? - spring

In Spring based application, Transaction Manager is responsible for committing or rolling back SQL transactions.
My application uses a custom cache for some part of persistent data.
This cache is not managed by Spring nor Hibernate.
If a SQL transaction encounters errors and must be rolled back, then cache modifications should be rolled back as well.
My question is, how to register an event listener or callback which will call my cache.evict() method when Spring Transaction Manager rolls back a transaction?

You can use TransactionSynchronizationAdapter for this. For reference, you can look at this: Spring - commit JMS Transaction after JPA transaction
In the answer given in the link, the synchronization is registered for afterCommit. In your case, it would be afterCompletion. In that implementation, you will need to check if the transaction is in a rolled back state and do the cache eviction.

There are lots of ways of doing this...
You can use application events if you want... Application Events in Spring Framework 4.2
You can throw a custom runtime exception and you can handle it in your exception handler (if you're using spring MVC). but here I don't recommend to do any important operations like clearing of cache here... Exception handling in Spring MVC
You can use a combination of #1 and #2. You can send an event that will eventually throw a runtime exception that you handle it in UI (spring mvc or whatever you use). This is how I would do it
You can throw an exception and anyone calling your bean will get the custom transaction exception you want and will have to deal with notification... I don't recommend this

Related

Spring Boot DB connection release mode

Is there a way to control database connection release strategy in Spring Boot? Something like Hibernate's ConnectionReleaseMode?
My test code would be rougly following:
INSERT query
HTTP call
UPDATE query
INSERT and UPDATE queries are their own methods in a repository bean (either extending CrudRepository, or as a Mybatis #Mapper). HTTP call is in it's own bean.
Now my service bean is where I experiment with different #Transactional settings as my ultimate goal is to have these three steps executed within a single transaction.
The problem is, the HTTP call can take hundreds of millis and Spring is holding the database connection during that time. This quickly leads to an empty connection pool, while the connections themselves are idle.
I have done the same experiments with default configuration using spring-boot-starter-data-jpa and also using mybatis-spring-boot-starter.
The only thing which got me closer to my goal was setting spring.jpa.open-in-view=false for data-jpa, which would release db connections in case of not using #Transactional at all or with propagation set to NEVER. It doesn't work if it's all wrapped in a transactin though.
I'm feeling like I'm missing some vital part of the transaction concept in Spring. Though the Spring reference docs mentions release mode only in relation to JTA transaction manager + JEE container + Hibernate.
Transactions are bound to a connection/session so you can't release the connection in the middle of a transaction. You have to finish the transaction to close/release the connection. Keep in mind that a transaction could abort at any time, and since the HTTP call is not "part of" the transaction, but just runs while the transaction is in progress, it won't help you to keep the transaction open while the HTTP call is running. I'd suggest you go with one of the following solutions:
Use a circuit breaker to cancel the HTTP call if it hits a timeout to have an upper bound for how long a connection/transaction can be open/held
Move the HTTP call out of the transaction, either before or after the transaction
Use two database transactions
Note that you could use a transactional Job Scheduler to schedule a job in the first TX. The job could then, with at least once semantics, try to invoke the HTTP call and then continue with the second transaction or do some compensation if something fails.

Spring Integration JMS - Spring code does commit on RuntimeException

I would like to ask experienced Spring Integration developers or even code authors regarding weird behavior Im experiencing with my code. The issue is that Spring Code executes COMMIT on my RuntimeException. So here is my setup:
1.
I have standard JMS inbound channel adapter configured using
<jms:message-driven-channel-adapter />
I have transactionManager which implements the Best effort 1PC pattern - that one available in Spring, ChainedTransactionManager.
Im consuming messages in simple testing flow where the JMS inbound adapter only publishes to direct channel and via one transformer endpoint the converted message is published to another direct endpoint where the handler which is ServiceActivator intentionally throws my application exception which extends RuntimeException.
Now when I debug the whole flow and when Im stepping through the Spring internal code I always end in place where the configured transaction manager calls COMMIT and thus my ChainedTransactionManager executes COMMIT as well despite Im simulating error and thus clear rollback scenario. The breakpoint in the rollback method is not hit at all.
What can I have wrong?
Now I have gone through the code again and even the Spring code sets the status to RollbackOnly and executes some exception handlers but at the end it still goes on successfully and ultimately it calls the commit line as shown on the screenshot and chainedTransactionManager instructs its local TMs to commit as well.
Im hoping there is some detail/mistake Im not able to spot :) Thanks!
Screenshot from class .m2\repository\org\springframework\spring-jms\5.2.3.RELEASE\spring-jms-5.2.3.RELEASE-sources.jar!\org\springframework\jms\listener\AbstractPollingMessageListenerContainer.java

Does Spring really gives new transaction for each thread?

I am working on a project which makes extensive use of spring transactions. I t so happened that i was throwing an exception AND NOT HANDLING IT PROPERLY which does not commit or rollback the transact Ion. So the connection remains active even when the thread is stopped. When new request to the web server ( Apache tomcat 7.0) comes the spring provides the earlier connection to the new thread. Since the thread didn't started the connection so the thread couldn't close it either ie .commit doesn't work. because of which the objects are not getting persisted in database in the consequent transactions even when there is no exception. How can i work around the problem so that i can detect where is the actual problem happening or can a design an exit point where i can explicitly close the transaction before response is sent to browser.
A runtime unchecked exception or error that is thrown from a #Transactional method will rollback that transaction by default; a return or a checked exception will commit the transaction by default. You can tell Spring to override that behaviour using the annotation type elements of the #Transactional annotation.
The Spring Framework reference manual has an entire chapter devoted to transaction management. You need to read and understand that as your starting point.

JMS/MDB rollback

We have a application, which reads of JMS and does some database transactions, all as part of one XA transaction..
If we find the message has some problems, we want to rollback the database,
but we don’t want the rollback off JMS to have us read the message again(we don't want the MDB to trigger the entire process one more if rollback was previously initiated).
To be sure I understand you correctly: you have a transactional message-driven bean that performs some actions on database. You still want the MDB to be a part of XA transaction, but you don't want the database actions to influence the state of the transaction in which the MDB takes part in.
That being said, there are two solutions:
If the MDB runs within a container-managed transaction, wrap all the database actions into an EJB with #REQUIRES_NEW annotation — unless they're already all run in such transactional context.
If your MDB is bean-managed, the message delivery is NOT a part of transaction, so it will not be redelivered, no matter what — section 5.4.12 of EJB Core Contracts and Requirements:
When a message-driven bean using bean-managed transaction demarcation uses the javax.transaction.
UserTransaction interface to demarcate transactions, the message receipt that causes
the bean to be invoked is not part of the transaction.

JBoss doesn't sends a JmsTemplate (Spring) message

Well, actually JBoss does send the message, but only when the current transaction started by the EJB is finished.
We have this problem with JBoss 4.0.3 and Spring's JmsTemplate. An EJB sends a message to a queue with a temporary queue as the reply_to field. Then, inside the same transaction, we listen for the response given by the first MDB. The problem is that the JmsTemplate's method "send" isn't executed after the transaction have finished. So, by the time the message is sent to the queue, and processed by the MDB, the listener of the temporary queue is gone.
This is called "Synchronous Reception"
Two things change this behavior but does raise some concerns:
Change the EJB's transaction type to BMT. (Concern: BMT sucks)
Create a thread that all it does is to call the JmsTemplate.send() method.
As a side note, this is an EJB that is working correctly on a weblogic environment, and the message does get sent when it should, in the middle of the transaction not when it's over.
Thanks for any help.
JBoss's behaviour is correct. JMS is a transactional API, and sends should only be executed when the tx commits.
It may be possible to convince JmsTemplate not use the current transactional context, although it makes a point of trying to hide the unpleasantness of the JMS API from you.
You could wrap the JMS template in code, either a Stateless session bean or a service method using Spring's transaction management, that uses a transaction propagation of REQUIRES_NEW. That way the sending of the message is in its own transaction that will commit the sending of the message outside the scope of the wrapping transaction.
I'm not sure why this would work on Weblogic though. My guess would be that on Weblogic it's not recognizing the queue as an XA Queue.

Resources