A Home object provides persistence operations for a particular entity class. Suppose we have our trusty Person
class:
@Entity
public class Person {
@Id private Long id;
private String firstName;
private String lastName;
private Country nationality;
//getters and setters...
}
We can define a personHome
component either via configuration:
<framework:entity-home name="personHome" entity-class="eg.Person" />
Or via extension:
@Name("personHome")
public class PersonHome extends EntityHome<Person> {}
A Home object provides the following operations: persist()
, remove()
, update()
and getInstance()
. Before you can call the remove()
, or update()
operations, you must first set the identifier of the object you are interested in, using the setId()
method.
We can use a Home directly from a JSF page, for example:
<h1>Create Person</h1>
<h:form>
<div>First name: <h:inputText value="#{personHome.instance.firstName}"/></div>
<div>Last name: <h:inputText value="#{personHome.instance.lastName}"/></div>
<div>
<h:commandButton value="Create Person" action="#{personHome.persist}"/>
</div>
</h:form>
Usually, it is much nicer to be able to refer to the Person
merely as person
, so let's make that possible by adding a line to components.xml
:
<factory name="person"
value="#{personHome.instance}"/>
<framework:entity-home name="personHome"
entity-class="eg.Person" />
(If we are using configuration.) Or by adding a @Factory
method to PersonHome
:
@Name("personHome")
public class PersonHome extends EntityHome<Person> {
@Factory("person")
public Person initPerson() { return getInstance(); }
}
(If we are using extension.) This change simplifies our JSF page to the following:
<h1>Create Person</h1>
<h:form>
<div>First name: <h:inputText value="#{person.firstName}"/></div>
<div>Last name: <h:inputText value="#{person.lastName}"/></div>
<div>
<h:commandButton value="Create Person" action="#{personHome.persist}"/>
</div>
</h:form>
Well, that lets us create new Person
entries. Yes, that is all the code that is required! Now, if we want to be able to display, update and delete pre-existing Person
entries in the database, we need to be able to pass the entry identifier to the PersonHome
. Page parameters are a great way to do that:
<pages>
<page view-id="/editPerson.jsp">
<param name="personId" value="#{personHome.id}"/>
</page>
</pages>
Now we can add the extra operations to our JSF page:
<h1>
<h:outputText rendered="#{!personHome.managed}" value="Create Person"/>
<h:outputText rendered="#{personHome.managed}" value="Edit Person"/>
</h1>
<h:form>
<div>First name: <h:inputText value="#{person.firstName}"/></div>
<div>Last name: <h:inputText value="#{person.lastName}"/></div>
<div>
<h:commandButton value="Create Person" action="#{personHome.persist}"
rendered="#{!personHome.managed}"/>
<h:commandButton value="Update Person" action="#{personHome.update}"
rendered="#{personHome.managed}"/>
<h:commandButton value="Delete Person" action="#{personHome.remove}"
rendered="#{personHome.managed}"/>
</div>
</h:form>
When we link to the page with no request parameters, the page will be displayed as a "Create Person" page. When we provide a value for the personId
request parameter, it will be an "Edit Person" page.
Suppose we need to create Person
entries with their nationality initialized. We can do that easily, via configuration:
<factory name="person"
value="#{personHome.instance}"/>
<framework:entity-home name="personHome"
entity-class="eg.Person"
new-instance="#{newPerson}"/>
<component name="newPerson"
class="eg.Person">
<property name="nationality">#{country}</property>
</component>
Or by extension:
@Name("personHome")
public class PersonHome extends EntityHome<Person> {
@In Country country;
@Factory("person")
public Person initPerson() { return getInstance(); }
protected Person createInstance() {
return new Person(country);
}
}
Of course, the Country
could be an object managed by another Home object, for example, CountryHome
.
To add more sophisticated operations (association management, etc), we can just add methods to PersonHome
.
@Name("personHome")
public class PersonHome extends EntityHome<Person> {
@In Country country;
@Factory("person")
public Person initPerson() { return getInstance(); }
protected Person createInstance() {
return new Person(country);
}
public void migrate()
{
getInstance().setCountry(country);
update();
}
}
The Home object automatically displays faces messages when an operation is successful. To customize these messages we can, again, use configuration:
<factory name="person"
value="#{personHome.instance}"/>
<framework:entity-home name="personHome"
entity-class="eg.Person"
new-instance="#{newPerson}">
<framework:created-message>New person #{person.firstName} #{person.lastName} created</framework:created-message>
<framework:deleted-message>Person #{person.firstName} #{person.lastName} deleted</framework:deleted-message>
<framework:updated-message>Person #{person.firstName} #{person.lastName} updated</framework:updated-message>
</framework:entity-home>
<component name="newPerson"
class="eg.Person">
<property name="nationality">#{country}</property>
</component>
Or extension:
@Name("personHome")
public class PersonHome extends EntityHome<Person> {
@In Country country;
@Factory("person")
public Person initPerson() { return getInstance(); }
protected Person createInstance() {
return new Person(country);
}
protected String getCreatedMessage() { return "New person #{person.firstName}
#{person.lastName} created"; }
protected String getUpdatedMessage() { return "Person #{person.firstName}
#{person.lastName} updated"; }
protected String getDeletedMessage() { return "Person #{person.firstName}
#{person.lastName} deleted"; }
}
But the best way to specify the messages is to put them in a resource bundle known to Seam (the bundle named messages
, by default).
Person_created=New person #{person.firstName} #{person.lastName} created
Person_deleted=Person #{person.firstName} #{person.lastName} deleted
Person_updated=Person #{person.firstName} #{person.lastName} updated
This enables internationalization, and keeps your code and configuration clean of presentation concerns.
The final step is to add validation functionality to the page, using <s:validateAll>
and <s:decorate>
, but I'll leave that for you to figure out.