4. Write the Model Code

How to Encode a JPA Entity Class Model

The JPA entity class model can be directly encoded for getting the model layer code of our Java back-end app.

4.1. Summary

  1. Encode each class from the JPA entity class model as a corresponding entity class.

  2. Encode an {id} property modifier with the JPA annotation @Id

  3. Encode any property modifier denoting the functionality type of a reference property, such as {manyToOne}, with the corresponding JPA annotation, such as @ManyToOne.

  4. Encode the integrity constraints specified in the model with the help of JavaBean Validation annotations (or custom validation annotations).

  5. Encode the getters and setters.

  6. Encode the add, retrieve, update and destroy storage operations as class-level methods.

These steps are discussed in more detail in the following sections.

4.2. Encode each class of the data model

Each class C of the JPA entity class model, which is our data model, is encoded as an annotated JavaBean class with name C having a default constructor (with no parameters) and a constructor with entity creation parameters.

For instance, the Book class from the JPA entity class model is encoded in the following way:

@Entity @Table( name="books")
@ViewScoped @ManagedBean( name="book")
public class Book {
  @Id @NotNull( message="An ISBN is required!")
  private String isbn;
  @Column( nullable=false)
  @NotNull( message="A title is required!")
  private String title;
  @Column( nullable=false)
  @NotNull( message="A year is required!")
  private Integer year;
  @ManyToOne( fetch=FetchType.EAGER)
  private Publisher publisher;

  public Book() {}
  public Book( String isbn, String title, 
      Integer year, Publisher publisher) {
    this.setIsbn( isbn);
    this.setTitle( title);
    this.setYear( year);
    this.setPublisher( publisher);
  ...  // getters, setters, etc.

The @ManyToOne annotation on the property publisher is used for specifying the functionality type of the association Book has Publisher represented by the reference property publisher since it holds that a book has one publisher and a publisher has many books. This annotation also allows to specify a fetch type with the parameter fetch taking one of the following two possible values:

  • FetchType.EAGER, implying that a retrieval of an entity includes a retrieval of the associated entity referenced by the entity with this property. In our example, this means that when a Book entity is retrieved, the Publisher entity referenced by the book's publisher property is also retrieved. This behavior is very useful and it should be used whenever the data to be retrieved can be handled in main memory.

  • FetchType.LAZY, implying that referenced entities are not automatically retrieved when a referencing entity is retrieved. In our example, this means that the referenced Publisher entity is not retrieved together with a referencing Book entity, leaving the value of the publisher property set to null. With this fetching behavior, a referenced entity has to be retrieved separately by invoking the reference property's getter in the context of a transaction (in our example by invoking getPublisher).

In the case of a single-valued reference property (representing a functional association) annotated with either @OneToOne or @ManyToOne, the default value is FetchType.EAGER, so referenced entities are fetched together with referencing entities, while in the case of a non-functional association (with either @OneToMany or @ManyToMany), the default value is FetchType.LAZY, so referenced entities are not fetched together with referencing entities, but have to be retrieved separately, if needed.

As a result of these JPA annotations, the following SQL table creation statement for creating the books table is generated:

  `ISBN` varchar(10) NOT NULL,
  `TITLE` varchar(255) NOT NULL,
  `YEAR` int(11) NOT NULL,

4.3. Encode the constraints

Take care that all property constraints specified in the entity class model are properly encoded by using suitable Bean Validation annotations, as explained in Part 2 (Validation Tutorial). For instance, for the name attribute, we have to use the JPA annotation @Id for specifying that the attribute corresponds to the primary key column of the database table to which the entity class is mapped, and the Bean Validation annotation @NotNull for defining a mandatory value constraint that is checked before an entity is saved to the database.

In the case of the address attribute, we have to define a mandatory value constraint in two forms: with the JPA annotation @Column( nullable=false) for the corresponding table column, and with the Bean Validation annotation @NotNull for the attribute.

@Id @NotNull( message="A name is required!")
private String name;

@Column( nullable=false)
@NotNull( message="An address is required!")
private String address;

Notice that, unfortunately, the current JavaEE technology requires defining the same constraint twice, once for the database in the form of a JPA annotation, and once for the Java app in the form of a Bean Validation annotation.

4.4. Encode getters and setters

Encode the setter operations as (instance-level) methods. The setters only assign values and do not perform any validation, since the property constraints are only checked before save by the JavaEE/JPA execution environment. The getters simply return the actual values of properties.

4.5. Implement a deletion policy

For any reference property, we have to choose and implement a deletion policy for taking care of the corresponding object destruction dependency in the destroy method of the reference property's range class. In our case, we have to choose between

  1. deleting all books published by the deleted publisher;

  2. dropping from all books published by the deleted publisher the reference to the deleted publisher.

We choose the second policy, which can only be used inf the case of an optional reference property such as book.publisher. This is shown in the following code of the Publisher.destroy method where for all book entities concerned the property book.publisher is cleared:

public static void destroy( EntityManager em, UserTransaction ut, 
    String name) throw Exception {
  Publisher publisher = em.find( Publisher.class, name);
  // find all Books which have this publisher
  Query query = em.createQuery( 
      "SELECT b FROM Book b WHERE b.publisher.name = :name");
  query.setParameter( "name", name);
  List<Book> books = query.getResultList();
  // clear these books' publisher reference
  for ( Book b: books) { b.setPublisher( null);}
  em.remove( publisher);

The method loops through all Book entities referencing the publisher to be destroyed and sets their publisher property to null.

4.6. Serialization and De-Serialization

Based on JPA annotations, together with suitable converter classes when needed, serialization (from Java objects to table rows) as well as the corresponding de-serialization (from table columns to Java objects) are performed automatically.