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
.