The Java Entity class model can be directly coded for implementing the model layer of our Java back-end app.
Code each class of the Java Entity class model as a corresponding entity class.
Code any {id} property modifier with the JPA property annotation @Id
.
Code any property modifier denoting the functionality type of a reference property, such as {manyToOne}, with the corresponding JPA annotation, such as @ManyToOne
.
Code the integrity constraints specified in the model with the help of Java Bean Validation annotations (or custom validation annotations).
Code the getters and setters as well as the add and remove methods for multi-valued properties.
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.
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(...) {...}
}
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:
delete all books (co-)authored by the deleted author;
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:
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).
For every found book referencing this author, we have to remove the author reference from its authors
list.
Finally, delete the author record.
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.