Understanding and Implementing Information Management Concepts and Techniques

Java Back-End Web App Tutorial Part 3: Dealing with Enumerations

Learn how to build a back-end web application with enumeration attributes, using Java with Java Server Faces (JSF) as the user interface technology, the Java Persistence API (JPA) for object-to-storage mapping, and a MySQL database

Gerd Wagner

Mircea Diaconescu

Warning: This tutorial may still contain errors and may still be incomplete in certain respects. Please report any issue to Gerd Wagner at G.Wagner@b-tu.de or Mircea Diaconescu at M.Diaconescu@b-tu.de.

This tutorial is also available in the following formats: PDF. See also the project page, or run the example app from our server, or download it as a ZIP archive file.

This tutorial article, along with any associated source code, is licensed under The Code Project Open License (CPOL), implying that the associated code is provided "as-is", can be modified to create derivative works, can be redistributed, and can be used in commercial applications, but the article must not be distributed or republished without the authors' consent.

2015-07-24

Revision History
Revision 0.320150724gw
improve entity class model, use both simple enums and code list enums
Revision 0.220150630gw
various revisions
Revision 0.120150529md
create first version

Table of Contents

Foreword
1. Enumerations and Enumeration Attributes
1. Enumerations
1.1. Simple Enumerations
1.2. Code Lists
1.3. Record Enumerations
2. Enumeration Attributes
3. Enumerations in Computational Languages
3.1. Enumerations in SQL
3.2. Enumerations in XML Schema
3.3. Enumerations in JavaScript
2. Dealing with Enumerations in Java, JPA and JSF
1. New Issues
2. Make a JPA Entity Class Model
3. Write the Model Code
3.1. Encode the enumerations
3.2. Encode the entity class
3.3. Define a converter for serializing enumeration attribute values
3.4. Encode the enumeration attribute setters
3.5. Write a serialization function
3.6. Database schema
3.7. Creating test data
4. Write the View Code
4.1. Selection lists
4.2. Choice widgets
4.3. Responsive validation for selection lists and choice widgets
5. Show multi-valued properties for the List Objects Use Case
6. Run the App and Get the Code
7. Possible Variations and Extensions
7.1. Ordered-Set-Valued Attributes

List of Figures

1.1. The simple enumeration GenderEL represented as a UML enumeration data type
1.2. The enumeration GenderEL defined as a code list
1.3. A single and a multiple select element with no selected option
1.4. A radio button group
1.5. A checkbox group
2.1. An information design model for the object type Book
2.2. A JPA data model for the object type Book
2.3. The user interface for creating a new book record with ISBN, title and four enumeration attributes
2.4. The user interface for listing all the book records with the corresponding attribute values

List of Tables

1.1. Representing an enumeration of records as a table
1.2. Representing a record enumeration as a table

Foreword

This tutorial is Part 3 of our series of six tutorials about model-based development of back-end web applications with Java, using JPA and JSF. It shows how to build a simple web app where model classes have enumeration attributes that need to be rendered in the user interface in the form of selection lists and other choice widgets.

A distributed web app is composed of at least two parts: a front-end part, which, at least, renders the user interface (UI) pages, and a back-end part, which, at least, takes care of persistent data storage. A back-end web app is a distributed web app where essentially all work is performed by the back-end component, including data validation and UI page creation, while the front-end only consists of a web browser's rendering of HTML-forms-based UI pages. Normally, a distributed web app can be accessed by multiple users, possibly at the same time, over HTTP connections.

In the case of a Java/JPA/JSF back-end app, the back-end part of the app can be executed by a server machine that runs a web server supporting the Java EE specifications Java Servlets, Java Expression Language (EL), JPA and JSF, such as the open source server Tomcat/TomEE.

The app supports the four standard data management operations (Create/Read/Update/Delete). The other parts of the tutorial are:

  • Part 1: Building a minimal app.

  • Part 2: Handling constraint validation.

  • Part 4: Managing unidirectional associations, such as the associations between books and publishers, assigning a publisher to a book, and between books and authors, assigning authors to a book.

  • Part 5: Managing bidirectional associations, such as the associations between books and publishers and between books and authors, also assigning books to authors and to publishers.

  • Part 6: Handling subtype (inheritance) relationships between object types.

Chapter 1. Enumerations and Enumeration Attributes

1. Enumerations

In all application domains, there are string-valued attributes with a fixed set of possible string values. These attributes are called enumeration attributes, and the fixed value sets defining their possible string values are called enumerations. For instance, when we have to manage data about persons, we often need to include information about the gender of a person. The possible values of a gender attribute may be restricted to one of the enumeration labels "male","female" and "undetermined", or to one of the enumeration codes "M", "F" and "U". Whenever we deal with codes, we also need to have their corresponding labels, at least in a legend explaining the meaning of each code.

Instead of using the enumeration string values as the internal values of an enumeration attribute (such as gender), it is preferable to use a simplified internal representation for them, such as the positive integers 1, 2, 3, etc., which enumerate the possible values. However, since these integers do not reveal their meaning (which is indicated by the enumeration label) in program code, for readability we rather use special constants, called enumeration literals, such as MALE or M, prefixed by the name of the enumeration, like GenderEL, in program statements like this.gender = GenderEL.MALE. Notice that we follow the convention that the names of enumeration literals are written all upper case, and that we also use the convention to suffix the name of an enumeration data type with "EL" standing for "enumeration literal" (such that we can recognize from the name GenderEL that each instance of this datatype is a "gender enumeration literal").

There are also enumerations having records as their instances, such that one of the record fields provides the name of the enumeration literals. An example of such an enumeration is the following set of units of measurement:

Table 1.1. Representing an enumeration of records as a table

Units of Measurement
Unit Symbol Unit Name Dimension
m meter length
kg kilogram mass
g gram mass
s second time
ms milisecond time


Notice that since both the "Unit Symbol" and the "Unit Name" fields are unique, either of them could be used for the name of the enumeration literals.

In summary, we can distinguish between the following three forms of enumerations:

  1. simple enumerations define a set of self-explanatory enumeration labels;

  2. code lists define a set of code/label pairs.

  3. record enumerations consist of a set of records, so they are defined like classes with simple attributes defining the record fields.

These three forms of enumerations are discussed in more detail below.

Notice that, since enumerations are used as the range of enumeration attributes, they are considered to be data types.

Enumerations may have further features. For instance, we may want to be able to define a new enumeration by extending an existing enumeration. In programming languages and in other computational languages, enumerations are implemented with different features in different ways. See also the Wikipedia article on enumerations.

1.1. Simple Enumerations

A simple enumeration defines a fixed set of self-explanatory enumeration labels, like in the example of a GenderEL enumeration shown in the following UML class diagram:

Figure 1.1. The simple enumeration GenderEL represented as a UML enumeration data type

The simple enumeration GenderEL represented as a UML enumeration data type

Since the labels of a simple enumeration are being used, in capitalized form, as the names of the corresponding enumeration literals (GenderEL.MALE, GenderEL.FEMALE, etc.), we may also list the (all upper case) enumeration literals in the UML enumeration data type, instead of the corresponding (lower or mixed case) enumeration labels.

1.2. Code Lists

A code list defines a fixed set of code/label pairs. Unfortunately, the UML concept of an enumeration data type does not support the distinction between codes as enumeration literals and their labels. For defining both codes and labels in a UML class diagram in the form of an enumeration data type, we may use the attribute compartment of the data type rectangle and use the codes as attribute names defining the enumeration literals, and set their initial values to the corresponding label. This approach results in a visual representation as in the following diagram:

Figure 1.2. The enumeration GenderEL defined as a code list

The enumeration GenderEL defined as a code list

In the case of a code list, we can use both the codes or the labels as the names of enumeration literals, but using the codes seems preferable for brevity (GenderEL.M, GenderEL.F, etc.). For displaying the value of an enumeration attribute, it's an option to show not only the label, but also the code, like "male (M)", provided that there is sufficient space. If space is an issue, only the code can be shown.

1.3. Record Enumerations

A record enumeration defines a record type with a unique field designated to provide the enumeration literals, and a fixed set of records of that type. In general, a record type is defined by a set of field definitions (in the form of primitive data type attributes), such that one of the unique fields is defined to be the enumeration literal field, and a set of operation definitions.

Unfortunately, record enumerations, as the most general form of an enumeration data type, are not supported by the current version of UML (2.5) where the general form of an enumeration is defined as a special kind of data type (with optional field and operation definitions) having an additional list of unique strings as enumeration literals (shown in a fourth compartment). The UML definition does neither allow designating one of the unique fields as the enumeration literal field, nor does it allow populating an enumeration with records.

Consequently, for showing a record enumeration in a UML class diagram, we need to find a workaround. For instance, if our modeling tools allows adding a drawing, we could draw a rectangle with four compartments, such that the first three of them correspond to the name, properties and operations compartments of a data type rectangle, and the fourth one is a table with the names of properties/fields defined in the second compartment as column headers, as shown in the table below.

Table 1.2. Representing a record enumeration as a table

UnitEL

«el» unitSymbol: String

unitName: String

dimension: String

Unit Symbol Unit Name Dimension
m meter length
kg kilogram mass
g gram mass
s second time
ms milisecond time

2. Enumeration Attributes

An enumeration attribute is an attribute that has an enumeration as its range.

In the user interface, an output field for an enumeration attribute would display the enumeration label, rather than its internal value, the corresponding enumeration index.

Figure 1.3. A single and a multiple select element with no selected option

A single and a multiple select element with no selected option

For allowing user input to an enumeration attribute, we can use the UI concept of a (drop-down) selection list, which may be implemented with an HTML select element, such that the enumeration labels would be used as the text content of its option elements, while the enumeration indexes would be used as their values. We have to distinguish between single-valued and multi-valued enumeration attributes. In the case of a single-valued enumeration attribute, we use a standard select element. In the case of a multi-valued enumeration attribute, we use a select element with the HTML attribute setting multiple="multiple". For both cases, an example is shown in Figure 1.3. While the single select element for "Original language" shows the initially selected option "---" denoting "nothing selected", the multiple select element "Other available languages" shows a small window displaying four of the options that can be selected.

For usability, the multiple selection list can only be implemented with an HTML select element, if the number of enumeration literals does not exceed a certain threshold (like 20), which depends on the number of options the user can see on the screen without scrolling.

Figure 1.4. A radio button group

A radio button group

For user input to a single-valued enumeration attribute, a radio button group can be used instead of a single selection list, if the number of enumeration literals is sufficiently small (say, not larger than 7). A radio button group is implemented with a HTML fieldset element acting as a container of labeled input elements of type "radio", all having the same name, which is normally equal to the name of the represented enumeration attribute.

Figure 1.5. A checkbox group

A checkbox group

For user input to a multi-valued enumeration attribute, a checkbox group can be used instead of a multiple selection list, if the number of enumeration literals is sufficiently small (say, not larger than 7). A checkbox group is implemented with a HTML fieldset element acting as a container of labeled input elements of type "checkbox", all having the same name, which is normally equal to the name of the represented enumeration attribute.

3. Enumerations in Computational Languages

As special data types, enumerations can be defined in information modeling languages (such as UML Class Diagrams), in data schema anguages (such as XML Schema), and in many programming languages (such as C++ or Java).

3.1. Enumerations in SQL

Unfortunately, standard SQL does not support enumerations. Some DBMS, such as MySQL and Postgres, provide their own extensions of SQL column definitions in the CREATE TABLE statement allowing to define enumeration-valued columns.

A MySQL enumeration is specified as a list of enumeration labels with the keyword ENUM within a column definition, like so:

CREATE TABLE people (
    name VARCHAR(40),
    gender ENUM('MALE', 'FEMALE', 'UNDETERMINED')
);

A Postgres enumeration is specified as a special user-defined type that can be used in columns definitions:

CREATE TYPE GenderEL AS ENUM ('MALE', 'FEMALE', 'UNDETERMINED');
CREATE TABLE people (
   name text,
   gender GenderEL
)

3.2. Enumerations in XML Schema

In XML Schema, an enumeration datatype can be defined as a simple type restricting the primitive type xs:string in the following way:

<xs:simpleType name="BookCategoryEL">
  <xs:restriction base="xs:string">
    <xs:enumeration value="NOVEL"/>
    <xs:enumeration value="BIOGRAPHY"/>
    <xs:enumeration value="TEXTBOOK"/>
    <xs:enumeration value="OTHER"/>
  </xs:restriction>
</xs:simpleType>

3.3. Enumerations in JavaScript

We can implement an enumeration in the form of a special JavaScript object definition using the Object.defineProperties method:

var BookCategoryEL = null;
Object.defineProperties( BookCategoryEL, {
  NOVEL: {value: 1, writable: false},
  BIOGRAPHY: {value: 2, writable: false},
  TEXTBOOK: {value: 3, writable: false},
  OTHER: {value: 4, writable: false},
  MAX: {value: 4, writable: false},
  labels: {value:["novel","biography","textbook","other"], 
           writable: false}
});

This definition allows using the enumeration literals BookCategoryEL.NOVEL, BookCategoryEL.BIOGRAPHY etc., standing for the enumeration indexes 1, 2 , 3 and 4, in program statements. Notice how this definition takes care of the requirement that enumeration literals like BookCategoryEL.NOVEL are constants, the value of which cannot be changed during program execution. This is achieved with the help of the property descriptor writable: false in the Object.defineProperties statement.

We can also use a generic approach and define a meta-class Enumeration for creating enumerations in the form of special JS objects:

function Enumeration( enumLabels) {
  var i=0, LBL="";
  this.MAX = enumLabels.length;
  this.labels = enumLabels;
  // generate the enum literals as capitalized keys/properties
  for (i=1; i <= enumLabels.length; i++) {
    LBL = enumLabels[i-1].toUpperCase();
    this[LBL] = i;
  }
  // prevent any runtime change to the enumeration
  Object.freeze( this);
};

Using this Enumeration class allows to define a new enumeration in the following way:

var BookCategoryEL = new Enumeration(["novel","biography","textbook","other"])

Having an enumeration like BookCategoryEL, we can then check if an enumeration attribute like category has an admissible value by testing if its value is not smaller than 1 and not greater than BookCategoryEL.MAX. Also, the label can be retrieved in the following way:

formEl.category.value = BookCategoryEL.labels[this.category - 1];

As an example, we consider the following model class Book with the enumeration attribute category:

function Book( slots) {
  this.isbn = "";     // string
  this.title = "";    // string
  this.category = 0;  // number (BookCategoryEL)
  if (arguments.length > 0) {
    this.setIsbn( slots.isbn); 
    this.setTitle( slots.title); 
    this.setCategory( slots.category);
  }
};

For validating input values for the enumeration attribute category, we can use the following check function:

Book.checkCategory = function (c) {
  if (!c) {
    return new MandatoryValueConstraintViolation(
        "A category must be provided!");
  } else if (!util.isPositiveInteger(c) || 
             c > BookCategoryEL.MAX) {
    return new RangeConstraintViolation(
        "The category must be a positive integer " +
        "not greater than "+ BookCategoryEL.MAX +" !");
  } else {
    return new NoConstraintViolation();
  }
};

Notice how the range constraint defined by the enumeration BookCategoryEL is checked: it is tested if the input value c is a positive integer and if it is not greater than BookCategoryEL.MAX.

Chapter 2. Dealing with Enumerations in Java, JPA and JSF

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.

We again consider the simple data management problem that was considered in Part 2 of this tutorial, but now we have four additional enumeration attributes, as shown in the UML class diagram in Figure 2.1 below:

  1. the single-valued mandatory attribute originalLanguage with the enumeration datatype LanguageEL as its range,

  2. the multi-valued optional attribute otherAvailableLanguages with range LanguageEL,

  3. the single-valued mandatory attribute category with range BookCategoryEL

  4. the multi-valued mandatory attribute publicationForms with range PublicationFormEL

Figure 2.1. An information design model for the object type Book

An information design model for the object type Book

Notice that the attributes otherAvailableLanguages and publicationForms are multivalued, as indicated by their multiplicity expressions [*] and [1..*]. This means that the possible values of these attributes are sets of enumeration literals, such as the set {ePub, PDF}, which can be represented in Java as a corresponding set, or array list, of enumeration literals.

1. New Issues

Compared to the Validation App discussed in Part 2 we have to deal with the following new issues:

  1. Enumeration datatypes have to be defined in a suitable way as part of the model code.

  2. Enumeration attributes have to be defined in model classes and handled in the user interface.

In terms of coding, the new issues are:

  1. In the model code we have to take care of

    1. enumeration datatypes to be defined in the form of Java enum classes;

    2. single-valued enumeration attributes (like Book::originalLanguage) requiring specific JPA annotations;

    3. multi-valued enumeration attributes (like Book::publicationForms) requiring specific JPA annotations and mapping methods for serializing their values in the form of suitable strings;

    4. extending the methods Book.add, and Book.update such that they take care of the added enumeration attributes.

  2. In the user interface code we have to take care of

    1. adding new table columns in listAll.xhtml;

    2. adding suitable form controls (such as selection lists, radio button groups or checkbox groups) in create.xhtml and upate.xhtml;

    3. rendering multi-valued enumeration attributes in the table view of listAll.xhtml and in the choice widgets of create.xhtml and upate.xhtml (with the help of an array of JSF SelectItems each consisting of an enumeration value and its label).

2. Make a JPA Entity Class Model

Using the information design model shown in Figure 2.1 above as the starting point, we make a JPA entity class model, essentially by adding JPA annotations, setters, and getters as explained in Part 2:

Figure 2.2. A JPA data model for the object type Book

A JPA data model for the object type Book

3. Write the Model Code

How to Encode a JPA Entity Class Model

3.1. Encode the enumerations

Simple enumerations, like BookCategoryEL and PublicationFormEL, are encoded in the following way with the help of Java's enum construct:

public enum BookCategoryEL {
  NOVEL, 
  BIOGRAPHY, 
  TEXTBOOK, 
  OTHER;
}

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:

public enum LanguageEL {
  EN( "English"),
  DE( "German"),
  FR( "French"),
  ES( "Spanish");

  private final String label;
  private LanguageEL( String label) {
    this.label = label;
  }
  public String getLabel() {
    return this.label;
  }
}

3.2. Encode the entity class

The entity class Book is encoded with the help of JPA annotations for class attributes, as well as the setter and getter methods of every class attribute.

@Entity @Table( name="books")
@ViewScoped @ManagedBean( name="book")
public class Book {
  @Id @Column( length=10)
  @NotNull( message="An ISBN value is required!")
  @Pattern( regexp="\\b\\d{9}(\\d|X)\\b", message = "...")
  private String isbn;
  
  @Column( nullable=false)
  @NotNull( message="A title is required!")
  private String title;

  @Column( nullable=false)
  @Enumerated( EnumType.STRING)
  @NotNull( message="An original language is required!")
  private LanguageEL originalLanguage;

  @Column( nullable=false)
  @Enumerated( EnumType.STRING)
  @NotNull( message="A category is required!")
  private BookCategoryEL category;

  @Convert( converter = 
      pl.model.converter.OtherAvailableLanguagesConverter.class)
  private Set<LanguageEL> otherAvailableLanguages;
 
  @Column( nullable=false)
  @Convert( converter = 
      pl.model.converter.PublicationFormsConverter.class)
  @Size( min=1, message="At least one publication forms is required!")
  private Set<PublicationFormEL> publicationForms;

  ...
}

For the case of single-valued enumeration attributes (like originalLanguage and category), the JPA annotation @Enumerated is used for specifying the type of value used when enumeration literals are serialized for being stored in a database table column. It accepts as parameter one of the following two values:

  • EnumType.STRING - the enumeration literal converted to string is used when the attribute value needs to be stored in the database ( e.g., for the originalLanguage attribute of the Book class, the values saved in the database are one of EN, DE, FR or ES) ;

  • EnumType.ORDINAL - the corresponding ordinal value of the enumeration literal is used when the attribute value needs to be stored in the database (i.e., the positive integers correpsonding to the enumeration literal).

We store the collection values of a multi-valued enumeration attribute (like otherAvailableLanguages and publicationForms) in a database table column as serialized arrays, i.e., in the form of [ "value1", "value2", ...], which for example in the case of originalLanguage attribute, a possible saved value can 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 attribute annotation specifies a Java converter that is responsible for the mappings with the converter parameter.

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. This is used as one of the validation which applies to the attribute, and create the corresponding error messages. For example, in the case of publicationForms, we use @Size( min=1, message = "At least one publication forms is required!"), which enforces to have at least one value for this attribute, otherwise the defined custom error message is used.

@Entity @Table( name="books")
@ViewScoped @ManagedBean( name="book")
public class Book {
  ...
  public Book( String isbn, String title, Integer year, 
      LanguageEL originalLanguage, 
      Set<LanguageEL> otherAvailableLanguages, 
      BookCategoryEL category, 
      Set<PublicationFormEL> publicationForms) { ...}

  public LanguageEL getOriginalLanguage() {...}
  public void setOriginalLanguage( LanguageEL originalLanguage) {...}  
  public Set<LanguageEL> getOtherAvailableLanguages() {...}
  public void setOtherAvailableLanguages( 
      Set<LanguageEL> otherAvailableLanguages) {...}
  public BookCategoryEL getCategory() {...}
  public void setCategory( BookCategoryEL category) {...}  
  public Set<PublicationFormEL> getPublicationForms() {...}
  public void setPublicationForms( 
      Set<PublicationFormEL> publicationForms) {...}

  public static Book retrieve(...) {...}
  public static List<Book> retrieveAll(...) {...}
  public static void add(...) throws Exception {...}
  public static void update(...) throws Exception {...}
  public static void destroy(...) throws Exception {...}

  public String getOtherAvailableLanguagesValues() {...}
  public SelectItem[] getLanguageItems() {...}
  public SelectItem[] getCategoryItems() {...}
  public String getPublicationFormsValues() {...}
  public SelectItem[] getPublicationFormsItems() {...}
}

The Book constructor is extended with the new parameters corresponding to the new enumeration attributes. Notice the new annotations, used for the single and multivalued enumeration attributes, as well as the additional methods (e.g., getPublicationFormsValues and getPublicationFormsItems) used with the purpose to fill with data the UI elements, corresponding to the enumeration attributes and to display multi-valued enumeration as string serializations on the view tables. We discuss each of these additional parts in the following sections.

3.3. Define a converter for serializing enumeration attribute values

A JPA attribute converter is a special Java class that implements the AttributeConverter interface with the methods convertToDatabaseColumn and convertToEntityAttribute. For the case of otherAvailableLanguages attribute, we define the following converter class:

@Converter
public class OtherAvailableLanguagesConverter implements 
    AttributeConverter<Set<LanguageEL>, String> {
  @Override
  public String convertToDatabaseColumn( Set<LanguageEL> attrValue) {
    JSONArray jsonArr = new JSONArray( attrValue);
    return jsonArr.toString();
  }
  @Override
  public Set<LanguageEL> convertToEntityAttribute( String colValue) {
    Set<LanguageEL> result = new HashSet<LanguageEL>();
    try {
      JSONArray jsonArr = new JSONArray( colValue);
        for ( int i = 0, length = jsonArr.length(); i < length; i++) {
          result.add( LanguageEL.valueOf( jsonArr.getString( i)));
        }
    } catch ( JSONException e) {
      e.printStackTrace();
    }
    return result;
  }
}

An attribute converter class needs to be annotated with @Converter. Java generics are used to specify the types used for the data mappings, e.g., Set<LanguageEL> for the JavaBean code and String for the database column.

In our case, the required convertToDatabaseColumn method is responsible to serialize the JavaBean attribute value (e.g., otherAvailableLanguages) to a JSON array which is stored in the database as a String serialization. The corresponding method body uses the Java built-in JSON capabilities obtain the final serialization of the JSON array as String.

The convertToEntityAttribute method is responsible to obtain the JavaBean attribute value from the corresponding table column stored value. In our case, this means to map the JSON array serialized as string to a Java Set of LanguageEL literal values. In the same way, as for the case of convertToDatabaseColumn, the Java built-in JSON capabilities are used together with the Java enumeration specific methods to perform the de-serialization.

The above code shows how to create the custom attribute converter class for the otherAvailableLanguages attribute, but in exactly the same way is defined also the attribute converter class for publicationForms attribute. The only differences are in the used Java generic types (PublicationFormEL in place of LanguageEL) and the parameter types of convertToDatabaseColumn, i.e., public String convertToDatabaseColumn( Set<PublicationFormEL> attrValue).

3.4. Encode the enumeration attribute setters

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.

3.5. Write a serialization function

The object serialization function now needs to include the values of enumeration attributes:

public String toString() {
  int i = 0, n = this.publicationForms.size();
  String result = "{ isbn: '" + this.isbn + "', title:'" + this.title
    + "', year: " + this.year + ", originalLanguage: '" + this.originalLanguage.getLabel();
  result += "', otherAvailableLanguages: [";
  for ( LanguageEL oal : this.otherAvailableLanguages) {
    result += "'" + oal.getLabel() + "'";
    if ( i < n - 1) {
      result += ", ";
    }
    i++;
  }
  result += "]";
  result += ", category: '" + this.category.name().toLowerCase();
  result += "', publicationForms: [";
  i = 0;
  for ( PublicationFormEL pf : this.publicationForms) {
    result += "'" + pf.name().toLowerCase() + "'";
    if ( i < n - 1) {
      result += ", ";
    }
    i++;
  }
  result += "]}";
  return result;
}

Notice that for multi-valued enumeration attributes we can't use the toString() function because this will give the default Set serialization which is not what we like to have (is not really human readable).

3.6. Database schema

As we discussed in Part 1, the database schema can be automatically generated by a JPA-enabled server like TomEE. The generated schema for our Book entity class is like so:

CREATE TABLE IF NOT EXISTS `books` (
  `ISBN` varchar(10) NOT NULL,
  `CATEGORY` varchar(255) NOT NULL,
  `ORIGINALLANGUAGE` varchar(255) NOT NULL,
  `OTHERAVAILABLELANGUAGES` varchar(255) DEFAULT NULL,
  `PUBLICATIONFORMS` varchar(255) NOT NULL,
  `TITLE` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

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.

3.7. Creating test data

In the test data records that are created by Book.createTestData, we now have to provide values for single- and multi-valued enumeration attributes:

public static void createTestData( EntityManager em, UserTransaction ut)
    throws NotSupportedException, SystemException, IllegalStateException,
    SecurityException, HeuristicMixedException, HeuristicRollbackException, 
    RollbackException {
  Book book = null;
  // first clear existing books
  Book.clearData( em, ut);
  ut.begin();
  Book book = new Book( "006251587X", "Weaving the Web", 2000, 
      LanguageEL.EN,
      new HashSet<LanguageEL>() {
        { add( LanguageEL.DE); add( LanguageEL.FR);}
      }, 
      BookCategoryEL.TEXTBOOK, 
      new HashSet<PublicationFormEL>() {
        { add( PublicationFormEL.HARDCOVER);
          add( PublicationFormEL.PDF);}
      }
  );
  em.persist( book);	
}

4. Write the View Code

The example app's user interface for creating a new book record looks as in Figure 2.3 below.

Figure 2.3. The user interface for creating a new book record with ISBN, title and four enumeration attributes

The user interface for creating a new book record with ISBN, title and four enumeration attributes

4.1. Selection lists

We use JSF selection lists for rendering the enumeration attributes originalLanguage and otherAvailableLanguages. The affected files are WebContent/views/books/create.xhtml and WebContent/views/books/update.xhtml:

<ui:composition template="/WEB-INF/templates/page.xhtml">
  <ui:define name="content">
    <h:form id="createBookForm" styleClass="pure-form pure-form-aligned">
      <h:panelGrid columns="3">
        ...    
        <h:outputLabel for="originalLanguage" value="Original language " />
        <h:selectOneMenu id="originalLanguage" value="#{book.originalLanguage}">
          <f:selectItem itemValue="" itemLabel="---" />
          <f:selectItems value="#{book.languageItems}" />
        </h:selectOneMenu>
        <h:message id="originalLanguageMessages" for="originalLanguage" errorClass="error" />
        <h:outputLabel for="otherAvailableLanguages" value="Other available languages " />
        <h:selectManyListbox id="otherAvailableLanguages" value="#{book.otherAvailableLanguages}">
    	<f:selectItems value="#{book.languageItems}" />
        </h:selectManyListbox>
        <h:message id="otherAvailableLanguagesMessages" for="otherAvailableLanguages" errorClass="error" />
        ...
      </h:panelGrid>
      ...
    </h:form>
  </ui:define>
</ui:composition>

The JSF h:selectOneMenu allows to create single selection lists. The rendered view uses the HTML5 select element. The list is auto-populated with the existing language items, as result of using <f:selectItems value="#{book.languageItems}"/>. The JSF expression #{book.languageItems} results in calling a method named getLanguageItems of the Book instance (a getter for the specified attribute). Notice that it is not mandatory to define the languageItems attribute in the JavaBean class Book (and in our case this attribute is not even needed), but we need to define a method named getLanguageItems. This method returns a set of SelectItem elements which are used to populate our selection list. The corresponding method code is shown below:

public SelectItem[] getLanguageItems() {
  SelectItem[] items = new SelectItem[LanguageEL.values().length];
  int i = 0;
  for ( LanguageEL lang : LanguageEL.values()) {
    items[i++] = new SelectItem( lang.name(), lang.getLabel());
  }
  return items;
} 

For the multiple selection list, the h:selectManyListbox JSF element is used. It uses the same getLanguageItems method to obtain the values used to populate the list. The corresponding rendered HTML5 code uses the select element with the multiple attribute set, i.e., <select multiple="multiple" .../>.

4.2. Choice widgets

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:

<ui:composition template="/WEB-INF/templates/page.xhtml">
  <ui:define name="content">
    <h:form id="createBookForm" styleClass="pure-form pure-form-aligned">
      <h:panelGrid columns="3">
          ...
        <h:outputLabel for="category" value="Category" />
        <h:selectOneRadio id="category" value="#{book.category}">
          <f:selectItems value="#{book.categoryItems}" />
        </h:selectOneRadio >
        <h:message id="categoryMessages" for="category" errorClass="error" />
        <h:outputLabel for="publicationForms" value="Publication forms " />
        <h:selectManyCheckbox id="publicationForms" value="#{book.publicationForms}">
   	<f:selectItems value="#{book.publicationFormsItems}" />
        </h:selectManyCheckbox>
        <h:message id="publicationFormsMessages" for="publicationForms" errorClass="error" />
      </h:panelGrid> 
      ...
    </h:form>
  </ui:define>
</ui:composition>

The radio button group is obtained by using the h:selectOneRadio JSF element. It renders in HTML5 as a set of <input type="radio" name="groupName" ... /> elements. Using the same technique as for selection lists, the group of radio buttons is created with the help of a set of SelectItem objects, which contains the required data. The corresponding getCategoryItems method from the Book class has a very similar code as getLanguageItems one.

The checkbox group is created by using the h:selectManyCheckbox JSF element, and populated in the same way as for the category attribute case, by using a getPublicationFormsItems. The resulting HTML5 code uses <input type="checkbox" ... /> elements.

4.3. Responsive validation for selection lists and choice widgets

As for all the other properties, the JPA annotations are responsible for validation and creation of custom error messages. For example, in the case of publicationForms attribute, we have the following code:

@Convert( converter = pl.model.converter.PublicationFormsConverter.class)
@Size( min = 1, message = "At least one publication forms is required!")
private Set<PublicationFormEL> publicationForms;

While the @Convert annotation is not used for validation but rather is responsible for the serialization and deserialization tasks, the @Size annotation with a min = 1 parameter enforce to have at least one element in multivalued publicationForms attribute, otherwise the custom message defined by using the message = "At least one publication forms is required!" annotation property is shown as an error in the corresponding HTML5 view.

5. Show multi-valued properties for the List Objects Use Case

In the case of multi-valued properties, (e.g., otherAvailableLanguages), the List All view must show a value in the form of value1, value2, ..., valueN, (e.g., English, German, Spanish), as shown in Figure 2.4 below.

Figure 2.4. The user interface for listing all the book records with the corresponding attribute values

The user interface for listing all the book records with the corresponding attribute values

For this case, we define a method which creates the desired multi-valued attribute string serialization, and we use it within the JSF WebContent/views/books/listAll.xhtml view file. For the case of of otherAvailableLanguages attribute, the following method is defined, but the same technique applies also for the publicationForms attribute:

public String getPublicationFormsValues() {
  String result = "";
  if ( this.publicationForms != null) {
    int i = 0, n = this.publicationForms.size();
    for ( PublicationFormEL pf : this.publicationForms) {
      result += pf.name().toLowerCase();
      if ( i < n - 1) {
        result += ", ";
      }
      i++;
    }
  }
  return result;
}

Notice that for the case of PublicationFormEL enumeration we do not use a label property, instead we use the enumeration literal name in lowercase as the label. The method code creates the string which contains the serialization as is should be shown by the UI. In the view code, the method is used as shown below:

<ui:composition template="/WEB-INF/templates/page.xhtml">
  <ui:define name="content">
    <h:dataTable value="#{bookController.books}" var="b">
      ...
      <h:column>
        <f:facet name="header">Other available languages</f:facet>
        #{b.otherAvailableLanguagesValues}
      </h:column>
      ...
    </h:dataTable>
    <h:button value="Main menu" outcome="index" />
  </ui:define>
</ui:composition>

The JSF expression, #{b.otherAvailableLanguagesValues}, is used to invoke the getPublicationFormsValues method and it uses the returned string serialization to display the value in the view.

6. Run the App and Get the Code

Running your application is simple. First stop (if already started, otherwise skip this part) your Tomcat/TomEE server by using bin/shutdown.bat for Windows OS or bin/shutdown.sh for Linux. Next, download and unzip the ZIP archive file containing all the source code of the application and also the ANT script file which you have to edit and modify the server.folder property value. Now, execute the following command in your console or terminal: ant deploy -Dappname=enumerationapp. Last, start your Tomcat web server (by using bin/startup.bat for Windows OS or bin/startup.sh for Linux). Please be patient, this can take some time depending on the speed of your computer. It will be ready when the console display the following info: INFO: Initializing Mojarra [some library versions and paths are shonw here] for context '/validationapp'. Finally, open a web browser and type: http://localhost:8080/enumerationapp/faces/views/books/index.xhtml

You may want to download the ZIP archive containing all the dependency libaries, or run the enumeration app directly from our server.

7. Possible Variations and Extensions

7.1. Ordered-Set-Valued Attributes

In cases where the order of the set of values of a multi-valued attribute, such as Person::phones, is significant, e.g. because it expresses a priority ordering, the collection value of such an attribute represents an ordered set. We use List as the collection type of an ordered-set-valued attribute and apply the @OrderColumn annotation for creating a sequence number column in the table used for storing the values of the multi-valued attribute, like in the follwoing example:

@Entity @Table( name="people")
@ViewScoped @ManagedBean( name="person")
public class Person {
  ...
  @OrderColumn
  @ElementCollection 
  private List <String> phones;
  ...
}