2 Replies Latest reply on Oct 12, 2009 9:47 PM by kipod

    Entity 'auto-persisted' on ajax reRender

      I have a simple situation where I have a page that allows editing of an entity called EventType.  Part of the EventType is an array of SubEventType.  My page allows the user to click an Add button to add new SubEventTypes.


      I would like to only persist these new SubEventTypes when the user clicks the Save button on the form.  However, what I am seeing is that the entities are being persisted prior to this.


      I have this problem in a variety of places and incarnations in my application, and believe I am just not understanding something correctly - some key insight I am missing that will be an Aha moment.
      I am hoping someone can take a few minutes and maybe help me fill in that blank.


      More specific details - the link to the page is an s:link which begins the conversation using propagation=join.  So a conversation is begun when the page is accessed.  When the user clicks the Add SubType button, an action is called which creates a new SubEventType entity and adds it to the list of SubEventTypes in the current EventType.  It does not call persist().  The button has a reRender attribute to reRender the table that lists the SubEventTypes for the current EventType.


      When I click the button, the new SubEventType is created, the database is somehow updated, and the table is refreshed to show the newly added SubEventType.  I don't understand why the database was updated.


      Here is my code:


      The relevant code from the page:


                     <s:decorate id="typeSubEventsDecoration" 
                          template="../layout/edit.xhtml">
                          <ui:define name="label">#{messages['com.leagueunited.admin.subeventtypes']}</ui:define>
                          <rich:panel id="subeventspanel" 
                               style="width:500px;border:0px;background:transparent;">
                               <h:outputText
                                    value="#{messages['com.leagueunited.admin.eventtype.nosubevents']}"
                                    rendered="#{empty eventType.subEventTypes}" /> 
           
                               <rich:dataTable id="subEventTypeTable" var="_subEventType"
                                    value="#{eventType.subEventTypes}"
                                    rendered="#{not empty eventType.subEventTypes}"
                                    onRowMouseOver="this.style.backgroundColor='#F1F1F1'"
                                    onRowMouseOut="this.style.backgroundColor='#{a4jSkin.tableBackgroundColor}'"
                                    rowClasses="rvgRowOne,rvgRowTwo"
                                    cellpadding="0" cellspacing="0">
                          
                                    <f:facet name="header">
                                         <rich:columnGroup>
                                              <rich:column colspan="2">
                                                   <h:outputText
                                                        value="#{messages['com.leagueunited.admin.subeventtypes']}" />
                                              </rich:column>
                                         </rich:columnGroup>
                                    </f:facet>
                                    
                                    <rich:column>
                                         <f:facet name="header">
                                              #{messages['com.leagueunited.admin.subeventtype']}
                                      </f:facet>
                                         <h:inputText value="#{_subEventType.subEventTypeName}"
                                              size="100" maxlength="100"
                                              required="true"/>
                                    </rich:column>
                                              
                                    <rich:column style="text-align:center">
                                         <f:facet name="header">
                                              #{messages['com.leagueunited.admin.eventtype.trackstats']}
                                         </f:facet>
                                         <h:selectBooleanCheckbox value="#{_subEventType.subEventTypeTrackStats}"/>
                                    </rich:column>
                                    
                               </rich:dataTable>
                               
                               <s:div styleClass="actionButtons">
                                    <a4j:commandButton id="addsub"
                                         action="#{eventTypeAction.newSubEventType()}"
                                         value="#{messages['com.leagueunited.admin.eventtype.addsubeventtype']}"
                                         reRender="subEventTypeTable"/>
                               </s:div>     
                               
                          </rich:panel>
                     </s:decorate>



      And here is the page.xml for the page:


      <page xmlns="http://jboss.com/products/seam/pages"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.0.xsd">
      
         <action execute="#{eventTypeHome.wire}"/>
         <param name="eventTypeId" value="#{eventTypeHome.eventTypeId}"/>
         <param name="from"/>
         
         <navigation from-action="#{eventTypeAction.persist}">
                   <rule if="#{not empty from}" if-outcome="persisted">
                    <begin-conversation join="true"/>
                   <redirect view-id="/manager/event.xhtml">
                        <param name="from" value="#{from}"/>
                   </redirect>
               </rule>
               
                   <rule if="#{empty from}" if-outcome="persisted">
                       <end-conversation before-redirect="true"/>
                    <redirect view-id="/admin/eventtypes.xhtml"/>
               </rule>
         </navigation>
      
         <navigation from-action="#{eventTypeAction.update}">
                   <rule if="#{not empty from}" if-outcome="updated">
                    <begin-conversation join="true"/>
                   <redirect view-id="/manager/event.xhtml">
                        <param name="from" value="#{from}"/>
                   </redirect>
               </rule>
               
                   <rule if="#{empty from}" if-outcome="updated">
                       <end-conversation before-redirect="true"/>
                    <redirect view-id="/admin/eventtypes.xhtml"/>
               </rule>
         </navigation>
      
         <navigation from-action="event_cancel">
                   <begin-conversation join="true"/>
               <redirect view-id="/manager/event.xhtml">
                    <param name="from" value="#{from}"/>
               </redirect>
         </navigation>
      
      </page>



      Now the eventTypeAction code:


      @Name("eventTypeAction")
      @Scope(ScopeType.PAGE)
      public class EventTypeAction  {
           
          @Logger Log log;
           
           @In     @Out
           protected EventTypeHome eventTypeHome;
      
           @In(required=true)
           @Out
           protected SportHome sportHome;
      
           @In private Map<String, String> messages;
      
           public String persist() {
                if (eventTypeHome.persist() == "persisted") {
                     sportHome.getDefinedInstance().getEventTypes().add(eventTypeHome.getDefinedInstance());
                     return "persisted";
                }
                return "failed";
           }
      
           public String update() {
                return eventTypeHome.update();
           }
           
            public void newSubEventType() {
                 EventType eventType = eventTypeHome.getInstance();
                 if (eventType == null)
                      return;
                 
                 // Create a new sub event type with default name and same track stats as parent type
                 SubEventType subEventType = new SubEventType();
                 subEventType.setSubEventTypeName(messages.get("com.leagueunited.admin.eventtype.addsubeventtype"));
                 subEventType.setSubEventTypeTrackStats(eventType.getEventTypeTrackStats());
                 subEventType.setParentEventType(eventType);
                 
                 // Add to parent
                 eventType.getSubEventTypes().add(subEventType);
            }
      
      }
      



      Here is the EventTypeHome:


      @Name("eventTypeHome")
      public class EventTypeHome extends EntityHome<EventType> {
           private static final long serialVersionUID = 443828533084198271L;
      
           @In(create = true)
           private SportHome sportHome;
      
           @Factory(value = "eventType", scope = ScopeType.PAGE)
          public EventType initEventType() { 
               return super.getInstance();
          }
      
           public void setEventTypeId(Long id) {
                setId(id);
           }
      
           public Long getEventTypeId() {
                return (Long) getId();
           }
                
           @Override
           protected EventType createInstance() {
                EventType event = new EventType();
                return event;
           }
      
           public void wire() {
                Sport sport = sportHome.getDefinedInstance();
                if (sport != null) {
                     getInstance().setSport(sport);
                }
           }
      
           public boolean isWired() {
                if (getInstance() == null)
                     return false;
      
                if (getInstance().getSport() == null)
                     return false;
      
                return true;
           }
      
           public EventType getDefinedInstance() {
                return isIdDefined() ? getInstance() : null;
           }
      
      }



      And EventType entity:


      @Entity
      @Table(name = "eventtype", catalog = "ludb")
      public class EventType implements java.io.Serializable, Comparable<EventType> {
           private static final long serialVersionUID = 420261612760471976L;
      
           private Long eventTypeId;
           private Sport sport;
           private String eventTypeName;
           private Boolean eventTypeTrackStats;
           private List<SubEventType> subEventTypes = new ArrayList<SubEventType>(0);
           public EventType() {
           }
      
           public EventType(Sport sport, String name, Boolean bTrackStats) {
                this.sport = sport;
                this.eventTypeName = name;
                this.eventTypeTrackStats = bTrackStats;
           }
      
           @Id
           @GeneratedValue(strategy = IDENTITY)
           @Column(name = "EVENTTYPE_ID", unique = true, nullable = false)
           public Long getEventTypeId() {
                return this.eventTypeId;
           }
      
           public void setEventTypeId(Long eventTypeId) {
                this.eventTypeId = eventTypeId;
           }
      
           @ManyToOne(fetch = FetchType.LAZY)
           @JoinColumn(name = "SPORT_ID", nullable = false)
           @NotNull
           public Sport getSport() {
                return this.sport;
           }
      
           public void setSport(Sport sport) {
                this.sport = sport;
           }
      
           @Column(name = "EVENTTYPE_NAME", nullable = false, length = 100)
           @NotNull
           @Length(max = 100)
           public String getEventTypeName() {
                return this.eventTypeName;
           }
      
           public void setEventTypeName(String eventTypeName) {
                this.eventTypeName = eventTypeName;
           }
      
          @Column(name="EVENTTYPE_TRACKSTATS", nullable=false)
          @NotNull
          public Boolean getEventTypeTrackStats() {
              return this.eventTypeTrackStats;
          }
      
          public void setEventTypeTrackStats(Boolean bTrackStats) {
              this.eventTypeTrackStats = bTrackStats;
          }
      
           @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "parentEventType")
           public List<SubEventType> getSubEventTypes() {
                return this.subEventTypes;
           }
      
           public void setSubEventTypes(List<SubEventType> subEventTypes) {
                this.subEventTypes = subEventTypes;
           }
      
           public int compareTo(EventType anotherType) throws ClassCastException {
                if (!(anotherType instanceof EventType))
                     throw new ClassCastException("EventType object expected.");
      
                if (anotherType.eventTypeName == null)
                     return 1;
                
                // Sort alphabetically based on type name
                return this.eventTypeName.compareTo(anotherType.eventTypeName);    
           }
      
      }



      Here is the SubEventType entity:


      @Entity
      @Table(name = "subeventtype", catalog = "ludb")
      public class SubEventType implements java.io.Serializable, Comparable<SubEventType> {
           private static final long serialVersionUID = -7314484403043744447L;
      
           private Long subEventTypeId;
           private EventType parentEventType;
           private String subEventTypeName;
           private Boolean subEventTypeTrackStats;
           
           public SubEventType() {
           }
      
           @Id
           @GeneratedValue(strategy = IDENTITY)
           @Column(name = "SUBEVENTTYPE_ID", unique = true, nullable = false)
           public Long getSubEventTypeId() {
                return this.subEventTypeId;
           }
      
           public void setSubEventTypeId(Long subEventTypeId) {
                this.subEventTypeId = subEventTypeId;
           }
      
           @ManyToOne(fetch = FetchType.LAZY)
           @JoinColumn(name = "EVENTTYPE_ID", nullable = false)
           @NotNull
           public EventType getParentEventType() {
                return this.parentEventType;
           }
      
           public void setParentEventType(EventType eventType) {
                this.parentEventType = eventType;
           }
      
           @Column(name = "SUBEVENTTYPE_NAME", nullable = false, length = 100)
           @NotNull
           @Length(max = 100)
           public String getSubEventTypeName() {
                return this.subEventTypeName;
           }
      
           public void setSubEventTypeName(String subEventTypeName) {
                this.subEventTypeName = subEventTypeName;
           }
      
          @Column(name="SUBEVENTTYPE_TRACKSTATS", nullable=false)
          @NotNull
          public Boolean getSubEventTypeTrackStats() {
              return this.subEventTypeTrackStats;
          }
      
          public void setSubEventTypeTrackStats(Boolean bTrackStats) {
              this.subEventTypeTrackStats = bTrackStats;
          }
      
           public int compareTo(SubEventType anotherType) throws ClassCastException {
                if (!(anotherType instanceof SubEventType))
                     throw new ClassCastException("SubEventType object expected.");
      
                if (anotherType.subEventTypeName == null)
                     return 1;
                
                // Sort alphabetically based on type name
                return this.subEventTypeName.compareTo(anotherType.subEventTypeName);    
           }
      
      }