14 Replies Latest reply on Aug 30, 2011 12:07 PM by smarlow

    Synchronization of extended persistence contexts

    gonne

      Hi,

       

      I am trying to understand the synchronization of extended persistence contexts (EPC).    

       

      The hibernate documentation says:

      {quote}
      When a method of the stateful session bean involved or starting a transaction is later called, the entity manager join the transaction.
      All queued operation will then be executed to synchronize the persistence context.

      {quote}

       

      What exactly is here the meaning of "involved"?

      I have a simple example with a stateful session bean (SFSB) with a EPC and 2 methods marked with TransactionAttribute.NEVER. One method (queueUserNoTX) persists an entity and because it is marked with NEVER the persist operation is queued in the EPC. The other method (saveUser) calls a transacted method of a DAO stateless session bean (SLSB) to persist another entity.

       

      If I call first queueUserNoTX() and then saveUser() both persist operations are committed to the database.

      Is this behaviour as expected?

       

       

      {code}

      @Stateful

      @TransactionAttribute(TransactionAttributeType.NEVER)

      public class TestBean {

       

          @PersistenceContext(type=PersistenceContextType.EXTENDED)

          private EntityManager em;

       

          @EJB

          private TestDao dao;

       

          public void queueUserNoTx(User user) {

              // queue persist operation, no tx

              em.persist(user);

          }

       

          public void saveUser(final User user) {

              // no tx, call transacted dao to persist user

              dao.saveUser(user);

          }

      }

       

      @Stateless

      public class TestDao {

       

          @PersistenceContext

          private EntityManager em;

       

          public void saveUser(User user) {

              em.persist(user);

          }

      }

      {code}

        • 1. Re: Synchronization of extended persistence contexts
          smarlow

          During the TestDao.saveUser() call to em.persist(user), the extended persistence context (really its Hibernate session which is an internal detail), is used for the persist.  When the TestDao.saveUser method returns, its TX is completed and all changes in the extended persistence context at that point are saved.  This is by design, as the JPA specification

           

          If you want isolation/separation between the TestBean persistence unit and the TestDao persistence unit, change them to use separate PU definitions.  If the entire application is going to use the same persistence unit, then this interaction between extended persistence contexts and transactional persistence context is expected (the XPC on the invocation call stack, will be reused). 

           

          If you do separate the XPC into using a separate persistence unit, eventually, you need to invoke a transactional bean method, so that the xpc can be flushed to the database.

          1 of 1 people found this helpful
          • 2. Re: Synchronization of extended persistence contexts
            gonne

            Hi Scott,

             

            many thanks for your fast reply.

            I tried to comprehend it by reading the JPA specification (7.6.3 PC Propagation, 7.6.3.1 Requirements for PC propagation), but I am not sure which case applies here.

             

            {quote}

            If a component is called and there is no JTA transaction or the JTA transaction is not propagated, the persistence context is not propagated.

            • If an entity manager is then invoked from within the component:

                • (a) Invocation of an entity manager defined with PersistenceContext-Type.TRANSACTION will result in use of a new persistence context

                  (as described in section 7.6.1).

                • (b) Invocation of an entity manager defined with PersistenceContext-Type.EXTENDED will result in the use of the existing

                  extended persistence context bound to that component.

                • (c) If the entity manager is invoked within a JTA transaction, the persistence context willbe bound to the JTA transaction.

            If a component is called and the JTA transaction is propagated into that component:

                • (d) If the component is a stateful session bean to which an extended persistence context has been bound and there is a different

                  persistence context bound to the JTA transaction, an EJBException is thrown by the container.

                • (e) Otherwise, if there is a persistence context bound to the JTA transaction, that persistence context is propagated and used.

            {quote}

             

            I would say that my component TestDao is called and there is a JTA transaction, so PC is propagated (??).

            In TestDao an entity manager is invoked within a JTA transaction (case c), so the PC will be bound to the JTA transaction and this results in the synchronization of the queued operations in the XPC after the commit of the JTA transaction.

            Is that right?

             

            Kind regards,

            Gonne

            • 3. Re: Synchronization of extended persistence contexts
              smarlow

              Currently (and this goes back to earlier versions of JBoss AS (e.g. see https://anonsvn.jboss.org/repos/jbossas/projects/ejb3/trunk/testsuite/src/test/java/org/jboss/ejb3/test/epcpropagation/unit/EPCPropagationTestCase.java NoTxEPCStatefulBean testing), we are propagating the XPC into the SLSB invocation (which is why the synchronization of the queued operations occurs).

               

              The JPA 2.0 specification specifically spells out, that this is correct for a SFSB (XPC) invoking a SFSB(PC).  The specification doesn't directly say what should happen for a SFSB(XPC) invoking a SLSB(PC).  I think the current JBoss way is useful but compatibility is more important.

               

              I'll try to get further clarification on this. 

              1 of 1 people found this helpful
              • 4. Re: Synchronization of extended persistence contexts
                smarlow

                AS7-1663 is for changing the propagation logic in AS 7.0.2 to not propagate the XPC without it coming from an active JTA TX.

                • 5. Re: Synchronization of extended persistence contexts
                  smarlow

                  After AS7-1663 is fixed, saveUser() should only commit the DAO operations  to the database (the other change will remain in the XPC).

                  • 6. Re: Synchronization of extended persistence contexts
                    smarlow

                    AS7-1663 is resolved, please test with the nightly AS7 build (http://community.jboss.org/thread/167590) tomorrow.  Write back here with your results.

                    • 7. Re: Synchronization of extended persistence contexts
                      gonne

                      Hi Scott,

                       

                      I am glad about this change. I have tested my case with the nightly build yesterday and now it works as expected by me. Many thanks to you.

                       

                      Kind regards,

                      Gonne

                      • 8. Re: Synchronization of extended persistence contexts
                        gonne

                        Hi Scott,

                         

                        I have created some further test cases which have changed their behaviour since your latest changes.

                         

                         

                        {code}

                        @Stateful

                        @TransactionAttribute(TransactionAttributeType.NEVER)

                        public class TestSFSB {

                         

                            @PersistenceContext(type=PersistenceContextType.EXTENDED)

                            private EntityManager em;

                         

                            @EJB

                            private TestDaoSLSB dao;

                         

                            public User getUserNoTx(String name) {

                                // no tx, calling SLSB with new PC

                                User user = dao.findUserByName(name);

                                System.out.println("TestSFSB.getUserNoTx() user is " + (em.contains(user) ? "" : " NOT ") + " managed.");

                                return user;

                            }

                         

                            @TransactionAttribute(TransactionAttributeType.REQUIRED)

                            public User getUserTx(String name) {

                                User user;

                         

                                // SFSB with XPC and TX is calling SLSB with TX

                                user = dao.findUserByName(name);

                         

                                try {

                                    System.out.println("TestSFSB.getUserTx() user is " + (em.contains(user) ? "" : " NOT ") + " managed.");

                                }

                                catch(EJBException e) {

                                    System.out.println("TestSFSB.getUserTx() ERROR breaking JPA spec 2.0 section 7.6.3.1");

                                }

                                return user;

                            }

                         

                            @TransactionAttribute(TransactionAttributeType.REQUIRED)

                            public User getUserTxXpcPropagated(String name) {

                                User user;

                         

                                user = em.createQuery("select u from User u where name = :name", User.class).setParameter("name", name).getSingleResult();

                                System.out.println("TestSFSB.getUserTxXpcPropagated() A user(" + user + ") is " + (em.contains(user) ? "" : " NOT ") + " managed.");

                         

                                // SFSB with XPC and TX is calling SLSB with TX

                                user = dao.findUserByName(name);

                         

                                System.out.println("TestSFSB.getUserTxXpcPropagated() B user(" + user + ") is " + (em.contains(user) ? "" : " NOT ") + " managed.");

                                return user;

                            }

                        }

                         

                         

                        @Stateless

                        public class TestDaoSLSB {

                         

                            @PersistenceContext

                            private EntityManager em;

                         

                           public User findUserByName(String name) {

                                return em.createQuery("select u from User u where u.name = :name", User.class)

                                    .setParameter("name", name)

                                    .getSingleResult();

                            }

                        }

                        {code}

                         

                        JBoss7.0.0:

                        The user entity returned from the Dao in all 3 methods is managed.

                         

                        JBoss7.1.0.ALPHA (with AS7-1663 fix):

                         

                        getUserNoTx():

                        User is not managed ( case (a) of 7.6.3.1, see my post from 25.08.2011)

                         

                        getUserTx():

                        The Dao returns a user entity, but when the entity manager is used after it, an EJBException is thrown.

                         

                         

                        {quote}

                        Found extended persistence context in SFSB invocation call stack but that cannot be used because the transaction already has a transactional context associated with it.  This can be avoided by changing application code, either eliminate the extended persistence context or the transactional context.  See JPA spec 2.0 section 7.6.3.1.

                        {quote}

                         

                        getUserTxXpcPropagated()

                        The entity manager is used before calling the Dao. The Dao returns the same user entity as selected before in the SFSB. The entity is managed.

                         

                        I think the change for getUserNoTx() is okay, but not for getUserTx() and getUserTxXpcPropagated().

                         

                        Kind regards,

                        Gonne

                        • 9. Re: Synchronization of extended persistence contexts
                          smarlow

                          I have a local fix for the EJBException (working on the unit test now). 

                           

                          For getUserTxXpcPropagated(), The extended persistence context should be used for both searches, so I'm not following what is wrong there (not completely sure what you are seeing).

                          • 10. Re: Synchronization of extended persistence contexts
                            gonne

                            Sorry, I was not clear enough. The result for getUserTxXpcPropagated() is the same for both JBoss versions and is okay. I have used this case only for comparison with case getUserTx().

                            • 11. Re: Synchronization of extended persistence contexts
                              smarlow

                              Jira for the EJBException is AS7-1673.

                              • 12. Re: Synchronization of extended persistence contexts
                                smarlow

                                Gonne,

                                 

                                The error check appears to be working (I was wrong about it being broken).  An EJBException is supposed to be thrown, if a transactional persistence context is first used in the JTA transaction and then an attempt to bring a extended persistence context into the transaction is made.  This is a JPA (7.6.3.1) requirement (as mentioned in the error message):

                                 

                                If the component is a stateful session bean to which an extended persistence context has been bound and there is a different persistence context bound to the JTA transaction, an EJBException is thrown by the container.

                                 

                                https://github.com/scottmarlow/jboss-as/commit/7c7fa408e415ce1bb91963536d83cc0338f75325 contains a unit test for making sure the EJBException is thrown or not. 

                                 

                                The fix would be for getUserTx() to use the "em" before the "dao", so that the extended persistence context is used for both.  When the "em" is used, the extended persistence context will be associated with the transaction (the "dao" will see the extended persistence context in the transaction and use it).

                                 

                                Make sense?

                                • 13. Re: Synchronization of extended persistence contexts
                                  gonne

                                  Hi Scott,

                                   

                                  I think it depends on the interpretation of your quote (7.6.3.1)

                                   

                                  {quote}

                                  If a component is called and the JTA transaction is propagated into that component:

                                  • If the component is a stateful session bean to which an extended persistence context has been

                                  bound and there is a different persistence context bound to the JTA transaction, an EJBException

                                  is thrown by the container.

                                  • Otherwise, if there is a persistence context bound to the JTA transaction, that persistence context

                                  is propagated and used.

                                  {quote}

                                   

                                  In my case the component called is a SLSB ("TestDaoSLSB") and the JTA transaction of my SFSB ("TestSFSB") is propagated to it. So I would say that the "Otherwise" case applies here and the PC should be propagated.

                                   

                                  From my user perspective it is difficult to accept the difference between using the entity manager before a component call or not.

                                   

                                  One quote from the hibernate 3.6 documentation, chapter 1.2.4:

                                   

                                  {quote}

                                  If a stateful session bean with an extended persistence context calls as stateless session bean or a stateful session bean with a transaction-scoped persistence context in the same JTA transaction, the persistence context is propagated.

                                  {quote}

                                   

                                  When should a PC be bound to a JTA transaction (at method entry, at first entity manager user,...), is it defined in the specification?

                                   

                                  Regards,

                                  Gonne

                                  • 14. Re: Synchronization of extended persistence contexts
                                    smarlow

                                    When should a PC be bound to a JTA transaction (at method entry, at first entity manager user,...), is it defined in the specification?

                                     

                                    Yes, back to section 7.6.3.1.

                                     

                                    7.6.3.1 Requirements for Persistence Context Propagation

                                     

                                    Persistence contexts are propagated by the container across component invocations as follows.

                                     

                                    If a component is called and there is no JTA transaction or the JTA transaction is not propagated, the

                                    persistence context is not propagated.

                                     

                                    If an entity manager is then invoked from within the component:

                                    • Invocation of an entity manager defined with PersistenceContextType.TRANSACTION will result in use of a new persistence context (as described in section 7.6.1).
                                    • Invocation of an entity manager defined with PersistenceContextType.EXTENDED will result in the use of the existing extended persistence contextbound to that component.
                                    • If the entity manager is invoked within a JTA transaction, the persistence context will be bound to the JTA transaction.

                                     

                                    The above covers the PC getting bound to the JTA transaction.  The first bullet item below, covers the error condition.

                                     

                                    If a component is called and the JTA transaction is propagated into that component:

                                    • If the component is a stateful session bean to which an extended persistence context has been bound and there is a different persistence context bound to the JTA transaction, an EJBException is thrown by the container.
                                    • Otherwise, if there is a persistence context bound to the JTA transaction, that persistence context is propagated and used.

                                     

                                    The above requirements are important for certain cases, like if the PC (bound to the transaction) has pending changes, the changes should be visible to other code that runs in the same transaction.  If multiple persistence contexts (for the same PU name), were allowed to be bound to the transaction, pending changes wouldn't always be visible to other code running in the same transaction (using the same PU name).