The JPA entity class model can be directly encoded for getting the model layer code of our Java back-end app.
Encode each class from the JPA entity class model as a corresponding entity class.
Encode an {id} property modifier with the JPA annotation @Id
Encode any property modifier denoting the functionality type of a reference property,
such as {manyToOne}, with the corresponding JPA annotation, such as
@ManyToOne.
Encode the integrity constraints specified in the model with the help of JavaBean Validation annotations (or custom validation annotations).
Encode the getters and setters.
Encode the add, retrieve, update and
destroy storage operations as class-level methods.
These steps are discussed in more detail in the following sections.
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:
CREATE TABLE IF NOT EXISTS `books` (
`ISBN` varchar(10) NOT NULL,
`TITLE` varchar(255) NOT NULL,
`YEAR` int(11) NOT NULL,
`PUBLISHER_NAME` varchar(255) DEFAULT NULL
FOREIGN KEY (`PUBLISHER_NAME`) REFERENCES `publishers` (`NAME`)
);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.
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.
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
deleting all books published by the deleted publisher;
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 { ut.begin(); 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); ut.commit(); }
The method loops through all Book entities referencing the publisher to be
destroyed and sets their publisher property to null.