9 Replies Latest reply on Nov 29, 2012 10:31 PM by smarlow

    Remoting and JPA Lazy Loading (AS 7.1.1)

    cfillot

      Hello all,

       

      I'm currently writing a standalone application which makes calls to remote stateless EJB. This part works great.

      Now, I've some EJB methods that return entities with fields being lazily fetched. If I try to access these fields

      from the client or from a next EJB call I get the famous LazyInitializationException.

       

      I tried to use the EJBClient.getUserTransaction() stuff to make all EJB calls run within the same transaction

      but that didn't help.

       

      Is there a way to do this ?

       

      Thanks in advance for any help.

       

      Christophe

        • 1. Re: Remoting and JPA Lazy Loading (AS 7.1.1)
          smarlow

          Which version of AS are you using?  Have you tried the AS nightly build?  When you tried accessing the fields on the next EJB call, did you try to EntityManager.merge(theEntity)?

           

          Scott

          • 2. Re: Remoting and JPA Lazy Loading (AS 7.1.1)
            cfillot

            I'm using AS 7.1.1. I don't call the EntityManager.merge() method, in fact I would like to have the entities still attached to the persistence context even if there are multiple EJB remote calls. To be honest, I don't know if this kind of behavior is supposed to be possible.

            I'll try the nightly build now. Do you suspect some bug to be fixed in it (I found AS7-5496 which was solved by a JBoss Marshalling update, but I don't have the same symptoms) ?

            • 3. Re: Remoting and JPA Lazy Loading (AS 7.1.1)
              smarlow

              I agree that It doesn't sound like your hitting AS7-5496.  I always like to try the latest AS build, just to see if it helps. 

              • 4. Re: Remoting and JPA Lazy Loading (AS 7.1.1)
                cfillot

                I've tried with 7.2.0.Alpha1-SNAPSHOT, still no luck. I updated the client to use the libraries matching this release (jboss-client, hibernate-core-4.1.6, and javassist 3.15.0).

                • 5. Re: Remoting and JPA Lazy Loading (AS 7.1.1)
                  smarlow

                  I don't call the EntityManager.merge() method, in fact I would like to have the entities still attached to the persistence context even if there are multiple EJB remote calls. To be honest, I don't know if this kind of behavior is supposed to be possible.

                   

                  You should be able to merge the detached entity into the new persistence context associated with the new JTA transaction (on the subsequent call).  Give that a try and see if that helps avoid the LazyInitializationException.  If you still get the LazyInitializationException, post the exception call stack.

                   

                  Another approach would be to use a stateful bean if your not already and switch to an extended persistence context which will keep the entity managed. 

                  • 6. Re: Remoting and JPA Lazy Loading (AS 7.1.1)
                    cfillot

                    - No problem if I use EntityManager.merge(), it works as expected

                     

                    - I tried the Stateful bean with extended persistence context, but without success, the entity is still seen as detached on next calls. Here is the code of the EJB:

                     

                    @Stateful
                    public class StatefulBean implements StatefulRemote {
                              @PersistenceContext(unitName="accounts",type=PersistenceContextType.EXTENDED)
                              private EntityManager em;
                      
                              @Resource
                              private TransactionSynchronizationRegistry txReg;
                      
                              public Account find(String username) {
                                        System.out.println("Transaction ID = " + txReg.getTransactionKey());
                                        return em.find(Account.class,username);
                              }
                      
                              public boolean isDetached(Account account) {
                                        System.out.println("Transaction ID = " + txReg.getTransactionKey());
                                        return !em.contains(account);
                              }
                    }
                    

                     

                    the client:

                     

                    StatefulRemote sfsb = (StatefulRemote) EjbLookup.lookup("StatefulBean","StatefulRemote?stateful");
                    Account a = sfsb.find("testuser");
                    System.out.println("isDetached = " + sfsb.isDetached(a));     // ==> prints "true"
                    
                    

                     

                    I get the same behavior even if I do this:

                     

                    StatefulRemote sfsb = (StatefulRemote) EjbLookup.lookup("StatefulBean","StatefulRemote?stateful");
                    UserTransaction tx = EJBClient.getUserTransaction("node");
                    tx.begin();
                    Account a = sfsb.find("testuser");
                    System.out.println("isDetached = " + sfsb.isDetached(a));     // ==> prints "true"
                    tx.commit();
                    
                    • 7. Re: Remoting and JPA Lazy Loading (AS 7.1.1)
                      smarlow

                      - No problem if I use EntityManager.merge(), it works as expected

                       

                      Good to hear that this worked for you.

                       

                      - I tried the Stateful bean with extended persistence context, but without success, the entity is still seen as detached on next calls. Here is the code of the EJB:

                       

                      @Stateful
                      public class StatefulBean implements StatefulRemote {
                                @PersistenceContext(unitName="accounts",type=PersistenceContextType.EXTENDED)
                                private EntityManager em;
                        
                                @Resource
                                private TransactionSynchronizationRegistry txReg;
                        
                                public Account find(String username) {
                                          System.out.println("Transaction ID = " + txReg.getTransactionKey());
                                          return em.find(Account.class,username);
                                }
                        
                                public boolean isDetached(Account account) {
                                          System.out.println("Transaction ID = " + txReg.getTransactionKey());
                                          return !em.contains(account);
                                }
                      }
                      

                       

                      the client:

                       

                      StatefulRemote sfsb = (StatefulRemote) EjbLookup.lookup("StatefulBean","StatefulRemote?stateful");
                      Account a = sfsb.find("testuser");
                      System.out.println("isDetached = " + sfsb.isDetached(a));     // ==> prints "true"
                       
                       
                      

                       

                       

                      The serialized entity becomes detached from the persistence context, when you return it to the ejb client tier.  The extended persistence context will have not help at all for your app and will consume additional memory since the original managed entity will still be held by the server tier for no purpose. 

                       

                       

                      I get the same behavior even if I do this:

                       

                      StatefulRemote sfsb = (StatefulRemote) EjbLookup.lookup("StatefulBean","StatefulRemote?stateful");
                      UserTransaction tx = EJBClient.getUserTransaction("node");
                      tx.begin();
                      Account a = sfsb.find("testuser");
                      System.out.println("isDetached = " + sfsb.isDetached(a));     // ==> prints "true"
                      tx.commit();
                      

                      yeah, same as explained above, when entities are returned over a remote connection, they are serialialized/deserialized which detaches them from the persistence context. 

                      • 8. Re: Remoting and JPA Lazy Loading (AS 7.1.1)
                        cfillot

                        Thanks for your answer, so I guess something like this won't work too ?

                         

                        StatelessRemote bean = (StatelessRemote) EjbLookup.lookup("StatelessBean","StatelessRemote");
                        Account a = bean.find("testuser");
                        List<Address> addresses = a.getAddresses(); // with addresses being a collection lazily fetched
                        

                         

                        In fact I thought the remoting code was able to maintain a "link" with the entitymanager, through some proxy magic.

                         

                        Thanks again for your help!

                        • 9. Re: Remoting and JPA Lazy Loading (AS 7.1.1)
                          smarlow

                          No link is currently maintained with the entity manager.  When bean.find("testuser") returns the Account entity, the account entity is detached from the entity manager (persistence context). 

                           

                          If you turn on TRACE logging for org.jboss.as.jpa, you will get a better idea of what is going on (see the as/standalone/log/server.log file.)  Instructions for enabling TRACE logging are here.