5 Replies Latest reply on Nov 10, 2006 4:26 AM by chrismeadows

    EntityManagers and DAOs

    chrismeadows

      My scenario is that I don't want to do my entity manager interaction in a session bean - I want to use a layering approach that has Session beans <-> Business Delegates <-> DAOs <-> JPA entities.

      I'm getting frustrated that I cannot inject an entity manager into a DAO class inside the ejb container becasue it is not an EJB. I could use a jndi lookup, but then I jhave to worry about closing the entity manager.

      What I really want is to be able to access the EntityManager that was injected into the session bean that was my entry point to the container, but

      I do not want to pollute my BD and DAO API's with entity manager parameters.

      So, can I use ThreadLocal's? Do the folk at JBoss have a paradigm for doing this, or am I doing something completely stupid?

      The example code that follows is significantly trimmed. I've also left out the BD, and ignore the fact that a JPA entity is being returned to the client.

      I have a session bean with a simple getter method to load an entity. It delegates to a DAO

      @Stateful
      @Remote
      public class StatefulColumnGroupServiceBean {
      
       @PersistenceContext(unitName="MyApp", type=PersistenceContextType.EXTENDED)
       protected EntityManager em;
      
       public ColumnGroupEntity getColumnGroup(int iThreadNumber, long id) throws ServerException {
       EntityManagerResource.getInstance().set(em);
       ColumnGroupEntity cgvo = null;
       try {
       cgvo = ColumnGroupDAO.getInstance().getColumnGroup(id);
       }
       catch (DAOException e) {
       log.error(e);
       throw new ServerException( MessageFactory.getMessage("Bean.CannotFind", "ColumnGroup") + id, e );
       }
       return cgvo;
       }
      }
      


      The DAO is

      public class ColumnGroupDAO {
      
       public ColumnGroupEntity getColumnGroup( long id ) throws DAOException {
       EntityManager em = EntityManagerResource.getInstance().get();
      
       ColumnGroupEntity res = null;
       try {
       res = em.find(ColumnGroupEntity.class, id);
       }
       catch (RuntimeException e) {
       throw new DAOException(MessageFactory.getMessage("DAO.FindError", getClass().getName()) + id, e);
       }
       return res;
       }
      
      
       protected ColumnGroupDAO() {
       }
      
       public static ColumnGroupDAO getInstance() {
       if( instance == null ) {
       synchronized( ColumnGroupDAO.class ) {
       if( instance == null ) {
       instance = new ColumnGroupDAO();
       }
       }
       }
       return instance;
       }
      
       private static ColumnGroupDAO instance = null;
      
      }
      


      and the EntityManager is passed between bean and DAO by using a singleton ThreadLocal wrapping an EntityManager instance

      public class EntityManagerResource extends ThreadLocalResource<EntityManager> {
      
       private static EntityManagerResource instance = null;
      
       public static EntityManagerResource getInstance( ) {
       if( instance == null ) {
       synchronized( EntityManagerResource.class ) {
       if( instance == null ) {
       instance = new EntityManagerResource( );
       }
       }
       }
       return instance;
       }
      
       protected EntityManagerResource() {
       }
      }
      
      public abstract class ThreadLocalResource<T> {
      
       private final ThreadLocal<T> resource = new ThreadLocal( );
      
       public void set( T em ) {
       resource.set(em);
       }
      
       public T get( ) {
       return resource.get();
       }
      }
      
      


      Does this make sense? Any suggestions?

        • 1. Re: EntityManagers and DAOs
          monkeyden

          I'd be interested in hearing the JBoss take on this as well. For the small discovery project I'm working on currently, I chose to "pollute my BD and DAO API's with entity manager parameters", while recognizing the design gap. I can't however accept the same for the next and much larger project.

          • 2. Re: EntityManagers and DAOs
            chrismeadows

            Done some more testing using the above ThreadLocal paradigm, because I was worried about multiple serfver side threads and in particular if the JBoss container ever uses >1 thread to service a session bean call.
            But apparently it does not - I can happily kick off multiple (e.g. 100) concurrent client threads that start multiple SFSBs, and all is fine.

            So it's promising.

            I don't want to take it too much further in case a JBoss representative confirms that there is a very good reason to not do this, and if so, indicate a better way of doing it.

            • 3. Re: EntityManagers and DAOs
              fbadt

              Watch out for memory leaks in threads. I handled resources in a BEA deployed app with thread locals. This the threads come from a cache, the thread local will hang onto the reference, to whatever you put into it, even after the session bean call is done.

              • 4. Re: EntityManagers and DAOs
                alrubinger

                A couple of years ago my company had an EJB training session w/ Bill. We had expressed concern over our "polluted" method signatures and passing the same variables all over the place.

                He suggested the use of ThreadLocal, citing that it will have the same scope as any one execution context (or transaction).

                So, the following holds true:

                * A session bean call will execute in one thread
                * Watch out for thread pooling

                ...if you set stuff in a ThreadLocal, ensure that its removed if the Thread has been obtained from a Thread pool. Otherwise, nightmares.

                For instance, I've set web-tier stuff in ThreadLocal to be easily obtained from other layers. But Tomcat uses a thread pool, so after my request had executed, I had to ensure that a Filter ran at the end of my FilterChain to clean up anything I placed in the ThreadLocal.

                Or in your case...if you don't want to put any JPA stuff in your DAO APIs...why not just pass the EM into the DAOImpl Constructor?

                S,
                ALR

                • 5. Re: EntityManagers and DAOs
                  chrismeadows

                   

                  "ALRubinger" wrote:

                  ...if you set stuff in a ThreadLocal, ensure that its removed if the Thread has been obtained from a Thread pool. Otherwise, nightmares.


                  Thanks ALR, I'll watch out for that

                  "ALRubinger" wrote:

                  Or in your case...if you don't want to put any JPA stuff in your DAO APIs...why not just pass the EM into the DAOImpl Constructor?


                  DAOImpls are currently singleton per DAO iface, but must be thread safe, so I'm using ThreadLocal rather than passing in via method parameters.

                  I could stop using a singleton and create a DAOImpl per DAO request (as you suggest), but then I have a proliferation of DAOs just to ensure thread safety which smells of an antipattern.

                  Thank you for your comments everyone.

                  C