6 Checking Inputs Using Bean Validation

In the requirements for the application, we specified a range of mandatory conditions for domain class attributes. There, we listed conditions such as the following:

Attribute name Condition User Message
name – Name of the campaign Min. 4 characters and max. 30 characters The name of a campaign must be a minimum of 4 characters long and a maximum of 30.

Up until this point, we have used JSF validators (see section about JSF Validation in Cloud Tutorial JSF in a Day) to ensure (to some extent[1]) that the user of the application does not make any inputs that would breach these conditions. However, this has two disadvantages:

  1. The conditins can also be breached in other places in the program code – that is, outside the JSF view. This could occur as a result of a flawed business method or a third system linked via an interface.
  2. The conditions are properties of the domain classes, which means they would be better defined in the entities than in the view.

Since Java EE 6, we have been able to remedy both of these disadvantages using Bean Validation version 1.0. In Java EE 7, the version of this API was updated to 1.1. Hibernate Validator 5.0[2] is the reference implementation. Bean Validation is actually independent from JPA; however, since the changes affect the entity classes, we have decided to include the process in this Cloud Tutorial.

The Bean Validation API provides annotations that can be used to annotate the attributes of JavaBeans directly with the desired conditions. For the condition listed above, we must annotate the name attribute of the Campaign class as follows:

@Size(min=4, max=30, message="The name of a campaign must be a minimum of 4 characters long and a maximum of 30.")

Both JSF and JPA will then check adherence to the defined conditions at runtime. However, they behave in different ways as they do so:

  • If a user enters a value that violates a condition, JSF produces the message specified in the annotation during the validation phase. This validation message is then displayed.
  • JPA generates a so-called ConstraintViolationExceptionat runtime if a condition of the entity to be persisted is not fulfilled. The entity will then not be persisted and an exception will be thrown instead.

Both mechanisms occur in a fully automated manner. Rather than producing lines of code, the progammer is required simply to annotate the attribute of the entity to be checked with an annotation from the Bean Validation API.

Again, we can use the project from the last chapter. If you have already closed it, you can start another temporary session by clicking the following link. Don’t forget that the whole work is volatile unless you sign up to Codenvy and persist the project in your own workspace.

Start Cloud IDE

My-Campaign Persist Donations

6.1 Annotate Entities with Conditions

To prompt the entities to use Bean Validation, individual attributes of the entities must be annotated with annotations from the Bean Validation API. The annotations are generally located in the package javax.validation.constraints. In our sample application, we can transfer a definition of a condition from the requirements directly into a corresponding annotation.

Let’s start with the targetAmount attribute of type Double from the class Campaign.

We’ll start here by adding a @NotNull annotation, which specifies that the value of the attribute can never be null. At runtime, JSF and JPA will then check that the condition has been adhered to.

We’ll also add a @DecimalMin annotation, which makes sure that value of the attribute is greater than or equal to 10.

@NotNull(message = "{campaign.targetAmount.notNull}")
@DecimalMin(value = "10.00", message = "{campaign.targetAmount.decimalMin}")
private Double targetAmount;

In the attribute message, please ensure that you do not specify the output text directly, but rather use curly brackets to set a key that is selected via a language-specific properties file. We will define this file once we’ve finished annotating our entities.

We’ll continue by annotating the attribute donationMinimum. Here, we follow the same procedure as with targetAmount; only the minimum allowed value and the corresponding error message are different:

@NotNull(message = "{campaign.donationMinimum.notNull}")
@DecimalMin(value = "1.00", message = "{campaign.donationMinimum.decimalMin}")
private Double donationMinimum;

Let’s now look at the annotations for the name attribute of type String:

@NotNull
@Size(min = 4, max = 30, message = "{campaign.name.size}")
private String name;

Here, we are using the @Size annotation to set the minimum and maximum lengths for the character string. If the value of the attribute does not fulfil this condition, the specified error message will be generated. A particular featureof this case is that we will not set an error message via the message parameter using the @NotNull annotation. This is because this error message would never be displayed. The reason for this is explained in the shaded box below.

Zero Does Not Equal Empty

If the user does not enter anything within a text field in a JSF view, the validator is not supplied with a null value, but with an empty character (“”). In our code, however, this empty character is caught during the verification performed by the  @Size annotation (since an empty character does not have a length). This means that if a user does not enter a value, a message from the @Size annotation will always be displayed; this, in turn, means that the @NotNull annotation does not require a user-defined message, since it would never be displayed to the user. If we want to specify a particular message to be displayed when the user does not enter anything, we must first of all set the following parameters in the configuration file web.xml:

<context-param>
<param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name>
<param-value>true</param-value>
</context-param>

This will cause empty user inputs to be interpreted as null values, which in turn means that the verification carried out by a @NotNull annotation will be applicable for character strings.

The authors of this book take the view that this represents an inconsistent interaction between JSF and Bean Validation – one that will hopefully be corrected in a future version.

It is worth mentioning that with values of type Double we previously mentioned, this was not an issue – for these values, the converter specified in the view passes/forwards a null value for empty inputs. There is therefore no reason for a user-specific message to be identified.

Annotations for the class Donation present no new issues and are done in exactly the same way as those for the class Campaign. The simplest way to complete this task is to compare them with the conditions for the domain classes.

@NotNull(message = "{donation.amount.notNull}")
@DecimalMin(value = "1.00", message = "{donation.amount.decimalMin}")
private Double amount;

@NotNull
@Size(min = 5, max = 40, message = "{donation.donorName.size}")
private String donorName;

We will also set a @NotNull annotation for the rest of the attributes, so that they are required to have a value in order for the entity to be persisted. While this does not serve to fulfil a condition from the definition of the domain classes, it is advantageous from a technical point of view:

@NotNull
private Boolean receiptRequested;

@NotNull
private Status status;

@NotNull
@Embedded
private Account account;

@NotNull
@ManyToOne
private Campaign campaign;

We are still missing the conditions of the class Account, which can be derived from the conditions of the domain classes on a 1:1 basis:

@NotNull
@Size(min = 5, max = 60, message = "{account.name.size}")
private String name;

@NotNull
@Size(min = 4, max = 40, message = "{account.nameOfBank.size}")
private String nameOfBank;

@NotNull
@Pattern(regexp = "[A-Z]{2}[0-9]{2}[A-Z0-9]{12,30}", message = "{account.iban.pattern}")
private String iban;

A new addition here is the annotation @Pattern, which sets a validation check with a regular expression. The regular expression in question is specified in the parameter regexp. The expression ensures that only one character string can be used in the attribute iban and that this string must correspond to the format of an IBAN[3].

As we have already mentioned, we still need the language-specific properties file for selecting the correct error message. Bean Validation uses its own files to do this, and they always begin with the prefix ValidationMessages.

Listing 6-1 shows the English-language  version of the file. Store this under the name ValidationMessages_en.properties in the directory src/main/resources. Some parameters of the error messages will be specified in curly brackets. These parameters will be replaced at runtime with the value of the attribute of the same name from the Bean Validation annotation. This is good for us as developers, since it means that the property file does not need to be modified if an attribute is changed.

# resource file for bean validation

# campaign model
campaign.name.size=The name of a campaign must be between {min} and {max} characters long.
campaign.targetAmount.notNull=Please specify a target amount.
campaign.targetAmount.decimalMin=The target amount for the campaign must be at least {value} Euro.
campaign.donationMinimum.notNull=Please specify a donation minimum.
campaign.donationMinimum.decimalMin=The donation minimum must be at least {value} Euro.
# donation model
donation.amount.notNull=Please specify a donation amount.
donation.amount.decimalMin=The donation amount must be at least {value} Euro.
donation.donorName.size=The donor's name must be between {min} and {max} characters long.
# account model
account.name.size=The name of the account owner must be between {min} and {max} characters long.
account.nameOfBank.size=The name of a bank must be between {min} and {max} characters long.
account.iban.pattern=An IBAN contains of two letters, followed by two digits and between 12 and 30 alphanumeric characters.

Listing 6-1 English-language version of ValidationMessages

The German-language version of the properties file is given in Listing 6-2. Store this under the name  ValidationMessages_de.properties in the same directory.

# resource file for bean validation

# campaign model
campaign.name.size=Der Name einer Aktion muss min. {min} und darf max. {max} Zeichen lang sein.
campaign.targetAmount.notNull=Bitte ein Spendenziel angeben.
campaign.targetAmount.decimalMin=Das Spendenziel für die Aktion muss min. {value} Euro sein.
campaign.donationMinimum.notNull=Bitte einen Spendenbetrag angeben.
campaign.donationMinimum.decimalMin=Der Spendenbetrag muss min. {value} Euro sein.
# donation model
donation.amount.notNull=Bitte einen Spendenbetrag angeben.
donation.amount.decimalMin=Der Spendenbetrag muss min. {value} Euro sein.
donation.donorName.size=Der Name eines Spenders muss min. {min} und darf max. {max} Zeichen lang sein.
# account model
account.name.size=Der Name des Besitzers eines Kontos muss min. {min} und darf max. {max} Zeichen lang sein.
account.nameOfBank.size=Der Name einer Bank muss min. {min} und darf max. {max} Zeichen lang sein.
account.iban.pattern=Eine IBAN besteht aus zwei Buchstaben, gefolgt von zwei Ziffern und 12 bis 30 alphanumerischen Zeichen.

Listing 6-2 German-lanaguge version of ValidationMessages

After the properties file has been added, the application can be restarted. Try entering a couple of values that would violate the domain conditions. Concentrate on the use case Donate money, since this will not be validated by JSF validators in the view.

You will see that the simple adding of annotations has allowed comprehensive validation of input values and that an appropriate message is displayed in the case of error. But this is not the only benefit: by checking the conditions of Bean Validation though JPA, we can prevent the entities more generally from saving any kind of invalid data.

6.2 Removing Validators From Views

To begin using Bean Validation with all views, any existing validators must be removed from these views. In earlier stages of the application development, for example, we created a small number of validators in the views editCampaign.xhtml and donateMoney.xhtml. These are now obsolete and must be removed without exception save for the view parameter validators. View parameters are independent from the entities and will therefore not be validated by Bean Validation.

To remove these parameters, we must delete the validatorMessage attributes and all tags beginning with f:validate from the above-mentioned views. However, tags within the <f:viewParam> remain unaffected, since this is a view parameter.

The following example – for the input text control a_targetAmount from the view editCampaign.xhtml – demonstrates the process:

<p:inputText id="a_targetAmount"
        value="#{selectedCampaign.targetAmount}"
        validatorMessage="#{msg['editCampaign.target_amount_validation']}">
    <f:convertNumber maxFractionDigits="2" minFractionDigits="2"/>
    <f:validateRequired/>
    <f:validateDoubleRange minimum="10.0"/>
</p:inputText>

turns to:

<p:inputText id="a_targetAmount"
        value="#{selectedCampaign.targetAmount}">
    <f:convertNumber maxFractionDigits="2" minFractionDigits="2"/>
</p:inputText>

This example also illustrates that the properties referenced in the validatorMessage attributes are no longer required – thus, we can also delete these from the files messages_en.properties and messages_de.properties. Instead, error messages are now found in the ValidationMessages properties that were defined in the previous section.

After restarting the application, Bean Validation for JSF will be used for the affected attributes. To do so, JSF accesses information stored in the Bean Validation annotations during the validation phase and uses it to carry out validation checks.

6.3 Automatically Removing Associated Donations When Deleting a Campaign

The deleteCampaign method of the CampaignService, which is be used to delete a Campaign object, has a small problem following the addition of the Bean Validation checks: if the method is executed for a campaign for which donations have already been received, the transaction will be rolled back by a ConstraintViolationException. The reason for this is that newly added @NotNull annotation belonging to the campaign attribute of the class Donation serves to ensure that a donation must always be assigned to a campaign. Here, this condition is violated by the method deleteCampaign, since donations cannot be assigned to a campaign that no longer exists.

Given our current knowledge, we can solve the problem by modifying the method in such a way that all Donation objects assigned to a campaign are iterated prior to the Campaign object being removed.

This is a little cumbersome when we consider that in our case, the necessity of deleting the Donation object is actually a property of the relationship between Campaign objects and Donation objects: a Donation object cannot exist if not assigned to a Campaign object. Specialist literature refers to this type of a relationship as a composition[4].

For this reason, it’s preferable to define in the relationship that the Campaign objects assigned to a campaign should be deleted along with the campaign. Luckily for us, JPA already offers this functionality, and it’s known as cascade delete. To activate it, we need to add the attribute cascade with the value CascadeType.REMOVE to the definition of the relationship (annotation @OneToMany) in the class Campaign. From the definition of the attribute donations:

@OneToMany(mappedBy = "campaign")
private List<Donation> donations;

becomes

@OneToMany(mappedBy = "campaign", cascade = CascadeType.REMOVE)
private List<Donation> donations;

Once the application has been restarted, the transaction will be successfully executed whenever a campaign is deleted and the assigned Donation objects will also be deleted. This happens completely automatically; the developer must simply define the relationship as described above.

For the sake of completeness, it should be mentioned that Delete is not the only operation that can be performed on assigned objects. For other operations, we must specify another value for the enumeration type CascadeType in the attribute cascade. This enumeration type can have the following values:

  • REMOVE (see example above)
  • MERGE
  • PERSIST
  • DETACH
  • REFRESH

We have not yet mentioned Detach or Refresh operations. The former causes an instance managed by the EntityManager to be removed from the memory of the EntityManager object; thereafter, it is no longer a managed instance.

The Refresh operation, meanwhile, causes the specified entity to be reloaded from the database. This operation can only be performed on already-managed entities; otherwise, an exception will be thrown.

The various cascading operations can be combined as desired in the definition of the relationship. This makes it possible to set combinations such as REFRESH and REMOVE. However, if you want to cascade all possible operations at the same time, it is not necessary to list them individually. In this case, you can simply attach the attribute cascade to the value CascadeType.ALL.

A cascading operation also works recursively: if a further entity is assigned to an assigned entity and both relationships have set the same cascading operation, the operation will be extended over two entities. If our Donation object had assigned a further object with CascadeType.REMOVE, this would also be removed at the same time as the higher-level Campaign object.

If you have other problems with your project, please compare it with the project behind the following link:

Start Cloud IDE

My-Campaign Bean Validation

Discussion

Use the message board below to give the authors your feedback or to discuss this page’s topic with other readers (in English please!). Please don’t expect the authors to answer directly, but they might update the content of this site according to your feedback.


  1. We purposefully implemented only a few conditions using the JSF validator, since we knew that this task would later be taken over by the Bean Validation API.
  2. http://www.hibernate.org/subprojects/validator/download
  3. Make sure that the validator only checks the format of the IBAN – not whether the two-digit checksum has been adhered to.  For this purpose, we need to develop a special validator; however, we will not address this during the course of this workshop.
  4. https://en.wikipedia.org/wiki/Class_diagram#Composition