The Java Entity class model can be directly coded for getting the model layer code of our Java back-end app.
Code each class from the Java Entity class model as a corresponding entity class.
Code an {id} property modifier with the JPA 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.
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.
Each class C
of the Java Entity class model is coded as an annotated bean 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 Entity class model is coded 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 coded by using suitable Bean Validation annotations, as explained in Part 2. 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 Java EE 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.
Code 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 Java EE 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 delete
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.delete
method where for all book
entities concerned the property book.publisher
is cleared:
public static void delete( 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
.