ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 01.01.2026

Просмотров: 2644

Скачиваний: 0

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

Vaadin JPAContainer

21.3. 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 21.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.

Figure 21.4. A Domain Model

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.

21.3.1. 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 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;

458

Defining a Domain Model

Vaadin JPAContainer

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.

Annotation: @Entity

Each class that is enabled as a persistent entity must have the @Entity annotation.

@Entity

public class Country {

Annotation: @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 @GeneratedValue annotation. Any generation type should work.

Annotation: @OneToOne

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 21.8, “Automatic Form Generation” for more details.

Annotation: @Embedded

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.

Persistence Metadata

459


Vaadin JPAContainer

Annotation: @OneToMany

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 21.8, “Automatic Form Generation” for more details.

Annotation: @ElementCollection

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.

Annotation: @ManyToOne

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.

460

Persistence Metadata



Vaadin JPAContainer

Annotation: @Transient

JPA assumes that all entity properties are persisted. Properties that should not be persisted should be marked as transient with the @Transient annotation.

@Transient

private Boolean superDepartment;

...

@Transient

public String getHierarchicalName() {

...

21.4. 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 Container, as described in Section 9.5, “Collecting Items in Containers”.

In this section, we look how to create and use JPAContainer instances. We assume that you have defined a domain model with JPA annotations, as described in the previous section.

21.4.1. Creating JPAContainer with JPAContainerFactory

The JPAContainerFactory is the easy way to create JPAContainers. 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 Section 21.5, “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 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(new String[]{"id","name","age"}); layout.addComponent(personTable);

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 make() uses a 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 EntityManagerFactory for the persistence unit and uses it to build entity managers. You can also create the entity managers yourself, as described later.

Basic Use of JPAContainer

461


Vaadin JPAContainer

The entity providers associated with the different factory methods are as follows:

Table 21.1. JPAContainerFactory Methods

make()

CachingMutableLocalEntityProvider

makeReadOnly()

CachingLocalEntityProvider

makeBatchable()

BatchableLocalEntityProvider

makeNonCached()

MutableLocalEntityProvider

makeNonCachedReadOnly()

LocalEntityProvider

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 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();

...

Notice that if you use update the persistent data with an entity manager outside a JPAContainer bound to the data, you need to refresh the container as described in Section 21.4.2, “Creating and Accessing Entities”.

Creating JPAContainer Manually

While it is normally easiest to use a JPAContainerFactory to create 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 JPAContainer.

First, we need to create an entity manager and then the entity provider, which we bind to a

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);

462

Creating JPAContainer with JPAContainerFactory