I thinking of a way to start a new transaction inside a single EJB bean. Typical use case would be to process every item from a list in a separate transaction.
One way to do it would be to use UserTransaction
:
@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class ManagerBean implements Manager {
@Resource
private UserTransaction tx;
@Override
public void processAll(List<Object> list) throws Exception {
for (Object obj : list) {
tx.begin();
try {
processOne(obj);
tx.commit();
} catch (Exception ex) {
tx.rollback();
}
}
}
private void processOne(Object obj) {
/* ... */
}
}
But I find it error prone to directly use commit()
and rollback()
. I'd prefer to leave transaction handing to the container. So I've come up with this pattern:
ManagerBean.java
@Stateless
public class ManagerBean implements Manager, ManagerInternal {
@Resource
private SessionContext context;
@Override
public void processAll(List<Object> list) {
ManagerInternal txThis = context.getBusinessObject(ManagerInternal.class);
for (Object obj : list) {
txThis.processOne(obj);
}
}
@Override
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void processOne(Object obj) {
/* ... */
}
}
@Local
interface ManagerInternal {
void processOne(Object obj);
}
Manager.java:
@Local
public interface Manager {
void processAll(List<Object> list);
}
Though I'm unsure if it's ok to have package-private local interface. It works on JBoss 4.2, but I don't know about other app servers.
What do you think about this pattern?
Alternative using self-injection:
Instead of using SessionContext.getBusinessObject(interface)
I suppose you can inject EJB into itself:
ManagerBean.java
@Stateless
public class ManagerBean implements Manager, ManagerInternal {
@EJB
private ManagerInternal txThis;
@Override
public void processAll(List<Object> list) {
for (Object obj : list) {
txThis.processOne(obj);
}
}
@Override
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void processOne(Object obj) {
/* ... */
}
}
@Local
interface ManagerInternal {
void processOne(Object obj);
}
Manager.java stays the same.
EJB interfaces with package
access seem to be not allowed by Wildfly.