Nested Transaction and EJBContext's setRollbackOnly() - java

I am reading the Transaction Management of Java EE 7 and I get confused by the concept of nested transaction and the functionality of EJBContext#setRollbackOnly().
Say I have two Session Beans, Bean1Impl and Bean2Impl and their signatures are:
#Stateless
#TransactionManagement(TransactionManagementType.CONTAINER)
public class Bean1Impl implements Bean1 {
#Resource
private EJBContext context;
#TransactionAttribute(REQUIRED)
public void method1() {
try {
//some operations such as persist(), merge() or remove().
}catch(Throwable th){
context.setRollbackOnly();
}
}
}
#Stateless
#TransactionManagement(TransactionManagementType.CONTAINER)
public class Bean2Impl implements Bean2 {
#Resource
private EJBContext context;
#TransactionAttribute(REQUIRED)
public void method2() {
try {
//some operations such as persist(), merge() or remove().
//an exception has been thrown
}catch(Throwable th){
context.setRollbackOnly();
}
}
}
As stated in Java EE 7 Tutorial:
51.3.1.1 Required Attribute
If the client is running within a transaction and invokes the enterprise bean's method, the method
executes within the client's transaction. If the client is not
associated with a transaction, the container starts a new transaction
before running the method.
The Required attribute is the implicit transaction attribute for all
enterprise bean methods running with container-managed transaction
demarcation. You typically do not set the Required attribute unless
you need to override another transaction attribute. Because
transaction attributes are declarative, you can easily change them
later.
In this case I don't need to specify #TransactionAttribute(REQUIRED) annotation declaration in the methods Bean1Impl#method1() and Bean2Impl#method2(). Am I right?
So in the above code the transaction of Bean2Impl#method2() would be running within the transaction of Bean1Impl#method1().
Can I consider it as a Nested Transaction?
If there is an Exception has been thrown inside the method Bean2Impl#method2() which eventually would lead to a call to the method EJBContext.setRollbackOnly() from the catch block and as expected it should roll back the operations performed within the try block of this method. In this case what would happen to the transaction and as well as the Bean1Impl#method1(). Would it be rolled back as well? What I mean is:
What would happen if there is a call of EJBContext.setRollbackOnly() from Bean2Impl#method2() and
Bean2Impl#method2() is called from the method Bean1Impl#method1() before any database operation like persist, merge or remove.
Bean2Impl#method2() is called from the method Bean1Impl#method1() after any database operation like persist, merge or remove.
And lastly what would happen if the method Bean2Impl#method2() get executed successfully but EJBContext.setRollbackOnly() is called from Bean1Impl#method1() after successful return of Bean2Impl#method2()?

To add to #Philippe Marshall's correct answer and your comment - REQUIRES_NEW will create a new transaction, independent from the first one. They are not nested. The first transaction is suspended while the second is active. Once the second transaction commits, the first one is resumed.
You do not have to setRollbackOnly() manually. Most PersistenceExceptions will do that if needed. You will gain nothing by rolling back transactions that you don't have to. For example, when querying data, you may get a NoResultException or a NonUniqueResultException. They do not cause a transaction to be rolled back as there is no risk of inconsistencies between the persistence context and the DB.
You do not need to specify neither #TransactionAttribute(REQUIRED) nor #TransactionManagement(TransactionManagementType.CONTAINER)- both are the default settings.
EDIT: to answer your further questions:
I am assuming #TransactionAttribute(REQUIRES_NEW) on method2 and therefore two separate transactions.
If there is an Exception that leads to the rollback of the transaction in method2, the transaction from method1 will not be rolled back if the Exception is caught. If the Exception is not caught, both transactions will be rolled back.
When setting the rollback flag on a transaction, it does not matter whether it happens before or after DB operations, since the entire transaction is rolled back.
Once method2 returns, it's transaction is committed. Rolling back or committing the transaction from method1 afterwards has no influence on the results of the first transaction.
A general advice - do not catch Throwable - it is much too broad and you might swallow exceptions which you would rather let propagate to the surface.

This is not nested transactions, JavaEE / JTA does not support nested transaction. If #method2() is called from #method1() it runs in the same transaction. If you want to have a different transaction you need #REQUIRES_NEW. EJBContext.setRollbackOnly() only works on the current transaction. Note there is a chance that after calling EJBContext.setRollbackOnly() all operations on the transactional resource including reads will throw an exception (JBoss AS 5.1 did this, don't know the current behaviour).
Update:
}catch(Throwable th){
context.setRollbackOnly();
}
You don't need this for runtime exceptions, this is the default behaviour of EJBs.

Related

Spring - Transaction should commit in one method but should rollback in other methods which does a DB transaction

How to handle Spring Transaction in Java where I have a scenario :
A service method let's say methodA() calls another method - methodB().
From methodB(), I am doing a validation and if that validation turns true, I will call another methodC() and this method must get committed, whereas all other methods should get rollback i.e. the transactions done from methodA() and methodB() must get rollback but transaction in methodC must be committed and it must not be rollback.
For rollbacking the transaction I use -
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
Real-time scenario -
When I perform a transaction, I try to create some transactions. Based on those transactions, I check if one of the value has reached threshold point, I need to trigger a mail. This mail is a DB transaction. All other transactions must be rollbacked and only the mail transaction must be persisted.
Any help appreciated.
EDIT: -
Similar question -
commit changes in try-catch inside #Transactional
But this doesnot solve my prblem. what if the #Transactional is given at Class Level and method level too..?
in my opinion,code like this
#Transactional(propagation= Propagation.REQUIRED)
public void methodA() {
methodB();
}
public void methodB() {
if(validtaion=true){
SeverB.methodC();
throw YourException;
}
}
// in ServerB.java
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodC() {
// do your thing
}
and,you'd better test whether it work well.

Spring UnexpectedRollbackException in nested #Transactional method

I have a dao class (MyDao) which is marked with #Transactional annotaion (on class level) with no additional parameters. In this dao class I have a method which in some case needs to throw a checked exception and perform a transaction rollback. Something like this:
#Transactional
public class MyDao {
public void daoMethod() throws MyCheckedException() {
if (somethingWrong) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
throw new MyCheckedException("something wrong");
}
}
This works perfectly fine. However, this dao method is called from a service method, which is also marked as #Transactional:
public class MyService {
#Autowired
private MyDao myDao;
#Transactional
public void serviceMethod() throws MyCheckedException {
myDao.daoMethod();
}
}
The problem is that when daoMethod() is called from serviceMethod() and marks the transaction as rollback only, I get an UnexpectedRollbackException.
Under the hood, Spring creates two transactional interceptors: one for MyDao and one for MyService. When daoMethod() marks the transaction for rollback, the interceptor for MyDao performs the rollback and returns. But then the stack moves to the interceptor for MyService, which finds out about the previous rollback, and throws the UnexpectedRollbackException.
One solution would be to remove the #Transactional annotation from MyDao. But this is not feasible right now because MyDao is used at a lot of places and this could cause errors.
Another solution would be to not set the transaction as rollback only in daoMethod() but rather mark serviceMethod() to revert the transaction on MyCheckedException. But I don't like this solution because I have a lot of these "service methods" and I would have to mark all of them explicitly to rollback on that exception. Also everyone who would add a new service method in the future would have to think of this and therefore it creates opportunities for errors.
Is there a way I could keep setting the transaction as rollback only from daoMethod() and prevent Spring from throwing the UnexpectedRollbackException? For instance, with some combination of parameters isolation and propagation?
I figured it out. I have to explicitly tell also the "outer" interceptor, that I want to rollback the transaction. In other words, both interceptors need to be "informed" about the rollback. This means either catching MyCheckedException in serviceMethod() and setting the transaction status to rollback only, or to mark serviceMethod() like this #Transactional(rollbackFor=MyCheckedException.class).
But as I mentioned in the OP, I want to avoid this because it's prone to errors. Another way is to make #Transactional rollback on MyCheckedException by default. But that's a completely different story.
Throwing an exception inside the transaction already trigger a rollback, using setRollbackOnly() is redundant here and that's probably why you have that error.
If the Transactionnal on the DAO is set up with Propagation.REQUIRED which is the default, then it will reuse an existing transaction if there is already one or create one if there is none. Here it should reuse the transaction created at the service layer.

Should I pass a managed entity to a method that requires a new transaction?

My application loads a list of entities that should be processed. This happens in a class that uses a scheduler
#Component
class TaskScheduler {
#Autowired
private TaskRepository taskRepository;
#Autowired
private HandlingService handlingService;
#Scheduled(fixedRate = 15000)
#Transactional
public void triggerTransactionStatusChangeHandling() {
taskRepository.findByStatus(Status.OPEN).stream()
.forEach(handlingService::handle);
}
}
In my HandlingService processes each task in issolation using REQUIRES_NEW for propagation level.
#Component
class HandlingService {
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void handle(Task task) {
try {
processTask(task); // here the actual processing would take place
task.setStatus(Status.PROCCESED);
} catch (RuntimeException e) {
task.setStatus(Status.ERROR);
}
}
}
The code works only because i started the parent transaction on TaskScheduler class. If i remove the #Transactional annotation the entities are not managed anymore and the update to the task entity is not propagated to the db.I don't find it natural to make the scheduled method transactional.
From what i see i have two options:
1. Keep code as it is today.
Maybe it`s just me and this is a correct aproach.
This varianthas the least trips to the database.
2. Remove the #Transactional annotation from the Scheduler, pass the id of the task and reload the task entity in the HandlingService.
#Component
class HandlingService {
#Autowired
private TaskRepository taskRepository;
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void handle(Long taskId) {
Task task = taskRepository.findOne(taskId);
try {
processTask(task); // here the actual processing would take place
task.setStatus(Status.PROCCESED);
} catch (RuntimeException e) {
task.setStatus(Status.ERROR);
}
}
}
Has more trips to the database (one extra query/element)
Can be executed using #Async
Can you please offer your opinion on which is the correct way of tackling this kind of problems, maybe with another method that i didn't know about?
If your intention is to process each task in a separate transaction, then your first approach actually does not work because everything is committed at the end of the scheduler transaction.
The reason for that is that in the nested transactions Task instances are basically detached entities (Sessions started in the nested transactions are not aware of those instances). At the end of the scheduler transaction Hibernate performs dirty check on the managed instances and synchronizes changes with the database.
This approach is also very risky, because there may be troubles if you try to access an uninitialized proxy on a Task instance in the nested transaction. And there may be troubles if you change the Task object graph in the nested transaction by adding to it some other entity instance loaded in the nested transaction (because that instance will now be detached when the control returns to the scheduler transaction).
On the other hand, your second approach is correct and straightforward and helps avoid all of the above pitfalls. Only, I would read the ids and commit the transaction (there is no need to keep it suspended while the tasks are being processed). The easiest way to achieve it is to remove the Transactional annotation from the scheduler and make the repository method transactional (if it isn't transactional already).
If (and only if) the performance of the second approach is an issue, as you already mentioned you could go with asynchronous processing or even parallelize the processing to some degree. Also, you may want to take a look at extended sessions (conversations), maybe you could find it suitable for your use case.
The current code processes the task in the nested transaction, but updates the status of the task in the outer transaction (because the Task object is managed by the outer transaction). Because these are different transactions, it is possible that one succeeds while the other fails, leaving the database in an inconsistent state. In particular, with this code, completed tasks remain in status open if processing another task throws an exception, or the server is restarted before all tasks have been processed.
As your example shows, passing managed entities to another transaction makes it ambiguous which transaction should update these entities, and is therefore best avoided. Instead, you should be passing ids (or detached entities), and avoid unnecessary nesting of transactions.
Assuming that processTask(task); is a method in the HandlingService class (same as handle(task) method), then removing #Transactional in HandlingService won't work because of the natural behavior of Spring's dynamic proxy.
Quoting from spring.io forum:
When Spring loads your TestService it wrap it with a proxy. If you call a method of the TestService outside of the TestService, the proxy will be invoke instead and your transaction will be managed correctly. However if you call your transactional method in a method in the same object, you will not invoke the proxy but directly the target of the proxy and you won't execute the code wrapped around your service to manage transaction.
This is one of SO thread about this topic, and Here are some articles about this:
http://tutorials.jenkov.com/java-reflection/dynamic-proxies.html
http://tutorials.jenkov.com/java-persistence/advanced-connection-and-transaction-demarcation-and-propagation.html
http://blog.jhades.org/how-does-spring-transactional-really-work/
If you really don't like adding #Transaction annotation in your #Scheduled method, you could get transaction from EntityManager and manage transaction programmatically, for example:
UserTransaction tx = entityManager.getTransaction();
try {
processTask(task);
task.setStatus(Status.PROCCESED);
tx.commit();
} catch (Exception e) {
tx.rollback();
}
But I doubt that you will take this way (well, I wont). In the end,
Can you please offer your opinion on which is the correct way of tackling this kind of problems
There's no correct way in this case. My personal opinion is, annotation (for example, #Transactional) is just a marker, and you need a annotation processor (spring, in this case) to make #Transactional work. Annotation will have no impact at all without its processor.
I Will more worry about, for example, why I have processTask(task) and task.setStatus(Status.PROCESSED); live outside of processTask(task) if it looks like does the same thing, etc.
HTH.

JPA2.0 commits transaction on flush immediatly

I'm using EJB3 and JPA2 in a project containing several modules.
Lately i have noticed that the DB-records won't rollback on exception. After doing some research i found that entity manager commits the transaction on flush immediatly even before the method ends, so that it can't rollback on exception.
I inject entity manager using
#PersistenceContext
private EntityManager entityManager;
To create a new record is persist and flush beeing called in the same class
entityManager.persist(entity);
entityManager.flush();
Even if i call throw new RuntimException("") right after flush, it wont rollback. On debug after flush is invoked i can select the DB-record with a database tool, before the method ends.
I already checked the persistence.xml and found nothing unusual. I dont use any other specifig configuration.
I'm out of ideas what might cause this behavior. I appriciate any clue.
You need to specify transaction boundaries, otherwise a new transaction will be opened and commited after each data manipulation query (INSERT, UPDATE, DELETE). As em.flush() will trigger such SQL query to be executed, it will open an implicit transaction and commit it if the SQL is successful, rollback in case of error.
In order to set transaction boundaries and make a RuntimeException trigger a rollback, the best option is to call entityManager methods from an EJB object. You must use a JTA datasource, not RESOURCE_LOCAL. If you don't use JTA datasource, you need to manage transactions by yourself, ie. by using entityManager.getTransaction() object.
Outside of an EJB, or with non-JTA datasource, you do not have any transaction open, unless you start it yourself by calling entityManager.getTransaction().begin(). However, in this way, your transaction will not be rolled back when Exception is thrown. Instead, you must roll back in catch block. This is mostly outside of Java EE container, in a Java SE application. In Java EE, I strongly suggest to use JTA datasource. Example:
public class NotAnEJB {
public persistEntity(EntityManager em, MyEntity entity) {
em.getTransaction().begin();
try {
em.persist(entity);
em.flush();
if (shouldFail()) {
throw new RuntimeException();
}
em.commit();
} catch (Exception e) {
em.rollback();
}
}
}

Why do we have to manually flush() the EntityManager in a extended PersistenceContext?

In our J2EE application, we use a EJB-3 stateful bean to allow the front code to create, modify and save persistent entities (managed through JPA-2).
It looks something like this:
#LocalBean
#Stateful
#TransactionAttribute(TransactionAttributeType.NEVER)
public class MyEntityController implements Serializable
{
#PersistenceContext(type = PersistenceContextType.EXTENDED)
private EntityManager em;
private MyEntity current;
public void create()
{
this.current = new MyEntity();
em.persist(this.current);
}
public void load(Long id)
{
this.current = em.find(MyEntity.class, id);
}
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void save()
{
em.flush();
}
}
Very important, to avoid too early commits, only the save() method is within a transaction, so if we call create(), we insert nothing in the database.
Curiously, in the save() method, we have to call em.flush() in order to really hit the database. In fact, I tried and found that we can also call em.isOpen() or em.getFlushMode(), well anything that is "em-related".
I don't understand this point. As save() is in a transaction, I thought that at the end of the method, the transaction will be committed, and so the persistent entity manager automatically flushed. Why do I have to manually flush it?
Thanks,
Xavier
To be direct and to the metal, there will be no javax.transaction.Synchronization objects registered for the EntityManager in question until you actually use it in a transaction.
We in app-server-land will create one of these objects to do the flush() and register it with the javax.transaction.TransactionSynchronizationRegistry or javax.transaction.Transaction. This can't be done unless there is an active transaction.
That's the long and short of it.
Yes, an app server could very well keep a list of resources it gave the stateful bean and auto-enroll them in every transaction that stateful bean might start or participate in. The downside of that is you completely lose the ability to decide which things go in which transactions. Maybe you have a 2 or 3 different transactions to run on different persistence units and are aggregating the work up in your Extended persistence context for a very specific transaction. It's really a design issue and the app server should leave such decisions to the app itself.
You use it in a transaction and we'll enroll it in the transaction. That's the basic contract.
Side note, depending on how the underlying EntityManager is handled, any persistent call to the EntityManager may be enough to cause a complete flush at the end of the transaction. Certainly, flush() is the most direct and clear but a persist() or even a find() might do it.
If you use extended persistence context all operations on managed entities done inside non-transactional methods are queued to be written to the database. Once you call flush() on entity manager within a transaction context all queued changes are written to the database. So in other words, the fact that you have a transactional method doesn't commit the changes itself when method exits (as in CMT), but flushing entity manager actually does. You can find full explanation of this process here
Because there is no way to know "when" the client is done with the session (extended scope).

Categories