Recently refactoring some code, came across transaction rollback scenario where one EJB bean calls another. Now in exception block each bean has its context which is marked for rollback.
Is this a good practice or they should just re-throw the exception & finally the initiator bean only does the rollback.
Also, if there is single transaction spanned across EJB's, then rollback should happen at the originator bean or where it encountered exception.
Transaction type JTA for persistence with XA data source.
With this
is marked for rollback
you mean that the EJB catch the exception and use setRollbackOnly ?
If that's the case, then it depends on your design to decide which approach is preferred.
Normally a transaction is rolled back if a system exception is detected by the container. Application exception on the opposite does not have this effect.
But, if your business logic require that even a business exception has an important impact such that it must have the effect of rolling back the whole transaction, then you have the choice of setRollBackOnly, or launch an application exception with rollback=true.
This second approach has also the effect of not destroying the bean.
Regarding your second question:
Also, if there is single transaction spanned across EJB's, then
rollback should happen at the originator bean or where it encountered
exception.
the rollback is managed by the container, and again it depends on your design. Keep in mind that the error may pass trough the hole of your unique bean in charge of rolling back the transaction, and not being catched at all. So you will end up with an unwanted scenario with a transaction not being rolled back at all.
Related
I am getting the below exception when doing a batch processing
encountered an error.org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:252)
at java.util.concurrent.FutureTask.get(FutureTask.java:111)
Could someone help me with what might be the issue?
Thanks.
Probably you have a checked exception somewhere in one of your beans that you catch in another one, but the transaction advice of spring notices it and will mark the transaction for rollback.
You can play with the noRollBackFor property of the Transactional annotation.
The possibility of getting this exception in Spring-managed environments is when the transaction propagation is set to REQUIRED:
<tx:method name="do*" propagation="REQUIRED" />
Consider a scenario as this:
Caller -------> [Transactional Method1(m1)] ----------> [Transactional
Method2(m2)]
In Spring-managed environments, there is a difference between logical and physical transactions. A logical transaction scope is created for each method upon which this setting is applied. The logical transaction scope for method m1 is different from m2. Each logical transaction can determine its own rollback-only status individually. And with this setting, an outer transaction scope (m1's scope) is logically independent from the inner transaction scope (m2's scope).
But all these scopes are mapped to the same physical transaction. So, if the inner transaction is marked for rollback, it effects the outer transaction's chance to commit (even if there is no exception thrown from the the outer transaction).
Now if there is an exception thrown from inner transaction and it is marked for rollback. But there is no exception thrown from outer transaction, so it has not decided on the rollback itself, and so the rollback (silently triggered by the inner transaction scope) is unexpected. And a corresponding UnexpectedRollbackException is thrown at that point.
So if an inner transaction (of which the outer caller is not aware) silently marks a transaction as rollback-only, the outer caller receives an UnexpectedRollbackException if it still calls commit. This is an indication to the outer caller that a rollback was performed instead of a commit. And this an expected behavior to let the caller of a transaction know that there was an exception and the the transaction was rolled back.
I am working on a legacy application. We are moving it from JDBC to Spring 3.2 + Hibernate 4.1.12 + JTA 2 with declarative transactions. I see that the Container-Managed Transactions (CMT) are transacting and rolling back as one would expect. We are using Infinispan as the second level cache (2LC). There is one wrinkle...
There is a portion of the code with a different entry point that is run in a different thread and uses programmatic transactions or Bean-Managed Transactions (BMT). In the BMT path, I see that in the underlying service layer, which is using CMT, the transactions are joining with the BMT as one would hope and expect.
The persistence unit, data source, etc. are the same for both entry points. In both cases, the Hibernate autoflush code is aware that there is a transaction and flushes to the database driver. In the CMT entry point, the database driver holds the data until told to commit or rollback. In the BMT path, the data is pushed into the database on flush – the later commit or rollback has no effect or apparent meaning. The transaction manager is the JtaTransactionManager. The JtaTransactionManager is defined in a #Configuration class with #EnableTransactionManagement to enable the CMT rather than the <tx:annotation-driven/> element.
The singleton JtaTransactionManager bean is wired with the ajuna UserTransaction and TransactionManager via jtaPropertyManager.getJTAEnvironmentBean().getTransactionManager() and jtaPropertyManager.getJTAEnvironmentBean().getUserTransaction(). Both the UserTransaction and TransactionManager are prototype #Bean definitions.
I am able to confirm the data is in or not in the database by a query from another query tool to verify the behavior while debugging.
When I am unit testing, the data commits and rolls back as expected for both the BMT and the CMT entry point.
The BMT is managed by a class that has the transaction begin and end in different methods. It also has methods that perform the actual unit of work. The transactions for the BMT are initiated with the PlatformTransactionManager, not the TransactionTemplate. The class is driven by another class that has the logic to manage the logic flow. I know that the transactions are beginning and ending as expected. When reading various other discussion, It seems implied that the transactional control should be within a single method. I would agree that this would be preferred but is it essential?
If a CMT-managed servlet in Spring spawns a new Thread and starts the thread with a plan thread.start(), is it reasonable to expect that a BMT within that new Thread would be able to manage its transactions as described above?
The datasource is retrieved by JNDI. Using XA or non XA does not influence the outcome.
I am unable to post the code.
As a reference, here is the link to the Spring 3.1 docs on transaction in chapter 11.
Added 2013/10/04 - I see that Spring uses the JtaTransactionManagerBeanDefinitionParser to construct the desired JtaTransactionManager based on the perceived container. When this is used, the JTA transaction manager will set into itself in the afterPropertiesSet the UserTransaction, TransactionManager, and TransactionSynchronizationRegistry.
It appears that I do actually still leak data in the CMT but that it is hard to perceive/observe this without a debugger or forcing an error unnaturally since the transactions typically commit.
It appears that my issue is that I have partially bypassed the JCA such that the JCA is using a different TransactionManager.
Partial Answer - Because I have seen this transact properly in a mix of CMT and BMT, I know that it is possible to have the BMT transaction started in one method and committed in another.
The question remains: If a CMT-managed servlet in Spring spawns a new Thread and starts the thread with a plan thread.start(), is it reasonable to expect that a BMT within that new Thread would be able to manage its transactions as described above?
From JTA 1.1 Specification (http://download.oracle.com/otn-pub/jcp/jta-1.1-spec-oth-JSpec/jta-1_1-spec.pdf) section 3.1, it is clear that the transaction is bound to the thread. This is managed by the TransactionManager. One should be able to expect the thread to be able to perform actions within a transactional context if the thread is the one that created the transaction.
Note that the support of nested transactions is optional as cited in the same portion of the JTA specification.
The actual issue I was encountering was that the managed datasource was using a different instance of the transaction manager than we had as a bean in the application. Changing the application code to do a JNDI lookup of the container-provided TransactionManager allowed the managed datasource to participate in the same transaction as the application.
I have read in the book that:
Typically, the container begins a transaction immediately before an enterprise bean method starts. It commits the transaction just before the method exits.
Suppose my stateful EJB has exposed 3 business methods and the client calls these 3 methods sequentially and want wants to run these 3 methods within a single transaction. These methods would be called across multiple requests.
Now the spec says that the transaction is committed just before the method exits. How will I be able to rollback the whole transaction, if my 1st method is successful and 2nd (or 3rd) method fails?
Please note that I don't want to use create my own transaction boundary.
Any help or the pointer in the right direction would be highly appreciated.
You are using a stateful session bean to act as a buffer. And you want a transaction around multiple calls.
From a design perspective, it's OK, if the transaction is started and committed/rollbacked from a single request (from within a single HttpServletRequest for example). In this case you can use a UserTransaction to span the transaction over multiple calls to an EJB. But a UserTransaction is bound to the current thread, so it might be difficult to pass it to the next request.
If you start and commit from different requests, you lose control over the duration of the transaction: Now a client controls the transaction. If that client crashes, the transaction won't be rolled back until the transaction timeout is reached. So the recommendation in this case is to buffer in a HttpSession for example. If all data has been collected, call a (stateless) EJB to persist it.
Create a method in the bean that calls all the other 3 methods. Then they'll be in the same transaction.
I am using the following piece of code I found online (Here) as an example of JTA Transaction processing:
// Get a UserTransaction
UserTransaction txn = new InitialContext().lookup("java:comp/UserTransaction");
try {
System.out.println("Starting top-level transaction.");
txn.begin();
stmtx = conn.createStatement(); // will be a tx-statement
stmtx.executeUpdate("INSERT INTO test_table (a, b) VALUES (1,2)");
stmtx.executeUpdate("INSERT INTO test_table2 (a, b) VALUES (3,4)");
System.out.print("\nNow attempting to rollback changes.");
txn.rollback();
}
I have a few questions, in general, about the JTA that are drawn from the example above:
I presume the whole point of saying txn.begin and then rollback is to be able to (apperently) rollback TWO SQL statements correct?
Each of the update queries were TRANSACTIONS themselves, right? They must have succeded so that we can get to rollback call at the bottom. Well, if they succeded i.e. commited, how on earth can we roll them back all of a sudden?
The most important question: what happens when we say txn.begin()? I understand from the JTA API that it is supposed to register this transaction with a calling thread by TransactionManager instance. How is TM even linked to the UserTransaction? And finally, how is the txn aware of the fact that we modified the DB twice and is able to speak to DB to roll it back? We have not registered ANY ResourceManagers with it so it should not be aware of any resources being at play...
I am a bit lost here, so any info would be appreciated... Question 3 bothers me the most.
yes, or event just one. It's also the ability of committing the transaction at the end, and thus have the other concurrent transaction only see the new state after the transaction has been committed, and not all the temporary states between the beginning and the end of the transaction (i.e. the I in ACID)
No. An update is an update. It's executed as part of the transaction that you begun previously. If one of them doesn't succeed, you'll have an exception, and can still choose to commit the transaction (i.e. have all the previous updates committed), or to rollback the transaction (i.e. have all the previous updates canceled).
The UserTransaction has a reference to its transaction manager, presumably. When you get a connection from a DataSource in a Java EE environment, the DataSource is linked to the transaction manager of the Java EE container, and rollbacking the JTA transaction will use the XA protocol to rollback all the operations done on all the data sources during the transaction. That's the container's business, not yours.
There's a lot to learn about transactions, but maybe I can give you a head start:
Yes. But you will usually only want to rollback in case of a problem - some step of the transaction could not be completed because of a technical issue (syntax error, table not found, segment overrun, ...) or an application logic problem (customer has not enough funds for all order line items for example).
Given auto commit mode is disabled, the inserts are not committed before you actually commit. They are temporarily applied to the database using a Write-Ahead-Log (PostgreSQL, InnoDB-Engine, Oracle) with sophisticated Multi-Version-Concurrency-Control (MVCC) which determines which state of the database each transactional client can see. A very interesting topic :-).
A UserTransaction is registered with your current Thread. Resources (i.e. Databases or Messaging services) enlist themselves with the UserTransaction. This is usually only necessary when you are using distributed transactions (XA transactions, 2PC).
I suggest to get a good read on SQL programming (for example Head First SQL) and check out the Java EE 6 tutorial.
I have a DBManager singleton that ensures instantiation of a single EntityManagerFactory. I'm debating on the use of single or multiple EntityManager though, because a only single transaction is associated with an EntityManager.
I need to use multiple transactions. JPA doesn't support nested transactions.
So my question is: In most of your normal applications that use transactions in a single db environment, do you use a single EntityManager at all? So far I have been using multiple EntityManagers but would like to see if creating a single one could do the trick and also speed up a bit.
So I found the below helpful: Hope it helps someone else too.
http://en.wikibooks.org/wiki/Java_Persistence/Transactions#Nested_Transactions
Technically in JPA the EntityManager is in a transaction from the
point it is created. So begin is somewhat redundant. Until begin is
called, certain operations such as persist, merge, remove cannot be
called. Queries can still be performed, and objects that were queried
can be changed, although this is somewhat unspecified what will happen
to these changes in the JPA spec, normally they will be committed,
however it is best to call begin before making any changes to your
objects. Normally it is best to create a new EntityManager for each
transaction to avoid have stale objects remaining in the persistence
context, and to allow previously managed objects to garbage collect.
After a successful commit the EntityManager can continue to be used,
and all of the managed objects remain managed. However it is normally
best to close or clear the EntityManager to allow garbage collection
and avoid stale data. If the commit fails, then the managed objects
are considered detached, and the EntityManager is cleared. This means
that commit failures cannot be caught and retried, if a failure
occurs, the entire transaction must be performed again. The previously
managed object may also be left in an inconsistent state, meaning some
of the objects locking version may have been incremented. Commit will
also fail if the transaction has been marked for rollback. This can
occur either explicitly by calling setRollbackOnly or is required to
be set if any query or find operation fails. This can be an issue, as
some queries may fail, but may not be desired to cause the entire
transaction to be rolled back.
The rollback operation will rollback the database transaction only.
The managed objects in the persistence context will become detached
and the EntityManager is cleared. This means any object previously
read, should no longer be used, and is no longer part of the
persistence context. The changes made to the objects will be left as
is, the object changes will not be reverted.
EntityManagers by definition are not thread safe. So unless your application is single threaded, using a single EM is probably not the way to go.