4 Replies Latest reply on Oct 27, 2011 5:25 AM by Serkan Eskici

    How to instantiate an entity with certain properties pre-set before use

    Gökhan Ozar Newbie

      In a core JSF project, I used to initialize certain properties of an entity object with pre-defined values as early as its instantiation in a @PostConstruct method of a jsf managed bean (or even the managed bean's constructor). I can no longer preserve this functionality when porting the project to Seam with the managed bean gone.


      Here's my code:


      The entity class:


      @Entity
      @Name("task") // this line in Seam version
      public class Task implements Serializable {
      
          private Integer id;
          private String subject;
          private Date creationDate;
          private Date completionDate;
          private Category category;
          private User user;
      
          public Task() {
          }
      
          public Task(Date creationDate) {
              this.creationDate = creationDate;
              this.user = user;
          }
      
          // getters & setters, etc.
      }
      



      The JSF Managed Bean




      public class TaskBean extends BaseBean {
      
          // Super class provides the logged in user object and a default category object
      
          private Task task = new Task(java.util.Calendar.getInstance().getTime(), this.user);
      
          @PostConstruct
          public void initTaskBean() {
              if (certainConditionMet) 
                  task.setCategory(defaultCategory);
          }
      
          // Other managed bean business...
      }




      The JSF page




      <h:form>
      <h:panelGrid columns="2">
          Date: <h:outputText value="#{taskBean.task.creationDate}">
                  <f:convertDateTime pattern="dd/MM/yyyy" />
                 </h:outputText>
          User:  <h:outputText value="#{taskBean.task.user.name}" />
          Subject: <h:inputText value="#{taskBean.task.subject}" id="subjectField" />
          Category: <h:selectOneMenu value="#{taskBean.task.category}" id="catMenu">
                      <f:selectItems value="#{taskBean.categories}" />
                    </h:selectOneMenu>
          // remaining stuff
      </h:panelGrid>
      </h:form>




      The Stateless Session Bean in Seam version also assuming the responsibility of the jsf managed bean




      @Name("taskAction")
      @Stateless
      public class TaskAction implements TaskActionLocal {
      
          @PersistenceContext
          private EntityManager em;
      
          @In private FacesMessages facesMessages;
          @In User user;
          @In Category defaultCategory
          @Logger private Log log;
      
          @In
          // private Task task = new Task(java.util.Calendar.getInstance().getTime(), user)
          // the above didn't work so 
          private Task task; 
      
          @PostConstruct
          public void baslarken() {
              System.out.println("TaskAction PostConstructed");
              task = new task(java.util.Calendar.getInstance().getTime(), user);
              if (certainConditionMet) 
                      task.setCategory(defaultCategory);
      
              // the above won't work either. Date and User always blank on the ui form
          }
      
          // other business methods, etc
      }




      Seam version of the form:




      <h:form id="todo" styleClass="edit">
          <rich:panel>
              <f:facet name="header">Task To Do</f:facet>
      
              <s:decorate id="dateField" template="layout/edit.xhtml">
                  <ui:define name="label">Date:</ui:define>
                  <h:outputText value="#{task.creationDate}"/>
              </s:decorate>
      
              <s:decorate id="userField" template="layout/edit.xhtml">
                  <ui:define name="label">User:</ui:define>
                  <h:outputText value="#{task.user.name}"/>
              </s:decorate>
      
              <s:decorate id="subjectField" template="layout/edit.xhtml">
                  <ui:define name="label">Başlık</ui:define>
                  <h:inputText id="subject" required="true" value="#{task.subject}" >
                  <a4j:support event="onblur" reRender="subjectField" bypassupdates="true" ajaxSingle="true" />
                  </h:inputText>
              </s:decorate>
      
              <!-- other components, etc -->
          </rich:panel>
      </h:form>




      I need advice on how to make the task object to hold the date and logged in user data (and preferably a default category) pre-populated when instantiated.

        • 1. Re: How to instantiate an entity with certain properties pre-set before use
          omid pourhadi Apprentice

          define your task component instance in components.xml




          <component name="user" class="User" />
          <component class="Task" name="task">
          
          <property name="creationDate">#{currentDate}</property>
          <property name="user">#{user}</property>
          </component>



          instead of





           Task task = new Task(java.util.Calendar.getInstance().getTime(), this.user); 
          



          use in your TaskBean




          @In
          Task task;







          • 2. Re: How to instantiate an entity with certain properties pre-set before use
            Serkan Eskici Novice

            There are many ways for doing this. I also prefer Omid's solution.


            If you don't like XML and you want it done programmaticaly, then you could do it with for example with a @Factory.


            @In(required=false)
            Task task;
            
            
            @Factory("task")
            public Task newDefaultTask() {
               Task task = new Task();
               task.setProperty("default");
               //etc.
               return task;
            }
            
            public void save() {
               if(task.containsValueX())
                  task.setProperty2("A");
              
               persist(task);
            }
            



            <h:inputText value="#{task.property}" />
            ...
            <h:commandLink value="Save" action="#{taskBean.save}" />
            



            Make sure that task is (at least) in a LRC scope if you want to inject it from another bean.


            B.t.w. it's not a good practice to put @Name on an Entity bean.

            • 3. Re: How to instantiate an entity with certain properties pre-set before use
              Gökhan Ozar Newbie
              Omid and Serkan, thank you both so much for your respective advice.

              Serkan, as far as what I know, @Factory annotated methods are invoked only once, therefore for it is OK to use it as long as the bean scope remains stateless or conversation, right? (Assume a case where the user is allowed to create consecutive tasks without leaving the page via AJAX in which case I could worry about that)

              As for making entity beans Seam components via @Name, are you sure it's not OK? Examples shipped with Seam itself have such entities (@Entity + @Name). What's the inconvenience?

              Final question: What does LRC stand for?
              • 4. Re: How to instantiate an entity with certain properties pre-set before use
                Serkan Eskici Novice

                Gökhan Ozar wrote on Oct 26, 2011 09:05:


                Omid and Serkan, thank you both so much for your respective advice.

                Serkan, as far as what I know, @Factory annotated methods are invoked only once, therefore for it is OK to use it as long as the bean scope remains stateless or conversation, right? (Assume a case where the user is allowed to create consecutive tasks without leaving the page via AJAX in which case I could worry about that)


                Yes this is correct. @Factory just initialises a component and sets it in a (map of certain) scope. If you call the factory again and the scoped component still exists, then the @Factory method won't be called again, but just read from memory/scope.



                As for making entity beans Seam components via @Name, are you sure it's not OK? Examples shipped with Seam itself have such entities Entity + Name. What's the inconvenience?


                The examples are outdated. There is actually no need to set @Name on an entity if you follow the examples above.
                The most important reason for me to not use @Name on entities is because an entity can have different roles e.q. different names. For example you can have a 'task' and a 'defaultTask'.
                Seam has @Roles annotations for that, but it seems that there are performance problems with it, so I don't use it.


                And another important reason for not using @Name is that you keep your entities clean from Seam dependencies.



                Final question: What does LRC stand for?


                Long running conversation.