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.4, “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.

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 18.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.

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


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 18.4.2, “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 JPAContainer to bind entities to user interface components such as Table, Tree, any selection components, or a Form.

You can add new entities to a JPAContainer with the 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 JPAContainer is the value of the ID property (column) defined with the @Id annotation. In our Country entity, it would have 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 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 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));

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 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 BeanContainer described in Section 9.4.1, “BeanContainer. You just need to add each nested property with 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(new String[]{"name","age",
                                           "country.name"});

// Have a nicer caption for the country.name column
personTable.setColumnHeader("country.name", "Nationality");

The result is shown in Figure 18.5, “Nested Properties”. Notice that the 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 toString() method in the country object to show the name instead of an object reference.


You can use the * wildcard to add all properties in a nested item, for example, "country.*".

JPAContainer implements the Container.Hierarchical interface and can be bound to hierarchical components such as a Tree or 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);
...

You set up a JPAContainer to have hierarchy by calling setParentProperty() with the name of the property that refers to the parent. Coincidentally, it is named "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");

You can use the rootItemIds() to acquire the item IDs of the root elements with no parent.

// Expand the tree
for (Object rootId: bodies.rootItemIds())
    tree.expandItemsRecursively(rootId);