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
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
|
---
title: Basic Use of JPAContainer
order: 4
layout: page
---
[[jpacontainer.usage]]
= Basic Use of JPAContainer
Vaadin JPAContainer offers a highly flexible API that makes things easy in
simple cases while allowing extensive flexibility in demanding cases. To begin
with, it is a [classname]#Container#, as described in
<<dummy/../../../framework/datamodel/datamodel-container#datamodel.container,"Collecting
Items in Containers">>.
In this section, we look how to create and use [classname]#JPAContainer#
instances. We assume that you have defined a domain model with JPA annotations,
as described in the previous section.
[[jpacontainer.usage.jpacontainerfactory]]
== Creating [classname]#JPAContainer# with [classname]#JPAContainerFactory#
The [classname]#JPAContainerFactory# is the easy way to create
[classname]#JPAContainer#s. It provides a set of __make...()__ factory methods
for most cases that you will likely meet. Each factory method uses a different
type of entity provider, which are described in
<<dummy/../../../framework/jpacontainer/jpacontainer-entityprovider#jpacontainer.entityprovider,"Entity
Providers">>.
The factory methods take the class type of the entity class as the first
parameter. The second parameter is either a persistence unit name (persistence
context) or an [classname]#EntityManager# instance.
----
// Create a persistent person container
JPAContainer<Person> persons =
JPAContainerFactory.make(Person.class, "book-examples");
// You can add entities to the container as well
persons.addEntity(new Person("Marie-Louise Meilleur", 117));
// Set up sorting if the natural order is not appropriate
persons.sort(new String[]{"age", "name"},
new boolean[]{false, false});
// Bind it to a component
Table personTable = new Table("The Persistent People", persons);
personTable.setVisibleColumns("id","name","age");
layout.addComponent(personTable);
----
See the http://demo.vaadin.com/book-examples-vaadin7/book#jpacontainer.basic[on-line example, window="_blank"].
It's that easy. In fact, if you run the above code multiple times, you'll be
annoyed by getting a new set of persons for each run - that's how persistent the
container is. The basic [methodname]#make()# uses a
[classname]#CachedMutableLocalEntityProvider#, which allows modifying the
container and its entities, as we do above by adding new entities.
When using just the persistence unit name, the factory creates an instance of
[classname]#EntityManagerFactory# for the persistence unit and uses it to build
entity managers. You can also create the entity managers yourself, as described
later.
The entity providers associated with the different factory methods are as
follows:
[[table.jpacontainer.usage.jpacontainerfactory]]
.[classname]#JPAContainerFactory# Methods
|===============
|[methodname]#make()#|[classname]#CachingMutableLocalEntityProvider#
|[methodname]#makeReadOnly()#|[classname]#CachingLocalEntityProvider#
|[methodname]#makeBatchable()#|[classname]#BatchableLocalEntityProvider#
|[methodname]#makeNonCached()#|[classname]#MutableLocalEntityProvider#
|[methodname]#makeNonCachedReadOnly()#|[classname]#LocalEntityProvider#
|===============
[classname]#JPAContainerFactory# holds a cache of entity manager factories for
the different persistence units, making sure that any entity manager factory is
created only once, as it is a heavy operation. You can access the cache to get a
new entity manager with the
[methodname]#createEntityManagerForPersistenceUnit()# method.
----
// Get an entity manager
EntityManager em = JPAContainerFactory.
createEntityManagerForPersistenceUnit("book-examples");
// Do a query
em.getTransaction().begin();
em.createQuery("DELETE FROM Person p").executeUpdate();
em.persist(new Person("Jeanne Calment", 122));
em.persist(new Person("Sarah Knauss", 119));
em.persist(new Person("Lucy Hannah", 117));
em.getTransaction().commit();
...
----
See the http://demo.vaadin.com/book-examples-vaadin7/book#jpacontainer.basic[on-line example, window="_blank"].
Notice that if you use update the persistent data with an entity manager outside
a [classname]#JPAContainer# bound to the data, you need to refresh the container
as described in <<jpacontainer.usage.entitites>>.
[[jpacontainer.usage.jpacontainerfactory.thehardway]]
=== Creating [classname]#JPAContainer# Manually
While it is normally easiest to use a [classname]#JPAContainerFactory# to create
[classname]#JPAContainer# instances, you may need to create them manually. It is
necessary, for example, when you need to use a custom entity provider or extend
[classname]#JPAContainer#.
First, we need to create an entity manager and then the entity provider, which
we bind to a [classname]#JPAContainer#.
----
// We need a factory to create entity manager
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("book-examples");
// We need an entity manager to create entity provider
EntityManager em = emf.createEntityManager();
// We need an entity provider to create a container
CachingMutableLocalEntityProvider<Person> entityProvider =
new CachingMutableLocalEntityProvider<Person>(Person.class,
em);
// And there we have it
JPAContainer<Person> persons =
new JPAContainer<Person> (Person.class);
persons.setEntityProvider(entityProvider);
----
See the http://demo.vaadin.com/book-examples-vaadin7/book#jpacontainer.thehardway[on-line example, window="_blank"].
You could save the first step by asking the entity manager from the
[classname]#JPAContainerFactory#.
[[jpacontainer.usage.entitites]]
== Creating and Accessing Entities
JPAContainer integrates with the JPA entity manager, which you would normally
use to create and access entities with JPA. You can use the entity manager for
any purposes you may have, and then [classname]#JPAContainer# to bind entities
to user interface components such as [classname]#Table#, [classname]#Tree#, any
selection components, or a [classname]#Form#.
You can add new entities to a [classname]#JPAContainer# with the
[methodname]#addEntity()# method. It returns the item ID of the new entity.
----
Country france = new Country("France");
Object itemId = countries.addEntity(france);
----
The item ID used by [classname]#JPAContainer# is the value of the ID property
(column) defined with the [literal]#++@Id++# annotation. In our
[classname]#Country# entity, it would have [classname]#Long# type. It is
generated by the entity manager when the entity is persisted and set with the
setter for the ID proeprty.
Notice that the [methodname]#addEntity()# method does __not__ attach the entity
instance given as the parameter. Instead, it creates a new instance. If you need
to use the entity for some purpose, you need to get the actual managed entity
from the container. You can get it with the item ID returned by
[methodname]#addEntity()#.
----
// Create a new entity and add it to a container
Country france = new Country("France");
Object itemId = countries.addEntity(france);
// Get the managed entity
france = countries.getItem(itemId).getEntity();
// Use the managed entity in entity references
persons.addEntity(new Person("Jeanne Calment", 122, france));
----
[[jpacontainer.usage.entitites.items]]
=== Entity Items
The [methodname]#getItem()# method is defined in the normal Vaadin
[interfacename]#Container# interface. It returns an [classname]#EntityItem#,
which is a wrapper over the actual entity object. You can get the entity object
with [methodname]#getEntity()#.
An [classname]#EntityItem# can have a number of states: persistent, modified,
dirty, and deleted. The dirty and deleted states are meaningful when using
__container buffering__, while the modified state is meaningful when using
__item buffering__. Both levels of buffering can be used together - user input
is first written to the item buffer, then to the entity instance, and finally to
the database.
The [methodname]#isPersistent()# method tells if the item is actually
persistent, that is, fetched from a persistent storage, or if it is just a
transient entity created and buffered by the container.
The [methodname]#isModified()# method checks whether the [classname]#EntityItem#
has changes that are not yet committed to the entity instance. It is only
relevant if the item buffering is enabled with [methodname]#setBuffered(true)#
for the item.
The [methodname]#isDirty()# method checks whether the entity object has been
modified after it was fetched from the entity provider. The dirty state is
possible only when buffering is enabled for the container.
The [methodname]#isDeleted()# method checks whether the item has been marked for
deletion with [methodname]#removeItem()# in a buffered container.
[[jpacontainer.usage.entitites.refreshing]]
=== Refreshing JPAContainer
In cases where you change [classname]#JPAContainer# items outside the container,
for example by through an [interfacename]#EntityManager#, or when they change in
the database, you need to refresh the container.
The [interfacename]#EntityContainer# interface implemented by
[classname]#JPAContainer# provides two methods to refresh a container. The
[methodname]#refresh()# discards all container caches and buffers and refreshes
all loaded items in the container. All changes made to items provided by the
container are discarded. The [methodname]#refreshItem()# refreshes a single
item.
[[jpacontainer.usage.nested-properties]]
== Nested Properties
If you have a one-to-one or many-to-one relationship, you can define the
properties of the referenced entity as __nested__ in a
[classname]#JPAContainer#. This way, you can access the properties directly
through the container of the first entity type as if they were its properties.
The interface is the same as with [classname]#BeanContainer# described in
<<dummy/../../../framework/datamodel/datamodel-container#datamodel.container.beancontainer,"BeanContainer">>.
You just need to add each nested property with
[methodname]#addNestedContainerProperty()# using dot-separated path to the
property.
----
// Have a persistent container
JPAContainer<Person> persons =
JPAContainerFactory.make(Person.class, "book-examples");
// Add a nested property to a many-to-one property
persons.addNestedContainerProperty("country.name");
// Show the persons in a table, except the "country" column,
// which is an object - show the nested property instead
Table personTable = new Table("The Persistent People", persons);
personTable.setVisibleColumns("name", "age", "country.name");
// Have a nicer caption for the country.name column
personTable.setColumnHeader("country.name", "Nationality");
----
See the http://demo.vaadin.com/book-examples-vaadin7/book#jpacontainer.nested[on-line example, window="_blank"].
The result is shown in <<figure.jpacontainer.usage.nested-properties>>. Notice
that the [literal]#++country++# property in the container remains after adding
the nested property, so we had to make that column invisible. Alternatively, we
could have redefined the [methodname]#toString()# method in the country object
to show the name instead of an object reference.
[[figure.jpacontainer.usage.nested-properties]]
.Nested Properties
image::img/nested-properties.png[]
You can use the [literal]#++*++# wildcard to add all properties in a nested
item, for example, " [literal]#++country.*++#".
[[jpacontainer.usage.hierarchical]]
== Hierarchical Container
[classname]#JPAContainer# implements the [interfacename]#Container.Hierarchical#
interface and can be bound to hierarchical components such as a
[classname]#Tree# or [classname]#TreeTable#. The feature requires that the
hierarchy is represented with a __parent__ property that refers to the parent
item. At database level, this would be a column with IDs.
The representation would be as follows:
----
@Entity
public class CelestialBody implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToOne
private CelestialBody parent;
...
} ...
// Create some entities
CelestialBody sun = new CelestialBody("The Sun", null);
CelestialBody mercury = new CelestialBody("Mercury", sun);
CelestialBody venus = new CelestialBody("Venus", sun);
CelestialBody earth = new CelestialBody("Earth", sun);
CelestialBody moon = new CelestialBody("The Moon", earth);
CelestialBody mars = new CelestialBody("Mars", sun);
...
----
See the http://demo.vaadin.com/book-examples-vaadin7/book#jpacontainer.hierarchical[on-line example, window="_blank"].
You set up a [classname]#JPAContainer# to have hierarchy by calling
[methodname]#setParentProperty()# with the name of the property that refers to
the parent. Coincidentally, it is named " [literal]#++parent++#" in the example:
----
// Create the container
JPAContainer<CelestialBody> bodies =
JPAContainerFactory.make(CelestialBody.class, "my-unit");
// Set it up for hierarchical representation
bodies.setParentProperty("parent");
// Bind it to a hierarhical component
Tree tree = new Tree("Celestial Bodies", bodies);
tree.setItemCaptionMode(Tree.ITEM_CAPTION_MODE_PROPERTY);
tree.setItemCaptionPropertyId("name");
----
See the http://demo.vaadin.com/book-examples-vaadin7/book#jpacontainer.hierarchical[on-line example, window="_blank"].
You can use the [methodname]#rootItemIds()# to acquire the item IDs of the root
elements with no parent.
----
// Expand the tree
for (Object rootId: bodies.rootItemIds())
tree.expandItemsRecursively(rootId);
----
See the http://demo.vaadin.com/book-examples-vaadin7/book#jpacontainer.hierarchical[on-line example, window="_blank"].
[[jpacontainer.usage.hierarchical.unsupported]]
=== Unsupported Hierarchical Features
Using [methodname]#setParent()# in the container to define parenthood is not
supported.
Also, the current implementation does not support __setChildrenAllowed()__,
which controls whether the user can expand a node by clicking a toggle. The
toggle is by default visible for all nodes, even if they have no children. The
method is not supported because it would require storing the information outside
the entities. You can override [methodname]#areChildrenAllowed()# to implement
the functionality using a custom logic.
----
// Customize JPAContainer to define the logic for
// displaying the node expansion indicator
JPAContainer<CelestialBody> bodies =
new JPAContainer<CelestialBody>(CelestialBody.class) {
@Override
public boolean areChildrenAllowed(Object itemId) {
// Some simple logic
return getChildren(itemId).size() > 0;
}
};
bodies.setEntityProvider(
new CachingLocalEntityProvider<CelestialBody>(
CelestialBody.class, em));
----
See the http://demo.vaadin.com/book-examples-vaadin7/book#jpacontainer.hierarchical[on-line example, window="_blank"].
|