In this chapter, we show how to build a back-end web application with enumeration attributes, using Java with JPA and JSF. In addition to the topic of enumeration attributes, we also show how to deal with multi-valued attributes.
Compared to the Validation App discussed in Chapter 9 we have to deal with the following new issues:
In terms of coding, the new issues are:
1.In the model code we have to take care of
a.enumerations to be defined in the form of Java enum
classes;
b.single-valued enumeration attributes, like Book::originalLanguage
, requiring the JPA annotation @Enumerated for defining the conversion between Java enumeration literals and corresponding database table column values;
c.multi-valued enumeration attributes, like Book::publicationForms
, requiring the JPA annotation @Convert with suitable arguments defining a JPA custom converter for (de‐)serializing collections of Java enumeration literals;
d.extending the methods Book. create
, and Book.update
such that they take care of the enumeration attributes.
2.In the user interface code we have to take care of
a.adding new table columns in retrieveAndListAll.xhtml
;
b.adding suitable choice widgets in create.xhtml
and upate.xhtml
;
c.rendering multi-valued enumeration attributes in the table view of retrieveAndListAll.xhtml
and in the choice widgets of create.xhtml
and upate.xhtml
(with the help of an array of JSF SelectItem
s, each consisting of an enumeration value and its label).
Using the information design model shown in Figure 10.2 above as the starting point, we make an entity class model, essentially by adding getters/setters, constraint annotations and CRUD methods, as explained before:
Simple enumerations, like BookCategoryEL
and PublicationFormEL
, are coded in the following way with the help of Java’s enum
construct:
Notice how the enumeration literals are defined with capitalized names in a comma-separated list.
For a code list, like LanguageEL
, where each enumeration instance consists of a code and a label, we use the codes as enumeration literals and define an attribute label
for storing the labels, as well as a private constructor that allows creating enumeration instances consisting of a code and a label. This is possible because in Java, an enum
is a special kind of class. Each entry in the list of enumeration literals, auch as EN("English")
, represents an invocation of the enum
’s constructor:
The entity class Book
is coded with the help of JPA annotations for class attributes, as well as the setter and getter methods of every class attribute.
For the case of single-valued enumeration attributes (like originalLanguage
and category
), the JPA annotation @Enumerated
is used for specifying the storage serialization. It takes one of the following two parameter values:
–EnumType.STRING
means that enumeration literals are converted to strings when they are serialized ( e.g., in the case of the originalLanguage
attribute of the Book
class, the values saved in the database are one of “EN”, “DE”, “FR” or “ES”) ;
–EnumType.ORDINAL
means that enumeration literals are converted to their index integer when they are serialized (i. e.,the values saved in the database are 1, 2 ,3 etc.).
We store the collection values of a multi-valued enumeration attribute (like otherAvailableLanguages
and publicationForms
) in a database table column as serialized arrays in the form of ["value1", "value2",
…]
. In the case of the originalLanguage
attribute, an example of a saved value would be ["EN", "FR", "DE"]
. Achieving this behavior is possible with JPA by using custom converters to map database table column values to Java types and vice versa. The @Convert
annotation allows specifying a Java converter that is responsible for the mappings.
Also, in the case of a multi-valued enumeration attribute, we use the @Size
annotation to specify the minimum, and if required also the maximum, number of elements stored by this attribute. For example, in the case of publicationForms
,we use @Size( min=1, message = "At least one publication form is required!")
,which enforces to have at least one value for this attribute, otherwise the error message is displayed.
Notice the new methods in the last code block, like getPublicationFormsValues
and getPublicationFormsItems
, which are used for handling the enumeration attributes in the UI. We discuss each of them in the following subsections.
A JPA attribute converter is a special Java class that implements the AttributeConverter
interface with the methods convertToDatabaseColumn
and convertToEntityAttribute
. For the otherAvailableLanguages
attribute, we define the following converter class:
An attribute converter class needs to be annotated with @Converter
. In our example, the convertToDatabaseColumn
method is responsible to convert the entity attribute value (e. g., otherAvailableLanguages
) to a JSON array which is stored in the database as a String
.
The convertToEntityAttribute
method is responsible for de-serializing a table column’s value to the corresponding entity attribute value. In our example, this means to map the JSON array string to a Java Set
of enumeration literals of type LanguageEL
.
The code above shows the custom attribute converter class for the otherAvailableLanguages
attribute. The attribute converter class for the publicationForms
attribute is defined in the same way.
Both for single-valued and for multi-valued enumeration attributes an ordinary setter is defined. In the case of a multi-valued enumeration attribute, this setter assigns an entire set of values (in the form of a Java Set
) to the attribute.
The object serialization function now needs to include the values of enumeration attributes:
As we discussed in Chapter 4, the database schema can be automatically generated by a Java EE server like TomEE. The generated schema for our Book
entity class is like so:
For every attribute (like category
), a column with the same name but using upper case is created, (e. g., CATEGORY
). This is the default naming, which is fine for our example application, but it can be changed, if required, by using the @Column ( name="
…")
annotation, as in @Column( name="book_category")
.
The single-valued and multi-valued enumeration attributes are by default created as varchar
columns with the default maximum length of 255
characters. However, if a longer (or shorter) length is desirable, this can be enforced by using the @Column( length=
…)
annotation.
In the test data objects that are created by Book.createTestData
,we now have to provide values for single- and multi-valued enumeration attributes:
The example app’s user interface for creating a new book record with ISBN, title and four enumeration attributes looks as in Figure 12.2 below.
We use JSF selection lists for rendering the enumeration attributes originalLanguage
and otherAvailableLanguages
in the code of the facelet files WebContent/views/books/create.xhtml
and …/update.xhtml
:
The JSF element h:selectOneMenu
allows creating single selection lists with the HTML select
element. The list is populated with language options due to its child element <f:selectItems value="#{book.languageItems}"/>
. Using the expression #{book.languageItems}
results in calling the method getLanguageItems()
on the book
object. This method returns a set of Select
Item
objects, which are used to populate the selection list. The corresponding method code is as follows:
A multiple selection list, corresponding to an HTML element <select multiple= "multiple"
…/>
, is created with the JSF element h:selectManyListbox
using the same getLanguageItems
method for obtaining the selection list items.
Since the enumeration attributes category
and publicationForms
have not more than seven possible values, we can use a radio button group and a checkbox group for rendering them:
The radio button group is obtained by using the JSF element h:selectOneRadio
. It renders a set of <input type="radio"
… />
elements. Using the same technique as for selection lists, the radio button group is populated with a set of Select
Item
objects. The corresponding getCategoryItems
method from the Book
class is similar to getLanguageItems
.
The checkbox group, consisting of <input type="checkbox"
… />
elements, is created with the JSF element h: selectManyCheckbox
and populated in the same way as a radio button group or a selection list.
In the case of a multi-valued enumeration attribute like otherAvailableLanguages
or publicationForms
, the Retrieve/List All view must show a value in the form of a comma-separated list, like “English, German, Spanish”, as shown in the following table:
For this purpose, we define a method that creates the desired serialization of a multivalued attribute and use it in the code of the facelet file retrieveAndListAll. xhtml
from WebContent/views/books/
. For the publicationForms
attribute, the method code is as follows:
Notice that in the case of the publicationForms
attribute, the underlying enumeration PublicationFormEL
does not have a label property. Instead, we use the enumeration literal name in lowercase as the label.
You can run the enumeration app147 on our server or download the code148 as a ZIP archive file.
In the following practice projects, first make a list of all the constraints that have been defined in this model and express them with validation annotations. Then make an entity class model as a starting for coding the app by following the guidance of this chapter and Chapter 9.
If you have any questions about how to carry out the following projects, you can ask them on our discussion forum149.
The purpose of the app to be built is managing information about movies. The app deals with just one object type, Movie
, and with two enumerations, as depicted in the following class diagram.
You can use the sample data shown in Table 11.1 for testing your app.
The purpose of the app to be built is managing information about countries. The app deals with just one object type, Country
, and with two enumerations, as depicted in the following class diagram.
Compared to the practice project of our validation tutorial, two attributes have been added: the single-valued enumeration attribute code
, which is a “key” attribute (implying a uniqueness constraint), and the multi-valued enumeration attribute religions
.
You can use the sample data shown in Table 11.2 for testing your app.
If you would like to look up the answers for the following quiz questions, you can check our discussion forum150. If you don’t find an answer in the forum, you may create a post asking for an answer to a particular question.
Complete the following JPA custom converter class code, such that it serializes a value of type List<GenreEL>
to String
.:
Complete the definition of the languages
property, such that a minimum of three values are required.
Use the appropriate JPA annotation, including parameter(s), such that the value of the genre
property is serialized and stored as enumeration literal name, i. e., as String
, and not as ordinal values (indexes):
Which of the following is the name of a JSF element that can be used for rendering a choice widget for a single-valued enumeration attribute?
selectOneRadio
selectOnlyOne
radioGroup
radioSelect
Complete the following facelet code, such that the resulting HTML code consists of a checkbox group allowing to select multiple languages, when creating a new Book
instance::