1 2 3 Previous Next 41 Replies Latest reply on Jul 13, 2009 5:47 PM by asookazian

    entityManager single entity update

      Hello,


      I'm new in SEAM (2 month of using this framework).


      I have a page that upload files and shows its on a dataGrid where I want to update the each individual file data.


      <rich:fileUpload id="file"
              fileUploadListener="#{fileUploadBean.persistUploadedFile}"
              immediateUpload="true" ajaxSingle="true"
              maxFilesQuantity="#{fileUploadBean.fileLeftToUpload}">
              <a:support event="onuploadcomplete" reRender="uploadedFiles" />
      </rich:fileUpload>
      
      <rich:panel id="uploadedFiles">
              <f:facet name="header">
                      <h:outputText value="Uploaded Files" />
              </f:facet>
              <h:outputText value="No files currently uploaded"
                      rendered="#{empty CHIARAfileUploadBean.uploadedFiles}" />
              <rich:dataGrid columns="1" value="#{fileUploadBean.uploadedFiles}"
                      var="_file" rowKeyVar="fileId">
                      <h:outputText value="File Name:" />
                      <rich:inplaceInput value="#{_file.name}" />
                      <h:outputText value="Enabled:"/>
                      <h:selectBooleanCheckbox value="#{_file.enabledToSend}" />
                      <h:commandButton action="#{fileUploadBean.updateFile(_file)}" value="Save" />
              </rich:dataGrid>
      </rich:panel>
      



      The relevant methods on my fileUploadBean are:



          public void persistUploadedFile(UploadEvent event) throws Exception{
              UploadItem item = event.getUploadItem();
              
              File file = new File();
              
              String[] fullFilename  = item.getFileName().split("\\.");
      
              file.setName(fullFilename[0]);
              file.setExtension(fullFilename[1]);
              file.setMimeType(item.getContentType());
              
              if (item.isTempFile()) {
                      file.setContent(FileUtils.readFileToByteArray(item.getFile()));
              }
              else{
                      file.setContent(item.getData());
              }
      
              file.setUploadDate(new Date(System.currentTimeMillis()));
              entityManager.persist(file);
              
              uploadedFiles.add(file);
          }
              
          public void updateFile(File file){
              entityManager.persist(file); 
          }
      



      The problem is that the file is not updated when I use:



      entityManager.persist(file);




      If I use:



      entityManager.flush()



      it works, but the problem with this is that will flush ALL my modified entities and I want to update only the selected file.


      Could anyone help me whit this?


      Thanks


        • 1. Re: entityManager single entity update
          jeanluc

          This is a matter of what persistence context management you use (Seam or container) and how you scope it. You need to have the upload in a different conversation so the PC is different.

          • 2. Re: entityManager single entity update

            Hello Jean, thanks for the answer


            I'm using Hibernate with Seam persistence context management and Seam transaction management:



               
            <persistence:entity-manager-factory name="entityManagerFactory"
                                  persistence-unit-name="myDataSource"/>
            
            <persistence:managed-persistence-context name="entityManager" auto-create="true"
                                      entity-manager-factory="#{entityManagerFactory}"/>
            
            <transaction:entity-transaction
                      entity-manager="#{entityManager}" />
            



            fileUploadBean is in conversation Scope.


            If I understand correctly, I need to end the current conversation and re-load the files in another conversation to do the update. Is this correct?


            If I do that, when the conversation is closed, I will lose the current uploaded files. I want upload a list of files and immediately update them, but I don't want upload them, search them in another converzation/page and update them.


            How can I do a persist an late an update in the same conversation?


            Regards,
            Chiara

            • 3. Re: entityManager single entity update
              ralf

              Hello Juan,



              maybe you should experiment with
              entitymanager.merge( item )


              hope it helps.


              Can you post more code of your Bean?

              • 4. Re: entityManager single entity update

                Hello Ralf,


                I tried with



                entitymanager.merge( item )



                but it didn't work. The documentation of merge says:




                merge(T entity)
                Merge the state of the given entity into the current persistence context.

                So, I realize that it isn't what I was looking for since the updated item is in my conversation context.



                Can you post more code of your Bean?

                I don't have the code here, I will upload it later, but i don't think it is necessary.


                I just need a way to do this:



                Item item = new Item();
                item.setName("itemName");
                entityManager.persist(item);
                ...
                item.setName("updatedItemName");
                entityManager.update(item);
                



                Regards,
                Chiara


                • 5. Re: entityManager single entity update
                  cash1981

                  entityManager.flush() is what you want since it will update for you. If other entities are changed and you dont want that, you can refresh() those entites. But it sounds like u are doing something wrong if more that the Item
                  Object is updated.

                  • 6. Re: entityManager single entity update
                    swd847

                    Hibernate/JPA will always flush all entities in the persistence context, that is just the way they were designed. I think for your use case you are better off using a transaction scoped entityManager. If you are in an ejb3 environment you can inject one using @PersisenceContext, otherwise you are going to need to use the EntityManagerFactory to create one directly.


                    This means that you will be working with detached entities, so every time you want to update them you will have to merge() them into the entityManager and then call flush().

                    • 7. Re: entityManager single entity update

                      Stuart,
                      I'm not in an ejb3 environment (I'm using Tomcat), so I haven't a persistenceContext available to inject.


                      If I understand well, it is not posible to do what I want with a pure Hibernate/JPA environment, and I need to read about EntityManagerFactory.


                      Thanks.
                      Chiara

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

                        I have a similar problem. I am wondering did you ever work out a solution to this? My scenario is as follows:


                        1) Using an extended persistence context I retrieve a bunch of entities.
                        2) Using a ui:repeat (surrounded by a single form) each entity is displayed along with a submit button.
                        3) Need somehow to only commit the data that is related to its submit button.


                        Cheers,


                        Philip

                        • 9. Re: entityManager single entity update
                          brandonsimpson

                          I've come across this as well, but unfortunately have not seen an elegant way to deal with it. Hopefully, one of the gurus can enlighten us on a better method.


                          From what I've read, one of the great things about Seam's managed persistence is that you can keep your entities managed throughout a use case, thus not needing to use merge or worry about detached entities, exceptions, etc. that normally plague developers. There are many examples which show using these managed entities directly in the view and flushing the persistence context manually to sync the state to the DB.


                          This looks great on paper, but in practice, it has its problems as I think we are all seeing. It's all well and good if you have a standard straight forward CRUD I guess. But if you have something more complex (aka real-world, supporting user interaction), it seems to me to break down. If a user can edit several entities in the persistence context throughout a use case but you only want part of them saved at some point, you are sort of out of luck.


                          My thoughts so far on how to handle this is that you really don't want to be editing the managed entities directly, but rather work on a transient copy with values coming from your managed entity if it is to be edited. Then you want to merge or re-attach the transient copy to the persistence context when you really want it to be synched to the DB. Otherwise you are either forced to synch entities you didn't want synched or forced to lose some of your temporary edits that the user made in the view but wasn't yet ready to synch.


                          It would be nice if there was an easier way to indicate exactly what you wanted synched (beyond just the automatic dirty checking) or easy way to copy managed entity data to a a transient copy for editing. I'm guessing merge is basically the reverse...copying transient/detached back into a managed entity.


                          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!

                          • 10. Re: entityManager single entity update

                            Philip,


                            I don't get a solution yet. I just change my usecase and now I flush all single entities updates together or none of it (a global update/cancel button). This give me some time while I still learn more about seam entity management.

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

                              Thanks Brandon and Juan. Originally I thought that I could use multiple forms to get the control that I needed. For each element in the repeating data set I would create a separate form - then on submit it would just submit the individual element. However, it appears that multiple forms are not supported within repeating components. Then I thought that I could use Ajax regions. The complete form is submitted, however, it should only process updates for the entity data that lives within the region (assuming button lives within same region). However, part of my application is quite complex where it uses multiple tabs where the first tab acts as a master tab. If data on the master tab is changed it needs to update the other tabs. The only way to do this is to detect the change on the master and then send a request back to the server to update the other entity data that is backing the other tabs. Finally, the other tabs are rerendered and they do indeed now reflect the master tab. This works fine if you have one tab panel, however, I have multiple tab panels, so if the user changes one master tab and then changes another master tab, but only wants to submit the latter, the first one also gets submitted as the data has already been changed back in the entity. What I need is some way to isolate the changes.


                              Brandon, I need to think a little bit more about your suggestion - I'm too tired at the moment as I've spent the first two days this week trying to track down why I was seeing this (undesirable) behaviour - and the rest of today trying to come up with some solution. I was thinking that the solution lay in using Seam conversations, however, as far as I can tell you cannot create multiple conversations on the same page. Getting a bit worried that is going to be my Waterloo ;-)


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

                                I forgot to mention that I also tried to use ajaxKeys to do this but with no success. It is quite tricky to implement this (based on an example implementation by Max Katz - author of Practical RichFaces) as you first need to support indexOf to do the look up to find the current entity. Hibernate generates Sets for entities that refer to other entities, therefore, you need to override hashCode and equals, which we couldn't do as it was a principle in work that we would never add any customizations to the the generated entities. However, this might be possible now since the latest release of seam-gen supports custom code generation (basically you can specify some code for a particular entity and it appends it to the generated entity).


                                Brandon, you might be on to something with the transient copy, however, this could be a nightmare to keep track of changes, especially, in the case where different entities can be updated via the same page, and this data is repeated many times.


                                Another point - for validation, generally what is recommended is to use bypassUpdates=true to stop the model been updated. However, I can't use this as I need to update some entities so that they are rerendered correctly on another part of the screen. (however, I don't want these changes commited unless the user explicitly presses the submit button associated with them). The bypassUpdates stops the Application Invocation phase from being invoked so my action method would never be called.


                                It would be good to hear opinions on good design patterns around this area (or any solution for that matter).

                                • 13. Re: entityManager single entity update
                                  swd847

                                  The trick for this type of behavior is to use an event scoped entity manager and merge the entities before updates each time. In a non-ejb environment there are a few steps to follow.


                                  First you need to get hold of an EntityManagerFactory, as you are using the seam managed entity manager factory the process is:


                                  @In EntityManagerFactory entityManagerFactory;
                                  



                                  now every time you need an entityManager you need to call


                                  entityManagerFactory.createEntityManager(); 
                                  



                                  (remember to call entityManager.close() when you are done with it). As your entity manager will now not last more that one method call you are dealing with detached entities, so they will not be flushed to the database automatically. To update a specific entity you need to do this:


                                  entityManager.merge(myEntity);
                                  entityManager.flush();
                                  



                                  This is the correct way to deal with entities that need to be updated piecemeal, however it puts the onus on the programmer to make sure that every entity that needs to be updated is merged into the persistence context. Also you probably need to call entityManager.joinTransaction() after the entityManager is created.


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

                                    Thanks Stuart. I'll give it a whirl tomorrow and report back soon. Philip

                                    1 2 3 Previous Next