In this chapter, we show how to build a simple Java EE web application using the Java Persistence API (JPA) for object-to-storage mapping and Java Server Faces (JSF) as the user interface (or “view”) technology. Such an application requires a web server that supports the Java EE specifications Java Servlets, Java Expression Language (EL), JPA and JSF, such as the open source web server Tomcat/TomEE66(pronounced “Tommy”). In addition to the Java code executed on the back-end computer that runs the TomEE web server, a Java EE web app also consists of HTML, CSS and possibly some auxiliary JavaScript code that is executed by a web browser running on the user’s front-end computer. Since essentially all data processing is performed on the back-end, and the front-end only renders the user interface, we classify Java EE web applications as back-end web apps.
We show how to develop, deploy and run a simple example app using the TomEE web server, which is Apache Tomcat extended by adding basic Java EE features for providing an execution environment for light-weight Java EE web apps. We assume that you have already installed the TomEE Plume67 server and MySQL68. Then simply follow our installation and configuration instructions69.
The purpose of our minimal example app is to manage information about books. For simplicity, we deal with a single object type Book
, as depicted in Figure 4.1.
The following table shows a sample data population for the model class Book
.
Table 4.1 Sample data about books
ISBN | Title | Year |
006251587X | Weaving the Web | 2000 |
0465026567 | Gödel, Escher, Bach | 1999 |
0465030793 | I Am A Strange Loop | 2008 |
What do we need for a data management app? There are four standard us e cases, which have to be supported by the app:
These four standard use cases, and the corresponding data management operations, are often summarized with the acronym CRUD.
For entering data with the help of the keyboard and the screen of our computer, we use HTML forms, which provide the user interface technology for web applications.
For any data management app, we need a technology that allows to store data in persistent records on a secondary storage device, such as a hard-disk or a solid state disk. JPA allows using a great number of different data storage technologies, including many SQL database management systems (DBMS) such as Oracle, MySQL and PostgreSQL. We don’t have to change much in the application code for switching from one storage technology to another. Adding the right driver implementation to our Java runtime environment, properly setting up the DBMS and changing the database access configuration is in general all we need to do. In our example application, we explain how to set up the JPA configuration for MySQL.
Compared to JavaScript, what is different in Java? We list a few observations:
1.No program withouta class: Any Java program must include at least one class.
2.Java is strongly typed: Properties, parameters and variables must be declared to be of some type.
3.No global variables, no global procedures: In Java, variables, procedures and functions must be defined in the context of a class, which provides their name space.
4.Arrays are static: Arrays have a fixed size, which cannot be changed at runtime.
5.No object without a class: For creating an object, a class has to be used (or defined) for
‒defining the properties of the object’s property slots
‒defining the methods and functions that can be applied to the object (and all other objects instantiating the class)
6.Classes, properties and methods are defined with a visibility level (public
, protected
or private
), which restricts their accessibility.
7.Type parameters: Classes and complex data structures (such as lists) can be defined with the help of type parameters. See, for instance, the generics tutorial70 by Oracle.
8.Java programs must be compiled before they can be executed.
9.Speed: Java is about twice as fast as optimized JavaScript.
In Java, visibility levels are used to define the possible levels of access:
Table 4.2 Java Visibility Level
Normally, properties are defined as private, such that they can only be assigned with a public setter, which allows to control, and possibly log, any change of the property value. Unfortunately, Java does not allow to protect only write, but not read access. Therefore, we always have to define getters along with setters, even if they are not needed.
A Java bean class (or, simply, bean class) is a Java class with a parameter-free constructor where all properties are serializable and have a get
and set
method (also called “getter” and “setter”). A Java bean is an object created with the help of a bean class.
A JPA entity class (or, simply, entity class) is a Java bean class with an @Entity
annotation, implying that a Java EE runtime environment (such as provided by the TomEE PLUS71 web server) will take care of the persistent storage of its instances.
JPA is a Java API for the management of persistent data in Java applications. It uses the Java Persistence Query Language (JPQL), a platform-independent object-oriented query language based on SQL. JPQL query expressions look very similar to SQL query expressions, but they are executed in the context of JPA entity objects.
JSF is a Java specification for building component-based user interfaces for web applications. Its current version, JSF 2, by default, uses Facelets as its view technology. By contrast, JSF 1 has used JavaServer Pages (JSP) as its default view technology.
A JSF facelet is an XML-based template that combines static UI content items with data variables for generating an XHTML user interface page.
In the first step, we set up our folder structure for the application program code. The application name is “Public Library”, so it would be natural to use a corresponding name for the application folder, like “PublicLibrary”, but we prefer using MinimalApp
. Then we create the application structure. There are many ways to do this, like for example to use the Eclipse72 development environment (and create and configure a Dynamic Web Project). In this chapter we show how to do it manually, so there is no need to use special tools. For your convenience, we also provide an ANT script (see our guide73 for a download link), allowing to automatically create the folder structure of the web application, compile it to a Web application Archive (WAR) file and then deploy it to a TomEE server for execution. The application structure (which is compatible with the Dynamic Web Project structure of Eclipse, so it can be imported in Eclipse) is the following:
This folder structure has the following parts:
1.The src
folder contains the app code folder pl
, defining the Java package name for the app (as a shortcut of ’public library’), and the folder META-INF
with configuration files:
a.the app code folder pl
contains the model and controller code in its subfolders m
and c
, while the view/UI related code is contained in the WebContent
folder;
b.the most important configuration file is the persistence.xml
file. It contains the configuration for the database connection. The content of this file is discussed in Section 4.2.
2. The WebContent
folder contains various web resources, including template files and custom view files:
a.the resources
folder is used for storing resources, such as downloadable documents, images or videos.
b.the views
folder stores our custom view files for the application, so it represents the View part of the MVC paradigm. Please note that it is not strictly required to name it “views”, but it makes a lot of sense to do it so, since this is what this folder represents.
c.the WEB-INF
folder contains project libraries (in the form of jar
files in the lib
subfolder (in our case we don’t need such libraries), the facelet files for the UI pages (as part of the templates
subfolder), the faces-config. xml
file, which stores the facelet configuration and the web.xml
configuration file, specific to the TomEE server used to run our application.
We create a “Main Menu” page for our application, thus we add an index.xhtml
file to our views
folder with the following content:
The code consists of HTML elements and JSF elements, which are discussed below.
In the second step, we create the model classes for our app, using a separate Java source code file (with extension . java
) for each model class. In the information design model shown in Figure 4.1 above, there is only one class, representing the object type Book
. So, we create a file Book. java
in the folder src/pl/m
with the following code:
Notice that the model class Book
is coded as a JPA entity class, which is a JavaBean class enriched with the following JPA annotations:
@Entity
designates a class as an entity class implying that the instances of this class will be stored persistently.@Table( name="books")
specifies the name of the database table to be used for storing the Book
entities. This annotation is optional and defaults to a table name being the same as the class name but in lower case (that is, it would be book
in our case).@Id
annotation marks the standard identifier attribute, implying that the corresponding column of the underlying SQL database table is designated as the PRIMARY KEY. In our example, isbn
is used as the standard identifier attribute, and the corresponding isbn
column of the books
table stores the primary key values.In the entity class Book
, we also define the following static (class-level) methods:
Book. create
for creating a new Book
instance.Book. retrieveAll
for retrieving all Book
instances from the persistent data store.Book.retrieve
for retrieving a specific Book
instance from the persistent data store by means of its standard identifier.Book. update
for updating an existing Book
instance.Book. delete
for deleting a Book
instance.Book. generateTestData
for creating a few example book records to be used as test data.Book.clearData
for clearing the book database table.The signatures of these methods, which are discussed in more detail in the following subsections, are shown in the following program listing:.
The JPA architecture for data management and object-to-storage mapping is based on the concept of an entity manager, which provides the data management methods persist
for saving a newly created or updated entity, find
for retrieving an entity, and remove
for deleting an entity.
Since the database access operations of an entity manager are executed in the context of a transaction, our data management methods have a parameter ut
of type UserTransaction
. Before the entity manager can invoke the database write method persist
, a transaction needs to be started with ut.begin()
. After all write (and state change) operations have been performed, the transaction is completed (and all changes are committed) with ut.commit()
.
The instances of our entity class Book
are Java objects representing “entities” (or business objects), which can be serialized, or, in other words, converted to records (or rows) of a database table, as shown in Table 4.1.
SQL
The Structured Query Language (SQL), created in the early 1970s by Donald D. Chamberlin and Raymond F. Boyce, is an ISO standard that is based on the Relational Database model of Edgar F. Codd and defines
SQL is widely used in Database Management Systems (DBMSs) and in programming languages for persistent data storage. Despite the existence of the SQL standard, each SQL DBMS has its own dialect of SQL. Consequently, SQL code is in practice not completely portable among different DBMSs without adjustments.
The data storage technology used in our example app is MySQL74, and the SQL code used to create the schema for the database table books
is the following:
While it is also possible to create the database schema manually (with the help of CREATE TABLE statements such as the one above), we show below how the database schema can be automatically generated by JPA. In both cases, the database setup, including a user account and the associated rights (create, update, etc), must be done manually before the JPA application can connect to it.
The Book. create
method takes care of creating a new Book
instance and saving it to a database with the help of an ’entity manager’:
To store the new object, the persist
method of the given ’entity manager’ is invoked. It is responsible for creating the corresponding SQL INSERT
statement and executing it.
The instances of an entity class, such as Book
, are retrieved from the database with the help of a corresponding query expressed in the Java Persistence Query Language (JPQL75). These queries are similar to SQL queries. They use class names Instead of table names, property names instead of column names, and object variables instead of row variables.
In the Book. retrieveAll
method, first a query
asking for all Book
instances is created, and then this query is executed with query. getResultList()
assigning its answer set to the list variable books
:
To update an existing Book
instance, first we need to retrieve it from the database, by using em. find
, and then set those attributes the value of which has changed:
Notice that, when invoking the find
method for retrieving an entity, the first argument must be a reference to the entity class concerned (here: Book. class
), so the JPA runtime environment can identify the database table from which to retrieve the entity’s data. The second argument must be the value of the entity’s primary key.
Notice that in the update case, we do not have to use persist
for saving the changes. Saving is automatically managed by the JPA runtime environment when we complete the transaction with ut.commit()
.
A book entity can be deleted from the database as shown in the following example code:
To delete an entity from the database, we first need to retrieve it with the help of the find
method as in the update case. Then, the remove
method has to be invoked by the ’entity manager’, and finally the transaction is completed with ut.commit()
.
For being able to test our code, we may create some test data and save it in our database. We can use the following procedure for this:
After clearing the database, we successively create 3 instances of the Book
entity class and save them with the help of persist
.
The following procedure clears our database by deleting all rows:
JPA does not provide a direct method to drop the entire population of a specific class from the database. However, this can be easily obtained by using a JPQL statement as shown in the above code. The JPQL code can be read as: delete all rows from the database table associated with the entity class Book
.
In this section we show how to
EntityManager
and UserTransaction
instances required for performing database operations,A controller class contains the code that glues the views to the model, as well as all methods that do neither belong to the model nor to the view, like getting a connection with the database server. In our example app, this class is pl.c.BookController
in the src/pl/c
folder.
JPA requires an EntityManager
object for executing JPQL queries (with SELECT
) and data manipulation statements (with INSERT
, UPDATE
and DELETE
). Also, in order to perform database write operations, a UserTransaction
object is required for starting and completing transactions. In a standalone application, the programmer has to create an entity manager and a transaction manually, using a factory pattern as shown in the following code fragment:
A JPA-enabled Java web application normally runs in an environment called “container” (in our case this is TomEE), which takes care of creating an EntityManager
and a UserTransaction
object if the right annotations are used. The code responsible for this is part of the controller class ( e. g., pl.c.BookController
) since the controller is responsible for managing the database connections.
A closer look at this code shows that it is sufficient to use the @PersistenceContext
annotation and provide a unitName
(see the next section) for obtaining an EntityManager
instance at runtime. Also, obtaining a UserTransaction
instance at runtime is as simple as using the @Resource
annotation for the user transaction reference property ut
. Not only that the required code is short and simple, but if the database type is changed (e. g. when we switch from MySQL to an Oracle database), this code remains the same.
In the previous section, discussing the BookController
class, we have shown how to obtain the EntityManager
and UserTransaction
objects required for performing database operations. The @PersistenceContext
annotation of the EntityManager
reference property requires a unitName
, which is just a name used for identifying the storage management configuration defined in the src/ META-INF/persistence.xml
file. In our example app this file has the following content:
The configuration name (“MinimalApp”) is defined by the name
attribute of the persistence-unit
element. This is the value we have to use for the unitName
property of the @PersistenceContext
annotation.
The persistence-unit
element has three content parts:
class
elements, each one containing the fully qualified name of an entity class of the app (like pl.m.Book
in our example app).property
elements used for providing further configuration settings.jta-data-source
element for specifying the configuration block in the config/TomEE.xml
web server configuration file in the web server installation folder.In our persistence.xml
file, two configuration properties have been set:
–javax.persistence.schema-generation.database.action
, with the possible values: none
(default), create
, drop-and-create
and drop
. It specifies if the database schema is to be automatically created and additionally allows to drop the existing tables before creating the new ones (with drop
or drop-and-create
)..
–javax.persistence.schema-generation.create-source
, with the possible values metadata
(default), script
, metadata-then-script
and script-then-metadata
. It specifies the source of information used to create the database schema. The value metadata
enforces using the JPA annotations while the value script
allows using an external script for defining the schema.
The jta-data-source
element of our persistence.xml
file refers to the Resource
element with id
value “MinimalApp” in the config/TomEE.xml
file, which has the following content:
The Resource
element contains the information required to connect with the database (user name, password, access URL and the Java class name of the connection driver). Notice that in order to use a connection driver, one needs to download the jar file for that specific technology and copy it into the lib
subfolder of the TomEE installation folder. Finally, TomEE needs to be restarted in order to load the new driver. See also our instructions page76, for additional details related to JDBC drivers, and in particular how to use the official MySQL JDBC driver “Connector/J”.
The main template, called page.xhtml
, is shown below. It has two sub-templates:
header.xhtml
defines the general header information items (such as the application name)footer.xhtml
defines the general footer information items (such as a copyrights notice)Both sub-templates are included in the main template with the help of a ui:include
element. We add all three template files to the WebContent/WEB-INF/ templates
folder.
The content of our HTML5-compliant main template page.xhtml
is the following:
In the code, one can see that some HTML elements are used (e. g., title
, header
, main
and footer
) while others like h:head
and ui:insert
are not HTML elements, but have been defined by JSF. For instance, JSF defines its own head element h:head
for injecting special HTML code such as script
elements that load JavaScript code for HTTP messaging between the web browser and the back-end Java program by invoking methods of the JavaScript API XML HTTP Request (XHR).
Our main template defines three content regions: header, main and footer. The header and footer regions are defined by sub-templates included with the help of the ui:include
element.
The header.xhtml
sub-template contains the application menu, corresponding to the CRUD operations:
The footer.xhtml
sub-template contains the “Back to main menu” link, which we like to show on each application page, except the index page:
Notice that the h
namespace must be defined once again in footer.xhtml
even if it has already been defined in page.xhtml
. In the footer template component, we define the “Back to main menu” operation, which is going to be injected into each page that uses this template. The main region is dynamic, and will be replaced with the content generated by a facelet template.
JSF is using the following namespaces:
–xmlns:ui=" http://java.sun.com/jsf/facelets"
for the JSF Facelets Tag Library providing templating elements (like ui:insert
for specifying the region of a template where to inject the facelet content).
–xmlns:h=" http://java.sun.com/jsf/html"
for the JSF HTML Tag Library providing JSF versions of HTML elements, which are then mapped to HTML elements. For example h: inputText
, which is mapped to an HTML input
element.
–xmlns:f=" http://java.sun.com/jsf/core"
for the JSF Core Tag Library providing custom actions or elements that are independent of any particular render kit. For example, f:actionListener
can be used to define a Java method which is executed when the user clicks a button.
–xmlns:p=" http://xmlns.jcp.org/jsf/passthrough"
for using HTML attributes in JSF HTML elements and passing them through to the generated HTML. For example, with p:type
in, <h:inputText p:type="number">
an HTML5 input
type attribute can be created in the generated HTML: <input type="number">
.
–xmlns:c=" http://java.sun.com/jsp/jstl/core"
for the JSTL Core Tag Library providing all kinds of features, like dealing with loops and defining variables. For example, we can use <c:set var="isbn" value="#{book. isbn}"/>
to create a variable named isbn
which can then be used in the view code for conditional expressions.
–xmlns:fn=" http://java.sun.com/jsp/jstl/functions"
for the JSTL Functions Tag Library providing various utility functions, such as string converters. For example, we can use fn:toUpperCase
to convert a string to its uppercase representation.
JavaBean classes, including entity classes, can be used for creating ’managed beans’ with the help of the @ManagedBean
annotation, which allows defining the name of a variable for accessing the created bean in the view code, typically in an EL expression. In our example app, we want to access a Book
bean as well as a BookController
bean, therefore both classes have to be annotated as follows:
Notice how a lifetime scope can be specified for a managed bean with a scope annotation. In our example the book
bean is @RequestScoped
, this means the instance exists as long as the HTTP request and the associated response are being processed. The bookCtrl
bean is @ SessionScoped
, which means it is created when the session starts, and destroyed when the session is closed. Other scopes are available, but in our example we only need these two.
There are multiple ways in which we could compile the source code, create the war file and deploy it to a TomEE server. For example one can use Eclipse IDE77 or Netbeans78, or one can use ANT and create a script that takes care of all these tasks.
We provide an ANT79 script that allows to automatically create the structure of a Java web application, compile the Java source code, build the WAR file and deploy it to a TomEE web server. Our ANT script generates an Eclipse IDE
compatible folder structure, so in case you want to use Eclipse, simply create a project from the existing application code. The ANT installation instructions, detailed description of the available tasks as well as download links are available on our instructions page80.
For our “PublicLibrary” example application, we have used the ANT script and the following steps were needed:
–create the application folder structure: ant create app -Dappname=publicLibrary -Dpkgname=pl.
–compile the Java code and build the war file: ant war -Dappname=publicLibrary.
–deploy the application to TomEE server: ant deploy -Dappname=publicLibrary.
This operation also builds the war file, if it was not already created using the war task, as shown before.
Hint: we do not recommend using spaces in folder names, but if for any reason, the application name needs to contain spaces, then it has to be enclosed in double quotes, e.g. create app -Dpgkname=hw -Dappname="Hellow World"
. In this case it was required to provide the pkgname
parameter, while “Hello World” is not a valid Java identifier, since it contains a white space.
The CRUD use case Create involves creating a new object in main memory and then saving it to persistent storage with the help of the create
method.
The corresponding create
action method code from the src/pl/c/BookController.java
is shown below:
The BookController::create
action method invokes the Book.create
model class method for creating and saving a Book
instance. It returns the name of the view file found in the same folder as the view that triggered the action. This file (create. xhtml
in our case) will be displayed after executing the action. Using the FacesContext
object, the form is cleared after creating a Book
instance. The code of the create
method in src/pl/m/Book.java
is the following:
Now we need to create the facelet template for the view of the Create use case, WebContent/views/books/create.xhtml
. Such a facelet template essentially defines an HTML form with data binding and action binding.
Data binding refers to the binding of model class properties to form (input or output) fields. For instance, in the following facelet code fragment, the entity property book. isbn
is bound to the form input field “isbn”:
In JSF, for the inputText
elements of a form, the id
attribute is used with a given value, e. g., id="isbn"
. The rendered HTML5 input
elements have both, the id
and the name
attributes, and their values are obtained by using the form id and element id values separated by a colon, i. e., id="createBookForm:isbn"
and name="createBookForm:isbn"
.
We can also see a first example of an expression in the Java EE Expression Language (EL), where an expression starts with #
and is enclosed in curly brackets, like # {expression}
. Such an expression allows reading the value of a property of, or invoking a method on, a Java bean or a context object. The value of the expression is assigned to the value
attribute of the generated HTML input
element. The example in our JSF code above is the expression #{book.isbn}
, which retrieves the value of the isbn
property of the book
bean.
Action binding refers to the binding of method invocation expressions to actionable UI elements, where the invoked methods typically are controller action methods, and the actionable UI elements typically are form buttons. For instance, in the following facelet code fragment, the method invocation expression bookCtrl. create(
…)
is bound to the form’s submit button:
After discussing data binding and action binding, it’s time to look at the complete code of the facelet:
This facelet replaces the main
region of the template defined in page.xhtml
, because the name
attribute of the ui:define
element has been set to “main”, but it also replaces the headerTitle
region of the template, which is part of the header
and displays the current operations allowed by the page.
h:outputLabel
elements can be used for creating form field labels, while h: inputText
elements are used for creating HTML input elements. It is possible to specify an HTML5 type of an input
element by using a special namespace prefix (xmlns:p = "http://xmlns.jcp.org/jsf/passthrough"
) for the type
attribute, enforcing it to be ’passed through’. In this way the year
input field can be defined with type number
, so it’s rendered by the corresponding number widget in the browser.
The h:commandButton
element allows creating submit buttons rendered as a input
elements with type="submit"
, and binding them to an action to be performed when the button is clicked. The value of the action
attribute is a method invocation expression. In our Create use case we want that, when the button is clicked, a Book
instance with the property values provided by corresponding form fields is created and saved.
For the Retrieve use case we have to add a method in the controller class (src/pl/ c/BookController.java
file), which reads all Book
records from the books
database table and then delivers this information to the view. The controller action method code is shown below:
The getBooks
method returns a list of Book
instances which are obtained by calling the static retrieveAll
method of the Book
model class. The code of the Book.retrieveAll
method is:
The code is simple, and as already discussed in Section 3.3, it uses a JPQL statement to retrieve the Book
records from the books
table and create the corresponding Book
instances. The EntityManager
object required for being able to perform the JPQL query is passed to Book.retrieveAll
from the BookController
object as discussed in Section 4.1 section.
Now it is the time to define the facelet template for displaying a table with all book records found in the database. The view template files corresponding to the model classes of our app are located in model class subfolders of the WebContent/views/
folder. For the Retrieve/list all books use case, we create a file named retrieveAndListAll.xhtml
in WebContent/views/books/
with the following content:
The ui:composition
element specifies which template is applied (i. e. template="/WEB-INF/templates/page.xhtml"
) and which view block (<ui: define name="main">
) is replaced by this facelet at render time.
The h:dataTable
element defines a table view for a set of records, which is then rendered as an HTML table. Its value
attribute defines a data binding to a record collection, while the var
attribute defines a variable name for iteratively accessing records from this collection. The expression provided in the value
attribute normally specifies a collection-valued property (here: books
) which is accessed via a corresponding getter (here: getBooks
) as defined in the controller class BookController
. In this particular case it is just sufficient to define the getBooks
method since there is no need of a books
property in the controller class. In any case, value
does not allow to invoke a method, so we cannot call getBooks
directly. Instead we have to use the (possibly virtual) property books
,which internally evaluates to a call of getBooks
without checking if a books
property really exists.
The h:button
element allows to create redirect buttons. The value of the outcome
attribute specifies a name of a facelet file (omitting the .xhtml
extension, such that, in the example, the file name is index.xhtml
).
Like for Create, for the Update use case also a controller action method is defined in the BookController
class:
The Book. update
takes care of saving property value changes for a book object identified by its isbn
value as shown below:
Now, we create the view where a Book
can be selected so the user can edit the title
and year
properties, and then save the changes. The code for this view is stored in the WebContent/views/books/update.xhtml
file which has the following content:
In this facelet template, a single selection list (that is, a single-select
HTML element) is created with the help of the JSF element h: selectOneMenu
, where the selection list items (the HTML option
elements) are defined by the JSF elements f: selectItem
or f:selectItems
. The value
attribute of h:selectOneMenu
binds book. isbn
to the value of the selected item (or option
element). The selection list is populated with book records with the help of a f:selectItems
element bound to bookCtrl.books
. The attributes itemLabel
and itemValue
define the option
elements’ text and value.
In the update view, when the user selects a book from the selection list, the form fields are filled with the (ISBN, title and year) property values of the selected book. While the ISBN is immediately available in the view (on the front-end) as the value of the selected option
element, the values of the title
and year
properties have to be fetched from the back-end database. This can be done with the help of the JSF element f:ajax
,which sends an HTTP request message for invoking a remote method, bookCtrl.refreshObject
, on the back-end using XHR. This method takes the managed book
bean, and updates its title
and year
properties with the current values retrieved from the database. Its code is the following:
To enforce a refresh of the HTML form after the user’s selection, such that it displays the values of isbn
, title
and year
, the f:ajax
element allows specifying form fields to be updated with the render
attribute like, in our case, render="isbn title year"
.
Finally, the h:commandButton
element is used for invoking the update
action method of the BookController
with the parameters isbn
, title
and year
, for making the changes persistent.
For the Delete use case, the delete
action method of the BookController
invokes the Book. delete
method by providing the ISBN of the Book
object to be deleted:
The Book. delete
method first retrieves the Book
object to be deleted, and then invokes the entity manager’s remove
method on it:
The view for the Delete action provides a selection list for selecting the book to be deleted. A “Delete” button allows performing the deletion of the selected book. The code of the view in WebContent/views/books/delete.xhtml
is as follows:
As in the Update use case, a h:selectOneMenu
element is used to create and populate a selection list containing all the books to choose from. Clicking on the “Delete” command button results in invoking the delete
action method of the controller with the isbn
value of the selected book, thus resulting in the deletion of the Book
object from the database.
We style the UI with the help of the HTML5 Boilerplate81 CSS and the browser style normalization file normalize.css82. The two CSS files main.css
and normalize. min.css
are located in the WebContent/resources/css
folder.
Adding the CSS to the HTML pages requires to edit the WebContent/WEB-INF/ templates/ page.xhtml
file and add the style
elements referencing the CSS files:
Notice that we also need to add the CSS to the WebContent/views/books/ index.xhtml
file, which is not generated with the page.xhtml
template. The facesContext.externalContext.requestContextPath
expression allows to get the path to WebContent
folder. We then need to append the rest of the path, including the CSS file name, for each of the CSS files.
With JSF, there are two main ways to apply CSS styles to specific elements. For the elements created with JSF, we need to use the @styleClass
attribute, e. g.:
For the HTML elements, we simply use the CSS classes, as in any normal HTML document, e.g.:
For creating advanced user interfaces, additional block elements that act as containers are used for defining paddings, margins, various background colors and other styles. Thus, in various HTML pages you may find DOM structures as shown below, where the div
element containing the @class
attribute is used as container for styling purposes:
You can run the minimal app83(or run the minimal app with CSS84) on our server or download the code85 as a ZIP archive file.
Follow our instructions86 to get your environment prepared for running back-end Java EE web apps with JPA and JSF.
Whenever an app provides public information about entities, such as the books available in a public library, it is desirable to publish this information with the help of self-descriptive resource URLs, such as http://publiclibrary.norfolk.city/books/006251587X
, which would be the resource URL for retrieving information about the book “Weaving the Web” available in the public library of Norfolk. However, resource URLs are not supported by JSF. In the Java world, we would have to use JAX-RS, instead of JSF, for programming a web API with resource URLs. But this would imply that we need to take care of the front-end UI in a different way, since JAX-RS is a pure back-end API, not providing any support for building user interfaces. A natural option would be to use a JavaScript front-end framework, such as BackboneJS or ReactJS, for rendering the UI.
Instead of MySQL, one can use various other database management systems for persistent storage. The following four steps are required to specify the used DBMS (only one DBMS at a time is possible):
–Configure TomEE so it uses the corresponding resource for your application. For a list of resource configuration examples used for common DBMS’s, check Common DataSource Configurations87. For example, if PostgreSQL was chosen as DBMS, then edit the conf/ tomee.xml
file and add the following code:
–Copy the jar file corresponding to the DBMS driver implementation to the lib
folder. After this, the TomEE server needs to be restarted.
–Install the DBMS, if not already installed. Installation instructions are usually available on the corresponding DBMS web page.
–Create the corresponding DBMS user and database to be used for your application.
The code of this app should be extended by adding constraint validation. We show how to do this in Chapter 9.
We briefly discuss three further issues.
Another issue with the code of this Java example app is the repetitious boilerplate code needed per entity class for the storage management methods create
, retrieve
, update
and delete
, and their wrappers in the corresponding controller classes. While it may be instructive to write this code a few times for learning app development, you don’t want to write it again and again later when you work on real projects.
It is desirable that a web app can still be used when the user is offline. However, this is not possible with a back-end web application architecture as implied by Java EE. Offline availability can only be obtained with a truly distributed architecture where essential application components can be executed both on the back-end and on the front-end.
From an architectural point of view, it is important to keep the app’s model classes independent of
We have kept the model class Book
independent of the UI code, since it does not contain any references to UI elements, nor does it invoke any view method. However, for simplicity, we didn’t keep it independent of storage management code. First, due to using JPA annotations, the model class is bound to the JPA object-to-storage mapping technology. Second, we have included the method definitions for create
, update
, delete
, etc., which invoke the storage management methods of a JPA environment’s entity manager, in the model class. Therefore, the separation of concerns is incomplete in our minimal app.
In most parts of the following projects you can follow, or even copy, the code of the book data management app discussed above.
For developing the apps, simply follow the sequence of seven steps described above:
Make sure that international characters are supported by using UTF-8 encoding for all HTML files.
The purpose of the app is managing information about movies. The app deals with just one object type: Movie
, as depicted in the following class diagram:
Notice that in most parts of this project you can follow, or even copy, the code of the book data management app, except that in the Movie
class there is an attribute with range Date
, so you have to find out how to handle such an attribute.
You can use the sample data shown in Table 3.2 for testing your app.
The purpose of the app to be developed is managing information about countries. The app deals with just one object type: Country
, as depicted in the following class diagram:
You can use the sample data shown in Table 3.3 for testing your app.
If you would like to look up the answers for the following quiz questions, you can check our discussion forum88. If you don’t find an answer in the forum, you may create a post asking for an answer to a particular question.
Which of the following are valid type casts in Java?
Complete the following code, so that the Book
JPA entity class contains a property named isbn
, with String
type and playing the role of the standard identifier:
Which of the following classes are valid JavaBean classes:
Complete the following code with the JSF construct which results in using date
as value of the @type
attribute of the rendered input
HTML5 element.:
Consider a JPA entity class named Customer
.Write down the JPQL query to extract all its instances managed by the EntityManager
(e. g., stored in DB or memory storage):