1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
|
---
title: Defining a Domain Model
order: 3
layout: page
---
[[jpacontainer.domain-model]]
= Defining a Domain Model
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.jpacontainer.domain-model>> illustrates a simple domain model as a UML
class diagram. It has two entities: [classname]#Country# and
[classname]#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.
[[figure.jpacontainer.domain-model]]
.A Domain Model
image::img/domain-model-hi.png[]
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 [interfacename]#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.
[[jpacontainer.domain-model.annotation]]
== Persistence Metadata
The entity relationships are defined with metadata. The metadata can be defined
in an XML metadata file or with Java annotations defined in the
[package]#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 [package]#javax.persistence# package. Please refer to JPA
reference documentation for the complete list of possible annotations.
[[jpacontainer.domain-model.metadata.entity]]
=== Annotation: [literal]#++@Entity++#
Each class that is enabled as a persistent entity must have the
[literal]#++@Entity++# annotation.
----
@Entity
public class Country {
----
[[jpacontainer.domain-model.annotation.id]]
=== Annotation: [literal]#++@Id++#
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 [literal]#++@GeneratedValue++#
annotation. Any generation type should work.
[[jpacontainer.domain-model.annotation.onetoone]]
=== Annotation: [literal]#++@OneToOne++#
The [literal]#++@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 [classname]#FieldFactory# to automatically create
fields for a form, the [literal]#++@OneToOne++# relationship generates a nested
[classname]#Form# to edit the data. See
<<dummy/../../../framework/jpacontainer/jpacontainer-fieldfactory#jpacontainer.fieldfactory,"Automatic
Form Generation">> for more details.
[[jpacontainer.domain-model.annotation.embedded]]
=== Annotation: [literal]#++@Embedded++#
Just as with the [literal]#++@OneToOne++# annotation, [literal]#++@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 [literal]#++@Embeddable++# annotation.
The JPAContainer [classname]#FieldFactory# generates a nested [classname]#Form#
for [literal]#++@Embedded++#, just as with [literal]#++@OneToOne++#.
[[jpacontainer.domain-model.annotation.onetomany]]
=== Annotation: [literal]#++@OneToMany++#
The [classname]#Country# entity in the domain model has a __one-to-many__
relationship with the [classname]#Person# entity ("country has persons"). This
relationship is represented with the [literal]#++@OneToMany++# annotation. The
[parameter]#mappedBy# parameter names the corresponding back-reference in the
[classname]#Person# entity.
----
@OneToMany(mappedBy = "country")
private Set<Person> persons;
----
When using the JPAContainer [classname]#FieldFactory# to automatically create
fields for a form, the [literal]#++@OneToMany++# relationship generates a
[classname]#MasterDetailEditor# for editing the items. See
<<dummy/../../../framework/jpacontainer/jpacontainer-fieldfactory#jpacontainer.fieldfactory,"Automatic
Form Generation">> for more details.
[[jpacontainer.domain-model.annotation.elementcollection]]
=== Annotation: [literal]#++@ElementCollection++#
The [literal]#++@ElementCollection++# annotation can be used for one-to-many
relationships to a collection of basic values such as [classname]#String# or
[classname]#Integer#, or to entities annotated as [literal]#++@Embeddable++#.
The referenced entities are stored in a separate table defined with a
[literal]#++@CollectionTable++# annotation.
----
@ElementCollection
@CollectionTable(
name="OLDPEOPLE",
joinColumns=@JoinColumn(name="COUNTRY_ID"))
private Set<Person> persons;
----
JPAContainer [classname]#FieldFactory# generates a
[classname]#MasterDetailEditor# for the [literal]#++@ElementCollection++#
relationship, just as with [literal]#++@OneToMany++#.
[[jpacontainer.domain-model.annotation.manytoone]]
=== Annotation: [literal]#++@ManyToOne++#
Many people can live in the same country. This would be represented with the
[literal]#++@ManyToOne++# annotation in the [classname]#Person# class.
----
@ManyToOne
private Country country;
----
JPAContainer [classname]#FieldFactory# generates a [classname]#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 handle
conversion between an entity and the entity ID using the
[classname]#SingleSelectConverter# as follows:
----
@Override
public <T extends Field> T createField(Class<?> dataType,
Class<T> fieldType) {
if (dataType == Country.class) {
JPAContainer<Country> countries =
JPAContainerFactory.make(Country.class, "mypunit");
ComboBox cb = new ComboBox(null, countries);
cb.setConverter(new SingleSelectConverter<Country>(cb));
return (T) cb;
}
return super.createField(dataType, fieldType);
}
----
The JPAContainer [classname]#FieldFactory# uses the translator internally, so
using it also avoids the problem.
[[jpacontainer.domain-model.annotation.transient]]
=== Annotation: [literal]#++@Transient++#
JPA assumes that all entity properties are persisted. Properties that should not
be persisted should be marked as transient with the [literal]#++@Transient++#
annotation.
----
@Transient
private Boolean superDepartment;
...
@Transient
public String getHierarchicalName() {
...
----
|