Developing a persistent application begins with defining a domain model. A domain model consists of a number of entities (classes) and relationships between them.
Figure 18.4, “A Domain Model” illustrates a simple domain
model as a UML class diagram. It has two entities:
Country
and Person
. They have a
"country has persons" relationship. This is a one-to-many
relationship with one country having many persons, each of which
belongs to just one country.
Realized in Java, the classes are as follows:
public class Country { private Long id; private String name; private Set<Person> persons; ... setters and getters ... } public class Person { private Long id; private String name; private Integer age; private Country country; ... setters and getters ... }
You should make the classes proper beans by defining a default constructor and
implementing the Serializable
interface. A
default constructor is required by the JPA entity manager for instantiating
entities. Having the classes serializable is not required but often useful for
other reasons.
After you have a basic domain model, you need to define the entity relationship metadata by annotating the classes.
The entity relationships are defined with metadata. The metadata can be defined in an XML metadata file or with Java annotations defined in the javax.persistence package. With Vaadin JPAContainer, you need to provide the metadata as annotations.
For example, if we look at the Person class in the JPAContainer AddressBook Demo, we define various database-related metadata for the member variables of a class:
@Entity public class Person { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; private Integer age; @ManyToOne private Country country;
The JPA implementation uses reflection to read the annotations and defines a database model automatically from the class definitions.
Let us look at some of the basic JPA metadata annotations. The annotations are defined in the javax.persistence package. Please refer to JPA reference documentation for the complete list of possible annotations.
Each class that is enabled as a persistent entity must have the
@Entity
annotation.
@Entity public class Country {
Entities must have an identifier that is used as the primary key for the table. It is used for various purposes in database queries, most commonly for joining tables.
@Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id;
The identifier is generated automatically in the database. The
strategy for generating the identifier is defined with the
@GeneratedValue
annotation. Any generation type
should work.
The @OneToOne
annotation describes a one-to-one
relationship where each entity of one type is associated with exactly
one entity of another type. For example, the postal address of a
person could be given as such.
@OneToOne private Address address;
When using the JPAContainer FieldFactory
to
automatically create fields for a form, the
@OneToOne
relationship generates a nested
Form
to edit the data. See
Section 18.8, “Automatic Form Generation” for more details.
Just as with the @OneToOne
annotation,
@Embedded
describes a one-to-one relationship, but
says that the referenced entity should be stored as columns in the
same table as the referencing entity.
@Embedded private Address address;
The referenced entity class must have @Embeddable
annotation.
The JPAContainer FieldFactory
generates a
nested Form
for @Embedded
,
just as with @OneToOne
.
The Country
entity in the domain model has a
one-to-many relationship with the
Person
entity ("country has persons"). This
relationship is represented with the @OneToMany
annotation. The mappedBy
parameter names the
corresponding back-reference in the Person
entity.
@OneToMany(mappedBy = "country") private Set<Person> persons;
When using the JPAContainer FieldFactory
to
automatically create fields for a form, the
@OneToMany
relationship generates a
MasterDetailEditor
for editing the items. See
Section 18.8, “Automatic Form Generation” for more details.
The @ElementCollection
annotation can be used for
one-to-many relationships to a collection of basic values such as
String
or Integer
, or to
entities annotated as @Embeddable
. The referenced
entities are stored in a separate table defined with a
@CollectionTable
annotation.
@ElementCollection @CollectionTable( name="OLDPEOPLE", joinColumns=@JoinColumn(name="COUNTRY_ID")) private Set<Person> persons;
JPAContainer FieldFactory
generates a
MasterDetailEditor
for the
@ElementCollection
relationship, just as with
@OneToMany
.
Many people can live in the same country. This would be represented
with the @ManyToOne
annotation in the
Person
class.
@ManyToOne private Country country;
JPAContainer FieldFactory
generates a
NativeSelect
for selecting an item from the
collection. You can do so yourself as well in a custom field factory.
Doing so you need to pay notice not to confuse the container between
the referenced entity and its ID, which could even result in insertion
of false entities in the database in some cases. You can translate
between an entity and the entity ID using the
SingleSelectTranslator
as follows:
@Override public Field createField(Item item, Object propertyId, Component uiContext) { if (propertyId.equals("station")) { ComboBox box = new ComboBox("Station"); // Translate between referenced entity and its ID box.setPropertyDataSource( new SingleSelectTranslator(box)); box.setContainerDataSource(stationContainer); ...
The JPAContainer FieldFactory
uses the
translator internally, so using it also avoids the problem.