4. Write the Model Code

How to Encode a Java Data Model

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

4.1. Summary

  1. Encode each model class as a Java class.

  2. Add the validation constraints by using Java Validation API (or custom defined) validation annotations.

  3. Encode the property setters.

  4. Encode the add, update and destroy operations as (static-level) methods.

  5. Encode any other operation.

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

4.2. Encode the entity classes

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 concatenation, underscore separated, 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 encoded as a simple Java class with the corresponding JPA and Java Validation API annotations:

@Entity
@Table( name="author")
@ManagedBean( name="author")
@ViewScoped
public class Author {
  @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;

  public Author() {}
  public Author( Integer personId, String name, Date dateOfBirth, 
      Date dateOfDeath) {...}

  public Integer getPersonId() {...}
  public void setPersonId( Integer personId) {...}
  public String getName() {...}
  public void setName( String name) {...}
  public Date getDateOfBirth() {...}
  public void setDateOfBirth( Date dateOfBirth) {...}
  public Date getDateOfDeath() {...}
  public void setDateOfDeath( Date dateOfDeath) {...}

  public static void add( EntityManager em, UserTransaction ut, Integer personId, 
      String name, Date dateOfBirth, Date dateOfDeath) {...}
  public static void update( EntityManager em, UserTransaction ut, Integer personId, 
      String name, Date dateOfBirth, Date dateOfDeath) {...}
  public static void destroy( EntityManager em, UserTransaction ut, 
      Integer personId) {...}
}

Custom validation annotations are defined and implemented (i.e., @PositiveInteger) as shown in Part 2 (Validation Tutorial).

4.3. Implement a deletion policy

For the reference property Book::authors, we have to implement a deletion policy in the destroy 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 fires, and the author cannot be deleted. We have two possiblitities for 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.destroy method:

public static void destroy( EntityManager em, UserTransaction ut, 
    Integer personId) throws Exception {
  ut.begin();
  Author author = em.find( Author.class, personId);
  // find all books with 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();
  // update the corresponding book-to-author relations from the association
  // table (otherwise the author can't be deleted)
  for ( Book b : books) {
    b.getAuthors().remove( author);
  }
  // remove the author entry (table row)
  em.remove( author);
  ut.commit();
}

Essentially, there are three steps for this operation:

  • create a JPQL query which allows to select all book instances for this author - remember, this is an unidirectional association, there is no direct method available for this case.

  • for every found book which reference this author, we have to remove the author from its authors list.

  • remove tha author - now is safe and no relation specific error should occur.

4.4. Serialization and De-Serialization

In Java, by using the JPA built-in annotations (i.e., @Entity for the class and the corresponding ones for the properties) together with converter classes where is the case as shown in Part 3 (Eumeration Tutorial), the serialization is made internally. This means, the serialization from Java object to the corresponding database table row as well as the de-serilaization from database table column to Java object are made automatically.