Search code examples
javasessionjpaejb

Pass Injected EntityManager to a web session persisted object


Is it secure to pass a Injected EntityManager created on an EJB, to a method that will return an Object, and after, persist that Object on a Web Session for web clients use it?

Like in this example: the EJB

@Stateless(mappedName = "MyService")
@LocalBean
public class MyService implements MyServiceLocal {

@PersistenceContext(unitName="primary")
private EntityManager em;

    /**
     * Default constructor. 
     */
    public MyService() {   
    }


    @Override
    public Service newServiceX(User user) {
        return new ServiceX(user,em); // here, passing the EntityManager
    }

}

After, I persist this Service in a web client (using struts): The base action

    public class YAction extends ActionSupport implements SessionAware{
    @Inject
    private MyServiceLocal service;

    public String execute(){
    Service x = service.newServiceX();
    persistInCookie("ServiceX",x);
    }

    public void persistInCookie(String, Object){
    // persist
    }
    }

And after, using another Action: // another Action that

   class XAction{

   public String useService(){
   getService().doSomething();
   }


   protected Service getService(){
    Service service = (Service) getSessionMap().get("ServiceX");
    return service;
}

}

the POJO class ServiceX using the EntityManager:

public class ServiceX extends Service{

EntityManager em;

public ServiceX(User user, EntityManager em){
this.em = em;
}

public void doSomething(){
// do something with the EntityManager passed by the EJB
}

}

First, the action that would be call is the Y action to persist the Service on the Session, next, the X action will return the Service persisted on the Session and try to use it.

I believe that the EJB Stateless Session Bean can close My EntityManager and this ServiceX POJO class can't use it. This can happen? I found similar question HERE, but in this question, the EntityManager is passed to a helper class. In my case is different because I want to persist this Object on a session cookie, and use later.


Solution

  • I don't think It is a good idea to store a EntityManager in SessionMap. What is more, I don't even think that it is a good idea to perform EntityManager operations outside the EJB container.

    Have read about transaction-boundaries in JPA?

    By default, EJB container is using CMT (Container Managed Transactions). In this case, container uses entitymanager-per-request pattern which means that the transaction begins and ends when one of the business methods of MyService starts and ends (transaction is committed or rollbacked in case of RuntimeException). For whole transaction time, EntityManager is connected with the same PersistenceContext. After the transaction is ended the container closes EntityManager which means that the EntityManager is disconnected with recent PersistenceContext:

    // transaction begins
    Service x = service.newServiceX();
    // transaction ends
    

    This might be crucial if you were going to do some update/insert operations outside the transaction.

    Now, when you call EntityManager operation (like find) outside the transaction, for every each operation the EntityManager will create new PersistentContext. This may cause some issues, as two entities that represent the same record will be treated as different entities:

    // each operation occurs in a separate persistence context, and returns 
    // a new detached instance
    Magazine mag1 = em.find(Magazine.class, magId);
    Magazine mag2 = em.find(Magazine.class, magId);
    assertTrue(mag2 != mag1);
    

    Some more articles to read:

    Persistent Context

    Transactions and Concurrency

    Entity Lifecycle Management