Consider the following rough example, where Spring is managing transactions as per a standard Spring Boot setup.
The default transaction manager provided by spring-boot-starter-data-jpa is used.
In this transactional unit of work, both database and JMS operations occur.
#Autowired JmsMessageOperations jmsMessageOperations;
#Autowired EntityManager entityManager;
#Transactional
public void doWork() {
entityManager.persist(new Foo(1));
entityManager.flush();
jmsMessageOperations.convertAndSend(BAR_QUEUE, new Bar(1));
entityManager.persist(new Foo(2));
entityManager.flush();
BazResponse bazResponse =
jmsMessageOperations.convertSendAndReceive(
BAZ_QUEUE,
new Baz(1),
BazResponse.class);
// here
}
What happens to these JMS messages (the two outbound and the one inbound), if at // here a:
rollback exception is thrown,
something (or nothing) occurs, resulting in committing the transaction.
I'm particularly interested in how the convertSendAndReceive(...) is managed, because obviously a response cannot be received until the send is committed - is the send of Baz handled in a different transaction to the send of Bar?
While I would expect the messaging implementation to not make a huge difference, I'm mostly interested in ActiveMQ.
Related
Instead of sending single message in a transaction:
jmsTemplate.convertAndSend(message);
How can I send multiple jms messages in a single transaction?
Is there an example I can loot at?
Start the transaction before calling the template
#Transactional
public void doSends() {
template.convertAndSend(...)
...
template.convertAndSend(...)
}
The transaction commits when the method exits. See the Spring documentation about transactions.
Or, use one the of the template's execute() methods and do the sends in the callback.
I am using Apache Camel to send messages to my Java service. I have kept transacted=true on consumer route. I also need to send e-mail on successfully processing of JMS messages.
I am using below code to register synchronization and send e-mail only after transaction is committed.
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter(){
#Override
public void afterCommit(){
sendMail(mailBody);
}
});
Problem: incoming transaction from Camel is not synchronized and I am getting
java.lang.IllegalStateException: Transaction synchronization is not active
I tried calling transactionsynchronizationmanager.initsynchronization() - I am not getting any exception but afterCommit() method is never called.
transactionsynchronizationmanager.initsynchronization();
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter(){
#Override
public void afterCommit(){
sendMail(mailBody); //never called
}
});
Same code is working when request is received via spring mvc controller (through Spring Transaction).
You likely need to turn on transacted on the route to enable spring transaction. The option transacted=true on the JMS endpoint is NOT spring-transaction, but its only for JMS acknowledge mode to be set as transacted. They are not the same.
So in your Camel route, setup spring transaction as well, eg
from jms
transacted
See more details in the Camel docs: http://camel.apache.org/transactional-client.html or even better if you have a copy of the Camel in Action book (1st or 2nd ed) then it has a fully chapter devoted to transactions.
It´s more of a conceptual question: I currently have a working activemq queue which is consumed by a Java Spring application. Now I want the queue not to permanently delete the messages until the Java app tells it the message has been correctly saved in DB. After reading documentation I get I have to do it transactional and usa the commit() / rollback() methods. Correct me if I'm wrong here.
My problem comes with every example I find over the internet telling me to configure the app to work this or that way, but my nose tells me I should instead be setting up the queue itself to work the way I want. And I can't find the way to do it.
Otherwise, is the queue just working in different ways depending on how the consumer application is configured to work? What am I getting wrong?
Thanks in advance
The queue it self is not aware of any transactional system but you can pass the 1st parameter boolean to true to create a transactional session but i propose the INDIVIDUAL_ACKNOWLEDGE when creating a session because you can manage messages one by one. Can be set on spring jms DefaultMessageListenerContainer .
ActiveMQSession.INDIVIDUAL_ACKNOWLEDGE
And calling this method to ack a message, unless the method is not called the message is considered as dispatched but not ack.
ActiveMQTextMessage.acknowledge();
UPDATE:
ActiveMQSession.INDIVIDUAL_ACKNOWLEDGE can be used like this :
onMessage(ActiveMQTextMessage message)
try {
do some stuff in the database
jdbc.commit(); (unless auto-commit is enabled on the JDBC)
message.acknowledge();
}
catch (Exception e) {
}
There are 2 kinds of transaction support in ActiveMQ.
JMS transactions - the commit() / rollback() methods on a Session (which is like doing commit() / rollback() on a JDBC connection)
XA Transactions - where the XASession acts as an XAResource by communicating with the Message Broker, rather like a JDBC Connection takes place in an XA transaction by communicating with the database.
http://activemq.apache.org/how-do-transactions-work.html
Should I use XA transactions (two phase commit?)
A common use of JMS is to consume messages from a queue or topic, process them using a database or EJB, then acknowledge / commit the message.
If you are using more than one resource; e.g. reading a JMS message and writing to a database, you really should use XA - its purpose is to provide atomic transactions for multiple transactional resources. For example there is a small window from when you complete updating the database and your changes are committed up to the point at which you commit/acknowledge the message; if there is a network/hardware/process failure inside that window, the message will be redelivered and you may end up processing duplicates.
http://activemq.apache.org/should-i-use-xa.html
I've been implementing some business logic/validation with the Spring Data Rest and JPA repositories using repository validators as described in:
http://docs.spring.io/spring-data/rest/docs/current/reference/html/#events.
After digging deeper into the SDR code I noticed that the validators (or more generally, repository listeners) are not invoked within a transaction.
From the source code of org.springframework.data.rest.webmvc.RepositoryEntityController:
private ResponseEntity<ResourceSupport> createAndReturn(Object domainObject, RepositoryInvoker invoker,
PersistentEntityResourceAssembler assembler, boolean returnBody) {
// validation logic is implemented in the listener, no transaction yet
publisher.publishEvent(new BeforeCreateEvent(domainObject));
// invoker calls repository, which is wrapped in the transactional proxy,
// only then transaction begins
Object savedObject = invoker.invokeSave(domainObject);
publisher.publishEvent(new AfterCreateEvent(savedObject));
PersistentEntityResource resource = returnBody ? assembler.toFullResource(savedObject) : null;
HttpHeaders headers = prepareHeaders(resource);
addLocationHeader(headers, assembler, savedObject);
return ControllerUtils.toResponseEntity(HttpStatus.CREATED, headers, resource);
}
As seen in the code, the listeners are not called within a transaction, which could lead to eventual data consistency issues.
Am I missing something? Or the framework simply sets the transactional boundary incorrectly?
In spring data rest the repository method would run its own transaction. I also think that this is problematic in some cases. At least the event handler should be run in the same transaction as the repository method.
There was a similar question here:
Handle spring-data-rest application events within the transaction
Especially this answer provides a workaround that allows you to wrap the whole RepositoryEntityController method in a transaction - I think this is what you need most of the time:
https://stackoverflow.com/a/30713264/5371736
I am working on spring4 mvc to introduce in our new web application and currently we are using struts1.x and wanted to use new framework for support html5/ajax request as simple as possible and wanted to use the power of DI and spring webflow support.
Currently in our struts1.x application db transaction management is done at our custom GenericAction which is subclass of Action, In GenericAction we are getting the connection from data source and handover to the subclasses then any exception raised then caught and rollback, otherwise commit such that database atomicity (transaction management )is done at one place. All the module action classes should extends GenericAction such that database connection will be available and performs the module related stuff and after completing connection will be either rollback or commit in GenericAction as said above.
In spring, scope of the Transaction is started with #Transactional annotation and then ends with a method in Service Class since the service class marked as #Transactional. This is not feasible solution for me. I have read several documents related to the spring transaction before starting and below are my questions.
I am using HibernateTransactionManager as transaction manager
Transaction should start from interceptors in case web request or any class (in case of unit testing).
Transaction should ends with the after interceptor is executed in case of web request or any class in case of unit testing.
In case of any exception raised then our HandlerExceptionResolverImpl handler will execute then connection should be rollback.
Any workaround or best practices would be greatly appreciate.
Thanks
Dhorrairaajj
In my opinion, you can achieve this with minimal change on the current application by following what Spring does in its org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(Object, TransactionDefinition). In detail, I think something like following should work:
On the GenericAction, get connection from data source (as you've already done)
Bind the connection via its holder with the current data source:
TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());
Eventually, commit the transaction as org.springframework.jdbc.datasource.DataSourceTransactionManager.doCommit(DefaultTransactionStatus).
In any case, Spring's http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/transaction/support/TransactionTemplate.html can help to achieve any programmatic transactional scenario that you want. Just need to ensure that the right transactional resource is bound to the opening transaction.
Hope this helps.
Thanks for you reply. Your post/suggestion drives to do the following
I written an interceptor called TxtManager and below are methods to perform the transaction management.
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception
{
Session session = sessionFactory.openSession();
SessionHolder hold = new SessionHolder(
session);
TransactionSynchronizationManager.bindResource(sessionFactory, hold);
// passing null would accept the default transaction definition.
status = transactionManager.getTransaction(null);
return true;
}
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception
{
Exception hanlderEx = (Exception) request
.getAttribute(HandlerExceptionResolverImpl.EXCEPTION_KEY);
if (hanlderEx != null)
{
transactionManager.rollback(status);
}
else
{
transactionManager.commit(status);
}
}
In HandlerExceptionResolverImpl class which responsible for handling things upon exception, put an exception object in request and read the same exception to rollback or commit in afterCompletion method of the interceptor.
In between(preHandler and afterCompletion) we will going to use the standard practices to perform the module related stuff including any other interceptos. Unit testing may not be supported, i will check other alternative for unit testing.
Finally we were able to simulate the existing frame work with your suggestion Thanks
Any suggestion to improves this would be much appreciate !!..
Thanks
Dhorrairaajj