1 2 3 Previous Next 41 Replies Latest reply on Jul 13, 2009 5:47 PM by asookazian Go to original post
      • 30. Re: entityManager single entity update
        pmurphy.pjmurphy.paddypower.com

        How would you stop edits to entities getting flushed out to the database by the original entity manager that retrieved them?

        • 31. Re: entityManager single entity update
          pmurphy.pjmurphy.paddypower.com

          Hi Juan,


          The support tag I'm using is for a h:selectOneMenu item, so I don't think that this applies in my case.


          <h:selectOneMenu value="My Label">
              <f:selectItems value="#{yesNoDropDown}" />
          
              <a4j:support event="onchange" action="#{myAction.updateEwMaster(market)}" ajaxSingle="true"/>
          
          </h:selectOneMenu>
          



          Thanks,


          Philip

          • 32. Re: entityManager single entity update
            brandonsimpson

            Set it to use manual flush mode and never flush it.

            • 33. Re: entityManager single entity update
              pmurphy.pjmurphy.paddypower.com

              Brandon,


              Now, that's what I call lateral thinking. Very interesting solution to a very tricky problem. Currently I am using Session Scope which should be fine for the scenario that you mention - (maybe I'd be better off to use Conversation scope, but that's another story). After the user clicks save I'd need to send the entity back to the server. Here is some pseudo code for what I think you are saying (take note that I am working from memory and have had a glass of wine ;-) [must be taking a leaf out of Stuart's book - ha ha]


              @Stateful
              @Scope(SESSION)
              @Name("myBackingBean")
              public class MyBackingBeanImpl implements MyBackingBean {
              
                  @In
                  private EntityManager sessionEM;
              
                  @In(scope=EVENT)
                  private EntityManager eventEM;
              
                  @DataModel
                  private List<MyEntity> entities;
              
                  public void search() {        
                      Query query = sessionEM.createQuery(...);
                      entities = query.resultSet();
                  } 
              
                  public void save(Entity entity) {
                      // Not too sure about this bit as entity would have been originally retrieved from 
                      // sessionEM - do you think this will work or is there some other data gymnastics 
                      // that has to be done as well. I guess that I would need to ensure that equals
                      // and hashCode are properly overridden for this to work.
                      eventEM.merge(entity);
                      eventEM.flush();
                  }
              }
              



              Every time the same user within a session does a search it would overwrite the entities variable which should allow them to be garbage collected i.e. they would now be unreachable. Maybe a call to clear on the list wouldn't go amiss if the list had already been initialised.


              Thanks again for your comments.


              Cheers,


              Philip



              • 34. Re: entityManager single entity update
                pmurphy.pjmurphy.paddypower.com

                Opps, forgot the most important line.


                @Begin(flushMode=MANUAL)
                public void search() {        
                        Query query = sessionEM.createQuery(...);
                        entities = query.resultSet();
                    } 
                


                • 35. Re: entityManager single entity update
                  asookazian

                  Quite possibly one of the most interesting (and most important) topics in this forum so far...  This is more likely JPA-related that Seam but whatever...


                  I have experienced a very similar problem recently.  We tend to use rich:dataTable and h:form components embedded in rich:modalPanel components a lot.


                  So you'll have the base JSF/xhtml view with a rich:dataTable and an edit column and a delete column.  Each row has an edit and delete button in the dataTable.  When user clicks one of the buttons for edit, the modalPanel displays and the data for that row in the base dataTable is now editable and there's a save button.  When the user clicks a delete button, that row is deleted (but not persisted to db until the save button is clicked on the main xhtml form).


                  Ok?


                  Well the problem was the exact same as the ones being discussed here in that when the user clicked delete in the main form/xhtml and then clicked edit and modified data in modalPanel form, then clicked save in modalPanel form, data was persisted that was not intended by the user to be persisted yet (the main form's row delete).  This is because I am using SMPC with Hibernate manual flush (as is recommended by most Seam core devs).  When the entityManager is flushed(), all dirty/managed/removed entities are sync'd with db.  A workaround was to have only one save button but that was deemed an unacceptable solution from a UI/functional requirements perspective.  You need to have a save button where the user expects it!


                  My solution to this problem was to use one SMPC for the main xhtml page/entities and a different SMPC for the modalPanel entities.  So no matter how many modalPanels there are in a single base page, there is a modalPanel SMPC that is used for those entities/forms.  And there is a single SMPC for the main form's entities.


                  I was not sure if that was the best solution but that's what we have now.  So I am using SMPC and Hibernate manual flush successfully for both.


                  I know it's possible to have more than one SMPC defined in components.xml but what if you had 10 or 20 hypothetically?  Each SMPC, AFAIK, takes up memory in your JVM's heap space ultimately.  Emanuel Bernard gave a talk regarding scaling Hibernate recently:


                  http://www.infoq.com/presentations/Scaling-Hibernate-Emmanuel-Bernard-Max-Ross


                  IIRC he stated that each PC takes approx. 20MB...

                  • 36. Re: entityManager single entity update
                    asookazian

                    Now considering the use case where multiple entities need to be presented and modified (let's keep it simple - just updates) on the same JSF view.  So you are trying to achieve some sort of isolation with or without multiple save buttons?


                    I would consider this an advanced use case, like the modalPanel use case I described above.  Most likely it would be easier to break this use case into multiple pages, perhaps one page per entity if possible.


                    If not, it looks like you'll be experimenting with accessing the Hibernate Session interface directly probably via this kind of code:


                    Session session = (Session)entityManager.getDelegate();



                    And then you are no longer vendor-neutral as you'd be if you stuck with the JPA EntityManager interface exclusively.


                    Are there any Seam examples in the distro that implement these advanced use cases?  I know the booking app is fairly straight-forward (in terms of CRUD to one entity per page basically).


                    You really want to use atomic conversations via SMPC and Hibernate manual flush.


                    I guess your other (worst case scenario) option is to use stored procs via JDBC for your CRUD in these multiple-entity CRUD pages....

                    • 37. Re: entityManager single entity update
                      asookazian

                      Brandon Simpson wrote on Jul 08, 2009 18:23:


                      I've also run into some other related problems lately as well. For example, suppose a user visits an interactive edit screen for an entity and makes some changes. Typically I think you would start a long runnning conversation at the beginning of loading the page so that the changes would be maintained througout the use case. Once the save button is clicked, the persistence context with changes can by flushed/synched to the DB and the long-running conversation demoted back to short-running so it will be cleaned up. But in my project there are always links to other pages the user can click at any time, so if the user clicks on one of these links without completing the use case, the long running conversation will go with them (along with the persistence context containing the edited entity). So if the user then went to some other page, perhaps a page allowing editing of some other entity and clicked the save button there, I could see that I may unintentionally be synching out my changes from another page! Maybe I'm wrong on this, but that's how it seems to me. It appears to me that you must take great care to make sure that a new conversation is started when leaving any page where data could be modified or you'll run into these persistence leaks. If I understand correctly, Seam will automatically propogate the conversation unless you take steps to not propogate the conversation...lots of room for error here!

                      I'm looking forward to hearing if I'm understanding this correctly or not and also if anyone has good ways to deal with this!


                      This scenario of when a user makes some changes (say possible updates to HtmlInputText fields) to a form on page A and then navigates to page B and does the same in another form and saves (entityManager.flush()) is a potential total disaster.  When they go back to page A and see the changes persisted in page A, is that ok?  Most likely not because they didn't click the save button on page A!


                      So is this how it would work by default if you're using the same EntityManager instance, and thus the same SMPC, in the backing beans for both pages (say two SFSBs that are conversation-scoped)?


                      Well, I guess it depends on your conversational modeling.  If you use @Begin(join=true, flushMode=FlushModeType.MANUAL) for the starting action methods in each SFSB, then yes.  But what if you begin a new LRC for page B (or a nested conversation?) 


                      Actually, can you begin a new LRC programmatically if one is currently active?  I think you can't because that's what @Begin does and you get an exception if one is running so you must use @Begin(join=true).


                      And you might not necessarily want to undo the user's changes in page A form when they navigate to page B, right?


                      What about if page B is not part of the same use case as page A?  Like in the booking example, there are multiple related pages in a multi-step, sequential wizard.


                      I believe there are a lot of outlier scenarios for which JPA is not well-suited for...


                      I'd like to hear an official response to this thread from a Seam core dev.


                      We're past 30 reponses man...

                      • 38. Re: entityManager single entity update
                        pmurphy.pjmurphy.paddypower.com

                        Thanks for the comments John. Interesting info on SMPCs and the amount of memory that they take up. I don't think that this is going to be an issue for my application as it for company internal use only - i.e. won't be subject to 1000's of users. Nevertheless, it is an important point to keep in ones mind.


                        Here is an example of what I have got working (thanks to Brandon - big time :-)) Feedback welcome as this is just a first ditch effort.


                        # Start by setting flush mode to manual at the page level.
                        
                        <pages ...>
                            ...
                            <page view-id="/updateScreen.xhtml">
                             <begin-conversation flush-mode="MANUAL" />
                            </page>
                        </pages>
                        
                        <components ...>
                            ...
                            <persistence:managed-persistence-context name="entityManager" auto-create="true"
                             persistence-unit-jndi-name="java:/myEntityManagerFactory" />
                        
                        # Introduce a second PC for saving the entities. This will keep unwanted changes been
                        # committed to the db. Note that I am using the same persistence unit.
                        
                            <persistence:managed-persistence-context name="eventEntityManager" auto-create="true" scope="EVENT"
                             persistence-unit-jndi-name="java:/myEntityManagerFactory" />
                        
                        </components>
                        
                        # Search action uses the session level entity manager.
                        
                        @Stateful
                        @Name("searchAction")
                        @Scope(ScopeType.SESSION)
                        public class SearchActionImpl implements SearchAction {
                            @In
                            private EntityManager entityManager;
                        
                            @DataModel("searchAction_myEntities")
                            private List<MyEntity> myEntities;
                        
                            // lifecycle methods
                            @Create
                            public void create() {
                            }
                        
                            @Remove
                            public void remove() {
                            }
                        
                            public void searchEvents() {
                             Query q = entityManager.createQuery("SELECT e FROM MyEntity e");
                             myEntityies = (List<MyEntity>) q.getResultList();
                            }
                        }
                        
                        # Save action uses the event level entity manager. I've separated it into its own
                        # class as it is created at the event level and I don't want it been injected
                        # unnecessaryly for other business method calls.
                        
                        @Stateless
                        @Name("saveAction")
                        @Scope(ScopeType.EVENT)
                        public class SaveActionImpl implements SaveAction {
                        
                            @In
                            private EntityManager eventEntityManager;
                        
                            public void updateMyEnity(MyEntity myEntity) {
                                 eventEntityManager.merge(myEntity);
                        
                                 // Don't forget to merge any modified associated entities
                                 for (MyAssocEntity myAssocEntity : myEntity.getAssocEntities()) {
                                      eventEntityManager.merge(myAssocEntity);
                                 }
                        
                                 eventEntityManager.flush();
                                 eventEntityManager.close();
                            }
                        }
                        
                        # Finally, the save action is called from JSF passing the entity that we want to 
                        # save. No other entities will be saved even if they have been modified back on 
                        # the server.
                        
                        updateScreen.xhtml
                        
                        <ui:composition>
                            ...
                            <rich:dataGrid value="#{searchAction_myEntities}" var="myEntity" columns="2"
                             <a4j:commandButton id="save" value="Save"
                                 action="#{saveAction.updateMyEntity(myEntity)}" />
                            </rich:dataGrid>
                        
                        </ui:composition>
                        
                        
                        

                        • 39. Re: entityManager single entity update
                          brandonsimpson

                          Nice! Seems like we've finally arrived at an elegant solution and I think something similar will work for most of the other use cases people mentioned above.


                          I'd been trying to figure out how to deal with this in my own project for a few weeks, but until this thread didn't quite have it. I think I was reluctant to use anything other than the conversation scoped SMPC (and not even sure how to go about doing it). Stuart's suggestion and code about creating an event scoped PC was the piece I was missing.


                          Anyway, this is a really good example of the community working together to come up with a solution to a sticky problem.


                          And John, yep...that's pretty much one of the problems I was worried about. Starting a LRC on page A and doing some interactive editing, navigating to page B (which picks up the same SMPC used in page A). You could either see changes from page A that the user didn't commit to saving yet, or the user might even make unrelated changes on page B and commit them unknowningly and unintentionally also committing the changes from page A at the same time. So the key for that would be to ensure that each page has its own SMPC when you really don't want the non-committed changes on one page to appear on another...and to me that means ensuring that each page has its own LRC. I can explicitly end the LRC on page A when the user clicks a Save or Cancel button for example (which I consider the end of the use case), but I also have site-wide links on each page also and the user could navigate away from the page at any time without finishing the use case...that's my concern, because I think the default behavior would be to propogate that LRC through the site-wide links unless I explicitly disable that conversation propogation. I haven't played around with this much yet to see what the best way handle it is...it's just been in the back of my mind as a potential problem that needs to be dealt with.

                          • 40. Re: entityManager single entity update
                            brandonsimpson

                            John Jameson wrote on Jul 11, 2009 00:04:


                            This scenario of when a user makes some changes (say possible updates to HtmlInputText fields) to a form on page A and then navigates to page B and does the same in another form and saves (entityManager.flush()) is a potential total disaster.  When they go back to page A and see the changes persisted in page A, is that ok?  Most likely not because they didn't click the save button on page A!

                            So is this how it would work by default if you're using the same EntityManager instance, and thus the same SMPC, in the backing beans for both pages (say two SFSBs that are conversation-scoped)?

                            Well, I guess it depends on your conversational modeling.  If you use @Begin(join=true, flushMode=FlushModeType.MANUAL) for the starting action methods in each SFSB, then yes.  But what if you begin a new LRC for page B (or a nested conversation?) 

                            Actually, can you begin a new LRC programmatically if one is currently active?  I think you can't because that's what @Begin does and you get an exception if one is running so you must use @Begin(join=true).

                            And you might not necessarily want to undo the user's changes in page A form when they navigate to page B, right?

                            What about if page B is not part of the same use case as page A?  Like in the booking example, there are multiple related pages in a multi-step, sequential wizard.

                            I believe there are a lot of outlier scenarios for which JPA is not well-suited for...

                            I'd like to hear an official response to this thread from a Seam core dev.

                            We're past 30 reponses man...


                            I went back and read your post again. Yep. Those are the same thoughts/concerns I had. The problem in a nut shell is how to ensure that you keep the use cases isolated (with their own LRC/SMPC). Admittedly, I'm still in the early stages of my project and new to Seam, so I don't fully know all the ins and outs yet.

                            • 41. Re: entityManager single entity update
                              asookazian

                              The problem is that the booking example app does not illustrate a solution for this type of scenario you are describing (or I am describing).  And I am unaware of any other app in the distro's examples which illustrate this scenario/solution.


                              Basically, how do you handle the scenario when there is more than one SMPC and/or LRC in the same (or tangential or possibly even unrelated) use case?


                              The booking example has one LRC and that is it.  Very simple and very straightforward in terms of implementation/design/functional requirements.


                              GKing does not even use SMPC in that example (it's @PersistenceContext(type=EXTENDED)).


                              @Stateful
                              @Name("hotelBooking")
                              @Restrict("#{identity.loggedIn}")
                              public class HotelBookingAction implements HotelBooking
                              {
                                 
                                 @PersistenceContext(type=EXTENDED)
                                 private EntityManager em;
                              ...
                              }



                              @Stateful
                              @Name("hotelSearch")
                              @Scope(ScopeType.SESSION)
                              @Restrict("#{identity.loggedIn}")
                              public class HotelSearchingAction implements HotelSearching
                              {
                                  @PersistenceContext
                                  private EntityManager em;
                              ...
                              }



                              NOTE: from Seam 2.1.2.GA


                              The example above from Philip uses two EntityManager instances, each associated with their own SMPC:


                              @In
                              private EntityManager eventEntityManager;



                              @In
                              private EntityManager eventEntityManager;



                              They are using the same PU.  One is EVENT scoped, the other is CONVERSATION scoped.  One SFSB is SESSION scoped, the SLSB is EVENT scoped.


                              This is getting very confusing to me.


                              My solution uses two SFSBs, both CONVERSATION scoped, and two SMPCs, both CONVERSATION scoped.


                              Which solution is correct/better for which situation?


                              You'd think a Seam dev would verify some of these findings, this is an important topic...


                              1 2 3 Previous Next