1 2 Previous Next 17 Replies Latest reply on Dec 18, 2007 3:02 PM by Pete Muir

    persist() / remove() at end of long running conversation?

    Michael Heß Newbie

      Hi,

      I´m experimenting with Seam Managed long running atomic conversations right now. I succesfully managed to have changes made to existing entities get flushed when the @End method is called. This works fine so far and is conform to my understanding of a Seam Managed Transaction using @Begin and @End Annotations.

      My next step was to create a new entity during that conversation. To attach the new instance to the entityManager I´m using persist() (side note: tried merge() as well, no difference). What puzzles me, is that immediately when em.persist(myNewInstance) is called, an insert is happening (used the debugger to confirm this).

      I tested some more, and so far I can say, that it´s only the insert that is happening at the time persist() is called. When I have also made changes to the already existing entities in the same conversation, they are still correctly updated at the end of the conversation, not before.

      So I´m wondering - is creating new instances in a long running conversation a somewhat "invalid" approach?

      A possible solution might be to put the new instances in some kind of collection and call persist on each of its members during the @End method. But maybe there is a more "automatic" way Seam provides?

        • 1. Re: persist() / remove() at end of long running conversation
          Pete Muir Master

          Are you using @Begin(flushMode=FlushModeType.MANUAL) - AFAIK this should only do the persist when flush() is called.

          • 2. Re: persist() / remove() at end of long running conversation
            Christian Bauer Master

            persist(), even with FlushMode.MANUAL, will do an immediate SQL INSERT if the identifier generator of the entity requires this. For example, 'identity' does, while 'sequence' can generate identifier values pre-INSERT.

            If you call persist() outside of transaction boundaries (TransactionType.NOT_SUPPORTED on your EJB method), it is allowed to defer insertion for pre- and post-insert identifier generators. The entity instance is then considered managed, but hasn't an identifier value assigned until the next flush of the persistence context occurs.

            http://www.manning.com/bauer2

            • 3. Re: persist() / remove() at end of long running conversation
              Manuel Gombocz Newbie

              Hi,
              I have similar problems, so I did further tests based on

              - JBoss AS 4.0.5.GA
              - Seam CVS nightly build of 02/18/2007 (based on 1.1.6.GA)
              - Hibernate as (preconfigured) JPA provider

              My initial situation is the same like the one of m_hess (using SMPC and flushMode=FlushModeType.MANUAL):

              Hi,

              I´m experimenting with Seam Managed long running atomic conversations right now. I succesfully managed to have changes made to existing entities get flushed when the @End method is called. This works fine so far and is conform to my understanding of a Seam Managed Transaction using @Begin and @End Annotations.

              My next step was to create a new entity during that conversation. To attach the new instance to the entityManager I´m using persist() (side note: tried merge() as well, no difference). What puzzles me, is that immediately when em.persist(myNewInstance) is called, an insert is happening (used the debugger to confirm this).

              I tested some more, and so far I can say, that it´s only the insert that is happening at the time persist() is called. When I have also made changes to the already existing entities in the same conversation, they are still correctly updated at the end of the conversation, not before.


              I tested 2 scenarios:

              1) Using @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) on relevant methods
              The Entity's @Id field is annotated with @GeneratedValue (i.e. by default: strategy=AUTO).
              After calling persist() no immediate SQL INSERTs are performed (-->correct).

              BUT, by calling the method which calls flush() it causes an exception:
              org.hibernate.TypeMismatchException: Provided id of the wrong type. Expected: class java.lang.Long, got class org.hibernate.action.DelayedPostInsertIdentifier

              2) Using @GeneratedValue (strategy = GenerationType.SEQUENCE, ...) on Entity objects
              By using Sequences SQL INSERTs will performed after calling flush(). No @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) annotation is required (-->very good).

              BUT, not all RDBMS systems, like e.g. MySQL, support sequences. :-(


              re. 1) Has anybody made the same experience or do I something wrong?
              re. 2) Are there other ways that are supported by all RDBMS or at least MySQL?

              Thanks!

              • 4. Re: persist() / remove() at end of long running conversation
                Manuel Gombocz Newbie

                I have to add:
                I used HSQL DB for the 2 scenarios.

                • 5. Re: persist() / remove() at end of long running conversation
                  Gavin King Master

                  Hibernate always does an immediate INSERT when you use generated ids. This is something we need to fix in Hibernate.

                  I have a feeling that this might not be the case if you use HEM instead of plain Hibernate, but I'm not sure about that.

                  • 6. Re: persist() / remove() at end of long running conversation
                    Gavin King Master

                    Steve says I'm wrong and that this now works in Hibernate.

                    • 7. Re: persist() / remove() at end of long running conversation
                      Manuel Gombocz Newbie

                      Thanks for your quick reply!

                      A few more questions:

                      Hibernate always does an immediate INSERT when you use generated ids. This is something we need to fix in Hibernate.
                      Steve says I'm wrong and that this now works in Hibernate.

                      1) Does this mean, that with the new Hibernate version only SMPC with FlushModeType.MANUAL is required, and no additional annotations like @TransactionAttribute are necessary?
                      2) In which Hibernate version is this fixed?
                      3) With Seam there comes "hibernate-all.jar". How can this be updated to solve the problem?

                      Thanks again!

                      • 8. Re: persist() / remove() at end of long running conversation
                        Gavin King Master

                        Wait for Steve to answer, he's actually not sure about the details of this now.

                        • 9. Re: persist() / remove() at end of long running conversation
                          Christian Bauer Master

                          What I said was correct :)

                          The questions:

                          1) Yes
                          2) Hibernate 3.2
                          3) Wait for a new E-EJB3 release

                          • 10. Re: persist() / remove() at end of long running conversation
                            Christian Bauer Master

                            And I did test this with something later than 3.2.0 GA and it worked with HSQL DB, tested 'identity' and 'sequence'.

                            • 11. Re: persist() / remove() at end of long running conversation
                              Steve Ebersole Apprentice

                              Historically Hibernate would always immediately perform an insert for entities using an IDENTITY based strategy. The reason was that the Hibernate save() method has a distinctly different semantic from persist(); namely save() requires the generated identifier value to be returned as a return value from the save call!

                              persist() is slightly different in that it's declared return type is void, thus the generated identifier does not need to be immediately available upon return from the persist() call. Thus I changed this behavior starting in 3.2 such that the insertion to generate and obtain the identifier value is delayed until a flush. However, that only occurs when we are outside a transaction. This should probably be extended such that we also delay in the case of FlushMode.MANUAL as well.

                              This should be easy to verify. Have a look at org.hibernate.event.def.AbstractSaveEventListener#performSaveOrReplicate. Specifically the part about 'shouldDelayIdentityInserts'. Currently, that is set via:

                              boolean shouldDelayIdentityInserts = !inTxn && !requiresImmediateIdAccess;
                              


                              Try changing that to:
                              boolean shouldDelayIdentityInserts = !requiresImmediateIdAccess &&
                               ( !inTxn || FlushMode.isManualFlushMode( source.getFlushMode() ) );
                              


                              Let me know how it goes at : http://opensource.atlassian.com/projects/hibernate/browse/HHH-2439

                              • 13. Re: persist() / remove() at end of long running conversation
                                Frits Jalvingh Newbie

                                I seem to have the same problem. I'm running seam 2.0.0b1 with hibernate 3.2.3, hbem 3.3.1 in JPA mode without EJB (Tomcat native).

                                I have a method as follows:

                                @Transactional(TransactionPropagationType.REQUIRED)
                                @Begin(flushMode=FlushModeType.MANUAL)
                                public String createNewTarget() {
                                 System.out.println("method entered (fm=manual we hope)");
                                 m_target = new Target();
                                 m_target.setTargetSourceList(new ArrayList<TargetSourceModule>());
                                
                                 m_target.setKey("akey");
                                 m_target.setName("Name");
                                 System.out.println("method before persist()");
                                 m_daoBuilder.save(m_target);
                                 System.out.println("method after persist()");
                                 return "newtarget";
                                }
                                


                                and when this gets called Hibernate immediately inserts the record:
                                01:26:41,964 DEBUG [SeamPhaseListener] before phase: INVOKE_APPLICATION 5
                                method entered (fm=manual we hope)
                                method before persist()
                                Hibernate: select nextval ('ab_target_sq')
                                *** fix: shouldDelayetc=false, fm=AUTO, ria=true
                                Hibernate: insert into ab_target (tgt_autoDiscover, tgt_key, tgt_name, tgt_repository_version, tgtid) values (?, ?, ?, ?, ?)
                                method after persist()
                                01:27:03,786 DEBUG [Manager] Beginning long-running conversation
                                


                                The ***fix line is displayed around the fix you proposed. As you can see flushmode is manual and requireimmediateresult is true.

                                I have debugged thru Seam and found one very odd thing though- the @Begin annotation seems to set the flushmode etc after the method is invoked, not before! It is done in ConversationInterceptor.aroundInvoke; the pertinent code is:
                                Object result = invocation.proceed();
                                beginConversationIfNecessary(method, result);
                                endConversationIfNecessary(method, result);
                                return result;
                                


                                The call to "beginConversation" sets the flush mode, but after the deed has been done. I don't yet know if this is the cause of the problem but having the annotation "start" after the method call seems weird to me to say the least.


                                • 14. Re: persist() / remove() at end of long running conversation
                                  Frits Jalvingh Newbie

                                  Ok; and part 2: AbstractSaveEventListener calls saveWithGeneratedId() with requiresImmediateIdAccess == false (which is good). It then generates an identifier which in my case comes from a sequence; it generates a Long. After that it defaults into a call to:

                                  return performSave( entity, generatedId, persister, false, anything, source, true );
                                  


                                  which causes requiresImmediateIdAccess in the lower calls to be true. I think the code should replace true with requiresImmediateIdAccess as the key *was* generated OK without an insert needed..

                                  I tested that and it fixed the problem. Sadly enough it broke my patch for HHH-2588 so I'll have to look into that some more.


                                  1 2 Previous Next