How to start a new transaction inside a single EJB
When invoking a method of an EJB we get features like transactions, security or monitoring for free. Method calls within the same bean do not enjoy this benefits. This post shows by example how a method can be isolated from the current running EJB transaction and processed in a new separated one.
To start a new transaction in an EJB-Context we use the attribute TransactionAttributeType.REQUIRES_NEW, which can be annotated to a method in the following example:
1 2 3 4 |
@TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW) public void doSomething() { // ... } |
However, this can be a little bit tricky if we want to call such a method from another method in a single EJB. Imagine we have two methods cleanEntities() and delete() in an EJB. The method cleanEntities deletes records from a database table by calling the delete for every record, which should be processed in a new transaction as shown in listing 2.
1 2 3 4 5 6 7 8 9 10 11 12 |
@Stateless public class CleanupControl { public void cleanEntities() { delete(); } @TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW) public void delete() { // … } } |
If we run this code, we’ll be surprised that the method delete will never run in a new separated transaction as expected. The container will ignore the TransactionAttributeType.REQUIRES_NEW from the EJB. The reason of this behaviour is that the call of delete inside method cleanEntities will be treated as a normal Java invocation and not as an EJB method invocation and we’ll not get any warning from the container preventing that a new transaction haven’t being started.
There are several approaches to solve this problem:
The first way is to have a new EJB reference of the class. We call the method with the injected reference of the EJB itself as shown in listing 3:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
@Stateless public class CleanupControl { @Inject private CleanupControl self; public void cleanEntities() { self.delete(); } @TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW) public void delete() { // ... } } |
In this example the call of the method delete from the EJB itself will start a separated new transaction.
* The second way is to get an instance of type SessionContext that can be used to invoke the current bean through a particular business interface. The code below shows how to archive this.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
@Stateless public class CleanupControl { @Inject private SessionContext ctx; public void cleanEntities() { ctx.getBusinessObject(getClass()).delete(); } @TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW) public void delete() { // ... } } |
Leave a Comment