4. Write the Model Code

The Java Entity class model can be directly coded for implementing the model layer of our Java back-end app.

4.1. Summary

  1. Code each class of the Java Entity class model as a corresponding entity class.

  2. Code any {id} property modifier with the JPA property annotation @Id.

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

  4. Code the integrity constraints specified in the model with the help of Java Bean Validation annotations (or custom validation annotations).

  5. Code the getters and setters as well as the add and remove methods for multi-valued properties.

  6. Code the create, retrieve, update and delete storage management operations as class-level methods.

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

4.2. Code each class of the Java Entity class model

For the multi-valued reference property Book::authors, we use a parametrized Set type:

@Entity @Table( name="books")
@ViewScoped @ManagedBean( name="book")
public class Book {
  ...
  @ManyToMany( fetch=FetchType.EAGER)
  private Set<Author> authors;
 
  public Book() {}
  public Book( String isbn, String title, Integer year, 
      Publisher publisher, Set<Author> authors) {...}
  ...
  public Set<Author> getAuthors() { return this.authors;}
  public void setAuthors( Set<Author> authors) { this.authors=authors;}
  ...
}

The JPA annotation @ManyToMany allows to specify the Many-To-Many relation between the Book and Author. The annotation parameter FetchType.EAGER is used, so when a Book instance is created, the list of authors is populated with the corresponding Author instances. As a result of this annotation, a relation table between Book and Author is created, and the resulting SQL code is shown below:

CREATE TABLE IF NOT EXISTS `books_author` (
  `Book_ISBN` varchar(10) NOT NULL,
  `authors_PERSONID` int(11) NOT NULL
);

The resulting class name is the underscore-separated concatenation of the corresponding table names (e.g., books_author). The primary key columns from each of the two tables are used to implement the relation. The corresponding column names are created as follows:

  • for the table (e.g., books) which correspond to the class with the @ManyToMany annotation (e.g., Book), the class name is used as well as the primary key column name, (e.g., Book_ISBN).

  • for the other table (e.g., authors), the table name is concatenated with the primary key column name, (e.g., authors_PERSONID).

It is possible to control these parameters, i.e., table name and relation column names, by using the @JoinTable annotation. To obtain a custom named relation table, e.g., books_authors and the corresponding custom named columns, e.g., book_isbn and author_personid, one can use:

@JoinTable( name="books_authors", 
    joinColumns = {@JoinColumn( name="book_isbn", referencedColumnName="ISBN")},
    inverseJoinColumns = {@JoinColumn( name="author_personid", 
                                       referencedColumnName="PERSONID")}

In our application, we keep the default, so a @JoinTable annotation is not used.

The corresponding Author class is coded as a Java Entity class with suitable annotations:

@Entity @Table( name="author")
@ManagedBean( name="author") @ViewScoped
public class Author {
  // Properties
  @Id @PositiveInteger
  private Integer personId;
  @Column( nullable=false)
  @NotNull( message="A name is required!")
  private String name;
  @Column( nullable=false)
  @NotNull( message="A date of birth is required!")
  @Past private Date dateOfBirth;
  @Past private Date dateOfDeath;
  // Constructors
  ...
  // Setters/getters
  ...
  // Data management operations
  ...
}

In addition to defining properties, the entity class needs to define constructors, setters/getters and data management operations:

@Entity @Table( name="author")
@ManagedBean( name="author") @ViewScoped
public class Author {
  // Properties
  ...
  // Constructors
  public Author() {}
  public Author( Integer personId, String name, 
      Date dateOfBirth, Date dateOfDeath) {...}
  // Setters/getters
  ...
  // Data management operations
  public static void create( EntityManager em, UserTransaction ut, 
      Integer personId, String name, Date dateOfBirth, 
      Date dateOfDeath) {...}
  public static void update(...) {...}
  public static void delete(...) {...}
}

4.3. Implement a deletion policy

For the reference property Book::authors, we have to implement a deletion policy in the delete method of the Author class. If we just try to delete an author, and the author is referenced by any of the book records, then an integrity constraint violation exception is raised, and the author cannot be deleted. We have two possiblitities for dealing with this situation:

  1. delete all books (co-)authored by the deleted author;

  2. drop from all books (co-)authored by the deleted author the reference to the deleted author.

We go for the second option. This is shown in the following code of the Author.delete method:

public static void delete( EntityManager em, UserTransaction ut, 
    Integer personId) throws Exception {
  ut.begin();
  Author author = em.find( Author.class, personId);
  // Find all books of this author
  Query query = em.createQuery( "SELECT DISTINCT b FROM Book b "+
      "INNER JOIN b.authors a WHERE a.personId = :personId");
  query.setParameter( "personId", personId);
  List<Book> books = query.getResultList();
  // Remove the author reference from the books' authors list
  for (Book b : books) {
    b.getAuthors().remove( author);
  }
  // Delete the author record (table row)
  em.remove( author);
  ut.commit();
}

Essentially, the delete operation consists of three steps:

  1. Create a JPQL query which selects all books of this author (since this is a unidirectional association from books to authors, the books of an author are not directly accessible from an author).

  2. For every found book referencing this author, we have to remove the author reference from its authors list.

  3. Finally, delete the author record.

4.4. Serialization and De-Serialization

In Java EE, the serialization from objects to corresponding database records as well as the de-serialization from database records to objects are performed automatically by the Java EE execution environment based on JPA annotations and converter classes for custom conversions.