7 Replies Latest reply on Apr 25, 2009 6:59 PM by clerum

    Refreshing an Entity in a Nested Conversation

    clerum

      OK-


      I have nested conversation going where I show a list or organizations as the root conversation, then click on a organization and display it's details. From there you start a 2nd nested conversation which add's the site. Once I finish adding the site I use a conversation.endAndRedirect(); to pop the outside conversation and return to the view for the organization.


      This page which shows the organization has a datatable listing out all the sites.


      <rich:simpleTogglePanel switchType="client" label="Sites" opened="#{organization.sites.size != 0}">
                               <h:outputText value="No Sites exist" rendered="#{organization.sites.size==0}"/>                                          
                            <rich:dataTable columnClasses="column-center,column-left,column-left,column-left,column-center"  width="75%" id="siteList" var="site" value="#{organization.sites}" rendered="#{organization.sites.size != 0}">
                                 <rich:column headerClass="column-center"> 
                                    <f:facet name="header">Edit Site</f:facet>
                                    <s:link value="Edit Site" action="#{siteUtil.findSite()}">
                                         <f:param name="site_id" value="#{site.id}" />                                   
                                    </s:link>
                                </rich:column>
                                      <rich:column headerClass="column-left"> 
                                    <f:facet name="header"><h:outputText styleClass="column-left" value="Name"/></f:facet> 
                                    <h:outputText value="#{site.name}" />
                                </rich:column>
                                .. 
                                      ....                                                 
                            </rich:dataTable>
      



      This datatable however isn't getting refreshed with the new site that was added. What approach should I be using to accomplish this? Would this be handled by an EXTENDED PersistenceContext? (I'm only using @In EntityManager em; as I don't really understand the different between it and @PersistenceContext)


      How can I refresh the organization entity so that the site list is updated?


      import java.io.Serializable;
      import java.util.Date;
      import java.util.List;
      import javax.persistence.Entity;
      import javax.persistence.GeneratedValue;
      import javax.persistence.Id;
      import javax.persistence.OneToMany;
      import javax.persistence.OrderBy;
      import javax.persistence.Table;
      import javax.persistence.Temporal;
      import javax.persistence.TemporalType;
      import javax.persistence.Version;
      import org.hibernate.validator.Length;
      import org.hibernate.validator.NotNull;
      import org.jboss.seam.annotations.Name;
      
      @Entity
      @Name("organization")
      @Table(name="organization")
      public class Organization implements Serializable
      {
           private static final long serialVersionUID = 1L;
           private long id;
           private int version;
           private String name;
           private String alias;
           private String msa_version;
           private int payment_terms;
           private boolean active;
           private String sfcode;
           private Date date_created;
           private Date date_inactive;     
           private List<User> users;
           private List<Site> sites;
           
           public Organization() {}
           
           @GeneratedValue
           @NotNull
           @Id
           public long getId() {
                return id;
           }
           public void setId(long id) {
                this.id = id;
           }
           
           @Version
           @NotNull
           public int getVersion() {
                return version;
           }
           public void setVersion(int version) {
                this.version = version;
           }
           
           @Length(min=1,max=75)
           @NotNull
           public String getName() {
                return name;
           }
           public void setName(String name) {
                this.name = name;
           }
           
           @Length(min=1,max=75)
           public String getAlias() {
                return alias;
           }
      
           public void setAlias(String alias) {
                this.alias = alias;
           }
      
           public String getMsa_version() {
                return msa_version;
           }
      
           public void setMsa_version(String msa_version) {
                this.msa_version = msa_version;
           }
      
           public int getPayment_terms() {
                return payment_terms;
           }
      
           public void setPayment_terms(int payment_terms) {
                this.payment_terms = payment_terms;
           }
      
           @NotNull
           public boolean isActive() {
                return active;
           }
      
           public void setActive(boolean active) {
                this.active = active;
           }     
           
           @OneToMany(mappedBy="organization")
           @OrderBy("lastname")
           public List<User> getUsers() {
                return users;
           }
           public void setUsers(List<User> users) {
                this.users = users;
           }
           
           @OneToMany(mappedBy="organization")
           @OrderBy("name")
           public List<Site> getSites() {
                return sites;
           }
           public void setSites(List<Site> sites) {
                this.sites = sites;
           }
      
           @Length(min=10,max=10)
           @NotNull
           public String getSfcode() {
                return sfcode;
           }
           public void setSfcode(String sfcode) {
                this.sfcode = sfcode;
           }
      
           @Temporal(TemporalType.DATE)
           public Date getDate_created() {
                return date_created;
           }
      
           public void setDate_created(Date date_created) {
                this.date_created = date_created;
           }
      
           @Temporal(TemporalType.DATE)
           public Date getDate_inactive() {
                return date_inactive;
           }
      
           public void setDate_inactive(Date date_inactive) {
                this.date_inactive = date_inactive;
           }
           
      }
      

        • 1. Re: Refreshing an Entity in a Nested Conversation
          gonorrhea

          try something like this:


          <a4j:outputPanel id="outputPanel1" ajaxRendered="true">
                                  <h:form rendered="#{listValueEntityList.getRowCount() > 0}">
                                          <rich:dataTable id="dataTable1" 
                                                                          value="#{listValueEntityList}" 
                                                                          var="listVal">
          ...
          </a4j:outputPanl>



          the ajaxRendered="true" automatically reRenders the contents of the outputPanel.


          The differences b/n @PersistenceContext, @PersistenceContext(type = PersistenceContextType.EXTENDED), and @In are explained in detail in the Seam ref docs, Bauer/King book and both Seam books.

          • 2. Re: Refreshing an Entity in a Nested Conversation
            clerum

            Thanks, but I'm not trying to render the datatable, but refresh the list backing the datamodel...the entity.

            • 3. Re: Refreshing an Entity in a Nested Conversation
              clerum

              I'm not looking for help on the in annotations but the difference between injecting an EntityManager using @In and Injecting one via @PersistanceContext


              Maybe I'm just missing it in the docs but I didn't see it clearly outlined.

              • 4. Re: Refreshing an Entity in a Nested Conversation
                gonorrhea

                if you're using @DataModel to outject a ListDataModel instance, then all you need to do if re-assign to the List instance that is being outjected by @DataModel.


                @DataModel
                List<String> sites;



                public void submit() {
                   sites = entityManager.createQuery("from foo").getResultList();
                }



                so when submit() is fired by JSF, site instance is updated with new value and then outjected.


                • 5. Re: Refreshing an Entity in a Nested Conversation
                  gonorrhea

                  There's a very big difference that is crucial to understand.  Read these sections in the books referenced below several times to understand how/why as well as adv/disadv.


                  @In EntityManager em; is a Seam injection for SMPC.


                  @PersistenceContext EntityManager em; is a JEE-specific annotation to inject either a transaction-scoped EntityManager or extended-scope EntityManager.


                  Seam in Action:



                  An extended persistence context, on the other hand, keeps the EntityManager open for the lifetime of the SFSB, delaying the call to the persistence manager’s close() method until the SFSB is destroyed.



                  Why would you want to use an extended persistence context? Simple: to prevent detached entities.

                  If you want to use application transactions (e.g. atomic tx commit for a wizard with multiple JSFs in a LRC), then you must use @In (SMPC) with Hibernate MANUAL flushmode.  @In (SMPC) also helps you avoid the dread LazyInitializationException that requires workaround solutions like OSIV with Spring/Hibernate, for example.


                  Figure 9.1 in Seam in Action would really help you understand:





                  As of Seam 2.1, you can set the default flush mode globally in the component descriptor:
                  <core:manager default-flush-mode="MANUAL" .../>


                  The MANUAL flush mode instructs the persistence context not to take action until it hears your command, ensuring that the conversation isn’t broken by a premature flush.

                  Be careful with that last statement b/c MANUAL flush works best with sequences (Oracle) and not IDENTITY generators which are used often in MSSQL.  See my other post if you have a question about this.


                  For further details, check out Bauer/King book, Mike Keith JPA book, and Seam in Action or Yuan et al.

                  • 6. Re: Refreshing an Entity in a Nested Conversation
                    clerum

                    Right or wrong I solved this by tacking in a em.refresh on the organization object. I was thinking that since the site object is related to the organization entity an insert of a new site which is related to the organization would update the list of sites on the organization.


                    public void addSite()
                         {
                              site.setDate_created(new Date());
                              site.setActive(true);
                              em.persist(site);
                              em.refresh(organization);
                              conversation.endAndRedirect();
                              //activeuser.addLog("core", "New Site Added: " + site.getName(), site);          
                         }
                    

                    • 7. Re: Refreshing an Entity in a Nested Conversation
                      clerum

                      Not using an @Datamodel. I'm accessing a list of entity (site) which are attached to entity (organization) via a


                      @OneToMany(mappedBy="organization")
                           @OrderBy("name")
                           public List<Site> getSites() {
                                return sites;
                           }
                           public void setSites(List<Site> sites) {
                                this.sites = sites;
                           }



                      Then accessing them from the page as so


                      rich:simpleTogglePanel switchType="client" label="Sites" opened="#{organization.sites.size != 0}">
                                               <h:outputText value="No Sites exist" rendered="#{organization.sites.size==0}"/>                                          
                                            <rich:dataTable columnClasses="column-center,column-left,column-left,column-left,column-center"  width="75%" id="siteList" var="site" value="#{organization.sites}" rendered="#{organization.sites.size != 0}">
                                                 <rich:column headerClass="column-center"> 
                                                    <f:facet name="header">Edit Site</f:facet>
                                                    <s:link value="Edit Site" action="#{siteUtil.findSite()}">
                                                         <f:param name="site_id" value="#{site.id}" />                                   
                                                    </s:link>
                                                </rich:column>
                                                      <rich:column headerClass="column-left"> 
                                                    <f:facet name="header"><h:outputText styleClass="column-left" value="Name"/></f:facet> 
                                                    <h:outputText value="#{site.name}" />
                                                </rich:column>
                                                .. 
                                                      ....                                                 
                                            </rich:dataTable>