In this section we show how to
configure an app to connect with a database in a controller class,
obtain the EntityManager
and UserTransaction
instances required
for performing database operations,
wrap an app in a WAR file and deploy it to a web server for execution.
A controller class contains the code that glues the views to the model, as well as all
methods that do no 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.ctrl.BookController
in
the src/pl/ctrl
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:
EntityManagerFactory emf = Persistence.createEntityManagerFactory("MinimalApp"); EntityManager em = emf.createEntityManager(); EntityTransaction et = em.getTransaction();
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.ctrl.BookController
) since the
controller is responsible for managing the database connections.
public class BookController { @PersistenceContext( unitName="MinimalApp") private EntityManager em; @Resource() UserTransaction ut; public List<Book> getBooks() {...} public void refreshObject( Book book) {...} public String add( String isbn, String title, int year) {...} public String update( String isbn, String title, int year) {...} public String destroy( String isbn) {...} }
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:
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.1" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <persistence-unit name="MinimalApp"> <class>pl.model.Book</class> <properties> <!-- Request auto-generation of the database schema --> <property name="javax.persistence.schema-generation.database.action" value="create"/> <!-- Use the JPA annotations for creating the database schema --> <property name="javax.persistence.schema-generation.create-source" value="metadata"/> </properties> <jta-data-source>jdbc/MinimalApp</jta-data-source> </persistence-unit> </persistence>
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:
One or more class
elements, each one containing the full qualified name of
an entity class of the app (like pl.model.Book
in our example app).
A set of configuration property
elements used for providing further
configuration settings.
A 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 DDL 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:
<?xml version="1.0" encoding="UTF-8"?>
<TomEE>
<Resource id="MinimalApp" type="DataSource">
JdbcDriver com.mysql.jdbc.Driver
JdbcUrl jdbc:mysql://localhost:3306/MinimalApp
UserName MinimalApp
Password MinimalApp
JtaManaged true
</Resource>
</TomEE>
The Resource
element contains the information required to connect with the
database (i.e. username, password, access URL and the Java class name of the connection
driver).
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:
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html"> <h:head> <title><ui:insert name="title">Public Library</ui:insert></title> <link href="#{facesContext.externalContext.requestContextPath}/resources/css/style.css" rel="stylesheet" type="text/css" /> </h:head> <body> <div id="header"> <ui:insert name="header"> <ui:include src="/WEB-INF/templates/header.xhtml"/> </ui:insert> </div> <div id="main"> <ui:insert name="main"/> </div> <div id="footer"> <ui:insert name="footer"> <ui:include src="/WEB-INF/templates/footer.xhtml"/> </ui:insert> </div> </body> </html>
In the code, one can see that some HTML elements are used (e.g., title
,
link
or div
) while others like h:head
and
ui:insert
are not HTML elements, but have been defined by JSF in different
namespaces. JSF is using its own head element h:head
because it allows injecting
special HTML code such as script
elements needed for XHR (or "AJAX") messaging.
Notice that in the main template, we have a first example of an expression using JSF's
Expression Language (EL). where an expression starts with #
and is encapsulated between 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. In any case, the value of the expression will be inserted into the HTML generated from
the template. The example in our main template is the expression
#{facesContext.externalContext.requestContextPath}
, which retrieves the value of
the requestContextPath
property of the context object
facesContext.externalContext
.
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
following:
<div><h2>Public Library</h2></div>
The footer.xhtml
sub-template contains the
following::
<div>Copyright 2014-2015, Gerd Wagner and Mircea Diaconescu</div>
The main region is dynamic, and will be replaced with the content generated by a facelet template as shown below.
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:define
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:
@Entity @Table( name="books") @RequestScoped @ManagedBean( name="book") public class Book { ... } @SessionScoped @ManagedBean( name="bookCtrl") public class BookController { ... }
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 we
only need these two scopes in this tutorial.
In this tutorial we show how to use an ANT script for generating the structure of a Java web app, and then compile the code, build the WAR file and deploy it to a TomEE web server. One may also use Eclipse (or NetBeans or other IDEs) for doing this, but for keeping it simple we use ANT. Our ANT script generates a folder structure, which is compatible with Eclipse, so in case you want to use Eclipse, you may simply create an Eclipse project from the existing application code.
The purpose of this section is only to show you how to use our ANT script for making your life easier. It is not intended to be an ANT tutorial, so we don't get into specific ANT details. The following ANT tasks are defined in the script:
create app -Dappname=yourAppName -Dpkgname=yourAppPackageName
allows creating the folder structure. Instead of yourAppName
and
yourAppPackageName
you have to provide your app's name and its package name.
In our example app, we invoke the task with ant create app -Dappname=publicLibrary
-Dpkgname=pl
.
The script creates the folder structure, as well as the required files
src/META-INF/persistence.xml
, WEB-INF/faces-config.xml
and
WEB-INF/web.xml
. The parameter yourAppPackageName
is used to
create the Java top level packages. If omitted, yourAppName
is used as Java top
package name instead. For the next tasks/commands you have to be sure that the ANT script
file is located in the same folder as your web application folder (and not one level deeper
in the web application folder). This way, one can use the same ANT script for building
multiple web applications.
Using the optional parameter, -Dforce=true
will overwrite an existing
application by first deleting the existing application folder.
Hint: a JPA/JSF application requires a set of libraries to run. The ANT script looks
for the jar
files in a folder named lib
located on the same folder
as the script itself. The location of the jar files can be modified by editing the ANT
script and setting the lib.folder
parameter to the right folder on your
computer. You can download the dependency JAR files with the link provided at the end of
this tutorial.
build war -Dappname=yourAppName
allows building the WAR file by using yourAppName
as file name. The
resulting WAR file will be in the same folder as the ANT script file. For our example app we
use the following command:
ant war -Dappname=publicLibrary
Hint: before being able to use this command, you have to edit the ANT script and modify
the value of the server.folder
parameter so it points to your TomEE
installation folder. In case that you get compilation errors, try to copy the
mysql-connector-java-xxxx-bin.jar
file to the lib
folder of your
TomEE installation folder. This file and some other dependency files are provided in a ZIP
archive that can be downloaded with the link provided at the end of this tutorial.
deploy -Dappname=yourAppName
allows deploying the WAR file associated with yourAppName
to the TomEE web
server. It automatically executes the build war -Dappname=yourAppName
command,
which means the WAR file is built before the deploy. The location of the deploy folder is
detected by using the server.folder
property, by appending the
webapps
folder name. For our example app we invoke the following command:
ant deploy -Dappname=publicLibrary
.
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 -Dappname="Hellow World"
.