Search code examples
jakarta-eejbosshibernate-entitymanager

In a JavaEE stateless session bean, why is the SessionContext responsible for rolling transactions back and not the EntityManager?


To me (someone who is new to JavaEE development), I would think that a container managed EntityManager would be responsible for rolling back failed transactions and not a SessionContext instance. Suppose the following scenario...

@Stateless
public class MySessionBean implements MySessionBeanRemoteInterface {
    @PersistenceContext(unitName="MYPu")
    private EntityManager em;

    @Resource
    private SessionContext sctx;

    @Override
    public StackOverFlowUser createSOUser(String userName, int rep) {
         try {
             StackOverFlowUser su = new StackOverFlowUser();
             su.setUserName(stackOverflowName);
             su.setRep(rep);
             su.setIsBalusC(userName.equals("BalusC");
             su.setIsTheJonSkeet(userName.equals("jon skeet"));
             return em.merge(su);
         } catch (Exception e) {
             //sctx.setRollbackOnly();
             return null;
         }
    }

}

Why wouldn't the EntityManager be responsible for this? Why would SessionContext be used?


Solution

  • Because you told the container to manage transactions via JTA (transaction-type="JTA"), not JPA (transaction-type="RESOURCE_LOCAL"). JTA is in turn managed by EJB container. Hence the role of the SessionContext here.

    But what bothers me, you're suppressing the exception and returning null. You'd better not do that in business service methods. You'd better let the exception go instead of returning null. The EJB container will in any exceptional case automatically perform the rollback. Get rid of the try-catch and return null in EJB and just have the EJB's client deal with the exception itself.

    E.g.

    try {
        mySessionBean.createSOUser(userName, rep);
    } catch (PersistenceException e) {
        showSomeGlobalErrorMessage(e.getMessage());
    }
    

    Or, even better, just let it further go to the underlying container. E.g. if it's actually a servlet container:

    <error-page>
        <exception-type>javax.persistence.PersistenceException</exception-type>
        <location>/WEB-INF/errorpages/db-fail.xhtml</location>
    </error-page>
    

    Or perhaps the MVC framework in question has even a customizable global exception handler for that. At least, JSF allows this opportunity, you could then globally set a faces message without the need to repeat the try-catch over all place in managed bean methods calling a service method.

    See also: