Transaction not rolling back - spring

I have mybatis 3.0.4 with mybatis-spring integration 1.0.1 deployed within Fuse (OSGi). I've created a basic database within SQLServer 2008. In Spring I've configured a TransactionAwareDataSourceProxy data source and a DataSourceTransactionManager transaction manager.
Now I've created my own bundle to be deployed within Fuse which inserts some rows into the database. I've told the bundle to use the configured data source and transaction manager. The method which carries out the logic looks like this:
#Transactional(propagation=Propagation.REQUIRED)
public void go(RecsCashContext context) throws ActionException {
When this method throws an exception I can follow Spring through seeing the expected behaviour triggered. This leads me to Springs JtaTransactionManager and doRollBack(..).
So everything looks promising, except that when I look at the database, sure enough it's in an unstable state as previous inserts have not been roll back.
I'm at a loss on this one and I'm struggling to find any information online. Any thoughts?

What kind of exception is being thrown? Unless you tell Spring to explicitly rollback when a particular exception is thrown, it will proceed. By default, Spring's transaction handling only rolls back when an unchecked exception (e.g. RuntimeException) is thrown. In your case, if you're expecting the rollback to occur when ActionException occurs, you're out of luck unless you make the following modification:
#Transactional(rollbackFor={ActionException.class})
public void go(RecsCashContext context) throws ActionException {
More details are in here, specifically in section 10.5.6.1, #Transactional settings

As it turns out Fuse (servicemix) already exposes a transaction manager via an OSGi service within bundle org.apache.aries.transaction.manager_0.2.0.incubating [49]. As a result when I was looking up the transaction manager service, the one exposed by bundle 49 got picked up first.
This was resolved by clearly specifying the transaction manager I was interested in. At the moment I am doing this using the bean-name propery:
<osgi:reference id="transactionManager" bean-name="transactionManager" interface="org.springframework.transaction.PlatformTransactionManager" />
Though this could also be done by using a filter, but preferably we'll just make use of the transaction manager service that's already being expose as opposed to providing our own.

Related

Spring Transaction - Do not rollback for error under specific method

I have a Spring Boot application in which a service is responsible to create a Business Entity. For simplicity, let's consider:
create(Object toCreate) {
validationService.validate(toCreate);
Object created = repository.save(toCreate);
notificationService.notify(created);
}
Business has changed and now I would like the creation to not fail if notification fails.
I therefore wrapped the notify() method in a try-catch block that only logs the error (which is a RuntimeException).
However when tested, a transaction rollback error was thrown, saying the connection was closed. I do not want any rollback, especially since the NotificationService does not modify the database.
How can I tell Spring that any exception happening in the NotificationService is fine and does not require a rollback? I tried annotating the class/method with #Transactional(propagation=NEVER) but got existing transaction found for transaction marked with propagation 'never'
Perhaps refactoring your code would help better than the introduction of more complex transaction handling.
Assuming your #Transactional is on the create() method, we can see that you have:
Pre-persistence business logic
Persistence logic
Post-persistence business logic
It depends on your use-case, but I would not expect pts. 1 & 3 to contain persistence logic. If that is the case, you could extract your persistence logic in its own service that would itself be a transaction, not the parent.
If you have the need for a transaction in those steps, you could also extract them in their own transaction.
Other leads might be:
The default rollback behavior is for runtime unchecked exceptions. You could use checked exceptions to avoid the rollback.
If your notification is separate from persistence and you do not care about the transaction, you could make it an asynchronous call (e.g. with #Async). It would be scheduled outside of the transaction in its own context.
You can use the option norollbackfor of #Transactional so you have to specify an exception class not the service and when an error occurs in notifications try to throw a specifc error which would not cause a rollback.

TransactionalEventListener won't kick in

I have a problem with #TransactionalEventListener.
Given:
Kotlin 1.3.50
Spring Webflux
Spring Data
Hibernate 5
Postgres JDBC (not RDBC!)
What happens:
Request to controller was made.
Service did some job within method marked as #Transactional an event and published event.
Response returned to client
Event listener marked as #Async, #TransactionalEventListener and #Transactional doesn't kick in.
What I did:
I have debugged into service method which publishes an event to find out whether an actual transaction is in progress with the help of some static utility method I don't remember, which told me what I suspected - it is in progress.
Then I enabled debug output to see log saying: No transaction is in progress. Discarding event.
I mean, both publishing and consuming sides do have annotation #Transactional on them. No alterations to default params of annotations were made.
I have found similar situation in this question question but no one answered that.
Also, I have read an article at dzone but there seems to be no mentioning of my case.
Workaround:
I can avoid this by either replacing #TransactionalEventListener to #EventListener (alas, remove transaction bound processing) or by changing param fallbackExecute to true which effectively is the same as first solution.
Suspected parties:
Possibly absence of reactive transaction manager in an webflux environment could be the case? And some weird bug not accounting JpaTransactionManager?

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

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

Pre-bound JDBC Connection found

We have an app that is using hibernate, spring, and DB2 in websphere 7. We have audit triggers and we need to set so the triggers can know the logged in user (we use generic logon to the database). We came up with a new scheme for setting this in a new app so that it can automatically join in new transactions. We overrode the transaction manager and did the work in the doBegin.
These scheme worked great in one app, and seemed to work great in a second app, but now, weeks later, and not consistently (behavior is intermittent and does not happen in local development), we are getting this Pre-bound JDBC Connection found error. Looking online most posts say this is when you use two transaction managers against one data source. That is now what we are doing.
I also read one post wondering if it was because he mixed annotation and AOP based transactions. This app does some of that. I don't really buy that theory, but thought I'd mention it.
Exception:
Caused by:
org.springframework.transaction.IllegalTransactionStateException: Pre-bound JDBC Connection found! HibernateTransactionManager does not support running within DataSourceTransactionManager if told to manage the DataSource itself. It is recommended to use a single HibernateTransactionManager for all transactions on a single DataSource, no matter whether Hibernate or JDBC access.
at java.lang.Throwable.<init>(Throwable.java:67)
at org.springframework.core.NestedRuntimeException.<init>(NestedRuntimeException.java:54)
at org.springframework.transaction.TransactionException.<init>(TransactionException.java:34)
at org.springframework.orm.hibernate3.HibernateTransactionManager.doBegin(HibernateTransactionManager.java:475)
at gov.usdoj.afms.umc.utils.hibernate.AfmsHibernateTransactionManager.doBegin(AfmsHibernateTransactionManager.java:28)
Code (note that the exception comes from the super.doBegin()):
protected void doBegin(Object arg0, TransactionDefinition arg1) {
super.doBegin(arg0, arg1);
if (!Db2ClientInfo.exists()) {
clearDBProperty();
} else {
setDBProperty(Db2ClientInfo.getClientUserId(), Db2ClientInfo.getClientApplicationId());
}
}
private void setDBProperty(String uId, String appName) {
Session session = getSessionFactory().getCurrentSession();
Properties props = new Properties();
props.setProperty(WSConnection.CLIENT_ID, uId);
props.setProperty(WSConnection.CLIENT_APPLICATION_NAME, appName);
try {
Connection nativeConn = new SimpleNativeJdbcExtractor().getNativeConnection(session.connection());
if (nativeConn instanceof WSConnection) {
WSConnection wconn = (WSConnection) nativeConn;
wconn.setClientInformation(props);
} else {
logger.error("Connection was NOT an instance of WSConnection so client ID and app could not be set");
}
} catch (Exception e) {
throw new RuntimeException("Cannot set DB parameters!", e);
}
}
I just realized I never answered this. It turns out that the exception had nothing whatever to do with our Tx manager. It was the fact that this particular EAR has two apps in it, each pointing to the same data source. Evidently this confuses hibernate. We've plans to separate the apps some day, but creating an identical (except in name) data source and pointing the apps at them separately fixes the issue for now.
Instead of modifying the transaction manager it might be easier (better?) to create a wrapper around your datasource (extending DelegatingDataSource from spring) and override the 2 getConnection methods. For the cleanup you could wrap the connection in a proxy and intercept the close method.
That should be a safer (and easier I guess) way then trying to fiddle with the transaction manager and it works for every technology (JDBC, Hibernate, JPA etc.) as long as you use the wrapped datasource. (The registration could be done with a BeanPostProcessor which detects DataSource instances and simply wraps them in the delegate).
If that is to radical (as it means changing your current applications instead of updating a library). It could be a configuration problem, make sure that you are only loading your configuration (and thus DataSource and TransactionManager) only once, duplicating bean instances might lead to a similair behavior.
For posterity, I just got this problem and the answers here weren't very helpful. We resolved the problem by removing a double import of a core XML file which had the AOP transaction manager definition in it:
<tx:annotation-driven transaction-manager="..."
proxy-target-class="true" />
I'm thinking that it causes there to be 2 transaction managers overlapping the same namespace. To fix it, we moved the imports around so they were being done once.
Hope this helps someone else.

Resources