Annotations are the tool that Java provides to define metadata in your
applications. In other words, it is the way to do declarative development
in Java, where you refer to “what” and not the “how” part.
In this lesson you will see the annotations you can use inside an OpenXava
application to define validations, user interface and some other aspects
to fit your application to your needs.
The goal of this lesson is to introduce you to these annotations. On the
other hand this lesson does not show you all the intricacies and use cases
of all the annotations. The comprehensive coverage of all the annotations
is out of the scope of this course.
OpenXava includes an easy to use and extensible validation framework.
Moreover OpenXava supports
The preferred way to do validation in OpenXava is by means of annotations,
i.e. in a declarative way. For example, you only have to mark a property
as
) are
recognized by OpenXava, so you can use all the built-in Bean Validation
annotations in your OpenXava applications:
Since OpenXava 6.1, Bean Validation 2.0 is supported, so we have available
the next annotations from
You can also the Hibernate
Validator annotations (
org.hibernate.validator.constraints):
Annotation
|
Apply on
|
Checking
|
@Length(min=, max=) |
Property (string)
|
Checks if the string length matches the range
|
@NotEmpty Deprecated use the standard @NotEmpty instead
|
Property (string, array, collection, map)
|
Checks if the value is not null nor empty
|
@Range(min=, max=)
|
Property (numeric or string representation of a numeric)
|
Checks if the value is between min and max (included)
|
@Email Deprecated use the standard @Email instead
|
Property (string)
|
Checks whether the string conforms to the email address
specification
|
@CreditCardNumber(ignoreNonDigitCharacters=)
|
Property (string)
|
Checks whether the string is a well formated credit card
number (derivative of the Luhn algorithm)
|
@EAN
|
Property (string)
|
Checks whether the string is a properly formated EAN or UPC-A
code
|
@LuhnCheck(startIndex=, endIndex=, checkDigitIndex=,
ignoreNonDigitCharacters=)
|
Property (string)
|
Checks that the digits within the annotated character sequence
pass the Luhn checksum algorithm
|
@Mod10Check(multiplier=, weight=, startIndex=, endIndex=,
checkDigitIndex=, ignoreNonDigitCharacters=)
|
Property (string)
|
Checks that the digits within the annotated character sequence
pass the generic mod 10 checksum algorithm
|
@Mod11Check(threshold=, startIndex=, endIndex=,
checkDigitIndex=, ignoreNonDigitCharacters=, treatCheck10As=,
treatCheck11As=)
|
Property (string)
|
Checks that the digits within the annotated character sequence
pass the mod 11 checksum algorithm
|
@NotBlank Deprecated use the standard @NotBlank instead
|
Property (string)
|
Checks that the annotated character sequence is not null and
the trimmed length is greater than 0
|
@SafeHtml(whitelistType=, additionalTags=,
additionalTagsWithAttributes=)
|
Property (string)
|
Checks whether the annotated value contains potentially
malicious fragments such as <script/>
|
@ScriptAssert(lang=, script=, alias=)
|
Property (any type)
|
Checks whether the given script can successfully be evaluated
against the annotated element
|
@URL(protocol=, host=, port= regexp=, flags=)
|
Property (string)
|
Checks if the annotated character sequence is a valid URL
according to RFC2396
|
Since OpenXava 6.1, Hibernate Validator 6.0 is supported, so we have
available the next annotations from
org.hibernate.validator.constraints
too:
Annotation
|
Apply on
|
Checking
|
@CodePointLength
|
Property (string) |
Checks if the code point length is between min and max
included |
@Currency
|
Property (MonetaryAmount of javax.money)
|
Checks if the MonetaryAmount has to be in the right
CurrencyUnit |
@ISBN
|
Property (string) |
Checks that the annotated character sequence is a valid ISBN.
The length of the number and the check digit are both verified |
@UniqueElements
|
Property (collection)
|
Checks that every object in the provided Collection is unique,
i.e. that we can't find 2 equal elements in the collection.
|
Custom
validation
It is very easy to add your own validation logic to your entity because
the
@PropertyValidator,
@EntityValidator and
@RemoveValidator
annotations allows you to indicate a class (the validator) with the
validation logic.
For example, if you want a custom logic to validate an
unitPrice
property, you have to write something like the code:
@PropertyValidator(UnitPriceValidator.class) // Contains the validation logic
private BigDecimal unitPrice;
And now you can write the logic you want inside
UnitPriceValidator
class:
public class UnitPriceValidator
implements IPropertyValidator { // Must implement IPropertyValidator (1)
public void validate( // Required because of IPropertyValidator (2)
Messages errors, // Here you add the error messages (3)
Object object, // The value to validate
String objectName, // The entity name, usually to use in message
String propertyName) // The property name, usually to use in message
{
if (object == null) return;
if (!(object instanceof BigDecimal)) {
errors.add( // If you add an error the validation fails
"expected_type", // Message id in i18n file
propertyName, // Arguments for i18n message
objectName,
"bigdecimal");
return;
}
BigDecimal n = (BigDecimal) object;
if (n.intValue() > 1000) {
errors.add("not_greater_1000"); // Message id in i18n file
}
}
}
As you can see your validator class must implement
IPropertyValidator
(shown as 1), this forces you to have a
validate() (shown as
2) method that receives a
Messages object, that we call
errors
(shown as 3). It is just a container of error messages. You only need to
add a message to
errors in order to provoke the validation to
fail.
This is a simple way to do custom validation, moreover the validation
logic in your validator can be reused across your application. Although,
if you want to create a reusable validation a better option is to create
your own validation annotation using Bean Validation. It's more verbose
than using a validator class but it's more elegant if you reuse the
validation many times.
User
interface
Though OpenXava automatically generates a crude but valid user interface
from a naked JPA entity, this is only useful for very basic cases. In
real life applications it is required to refine the ways the user
interface is generated. In OpenXava this is done by annotations that, in
a very abstract way, defines the user interface shape.
The
default user interface
By default, OpenXava generates a user interface that shows all members
of the entity sequentially. If you have a
Seller entity as
here:
@Entity
public class Seller {
@Id @Column(length=3)
private int number;
@Column(length=40) @Required
private String name;
@OneToMany(mappedBy="seller")
private Collection<Customer> customers;
// Getters and setters
...
}
OpenXava will produce for you the next
user interface:
As you see, it shows the members (
number,
name and
customers
in this case) in the same order as they are declared in the Java source.
OpenXava uses the JPA and validation annotations to generate a better
user interface, for example it determines the size of the editors from
@Column(length),
shows a key icon for the
@Id property and shows an icon to
indicate that it is required if the property is marked as
@Required,
and so on.
This default interface is useful for simple cases, but for a more
advanced user interface you need a way to customize it. OpenXava
provides you with annotations to do so, such as
@View for
customizing the layout of the members.
The
@View annotation
@View annotation is used to define the layout of the members in
the user interface. It is defined at the entity level:
@Entity
@View(members=
"year, number, date;" + // Comma means in the same line
"discounts [" + // Between square brackets means inside a frame
" customerDiscount, customerTypeDiscount;" +
"];" +
"comment;" + // Semicolon means new line
"customer { customer }" + // Between curly brackets means inside a tab
"details { details }" +
"amounts { amountsSum; vatPercentage; vat }" +
"deliveries { deliveries }"
)
public class Invoice {
This is the resulting user interface:
As you can see, defining the layout of the member is easy. You only need
to enumerate them inside a string using commas to separate elements,
semicolons for new line, square brackets for groups (frames), curly
brackets for sections (tabs) and so on.
You may have several views for each
entity. For that purpose the @Views annotation gives a name
to each view:
@Entity
@Views({
@View(
members="number, name; address; invoices"
),
@View(
name="Simple", members="number, name"
)
})
public class Customer {
You can leave a view without a name which will result in a default view.
The view names are used from other parts of the application to choose
which view to use.
Though with a
@View annotation you can define the layout but
you also need to define the way each member is displayed. OpenXava takes
care to provide you a lot of useful annotations which you will see in
the next section.
Refining
member presentation
OpenXava allows you to refine the user interface for any property,
reference or collection in nearly infinite ways. You only need to add
the corresponding annotation. For example, by default a reference (a
@ManyToOne
relationship) is displayed using a frame with a detailed view. If you
want to show that reference using a combo you only have to annotate the
reference with
@DescriptionsList:
If you want the effect of the annotation only for some views you can do
so by using the attribute
forViews available in all user
interface annotations:
@Views ({ // You have several views for Seller
@View(members=" ... "),
@View(name="Simplest", members=" ... "),
@View(name="Simple", members=" ... "),
@View(name="Complete", members=" ... "),
})
public class Seller {
@DescriptionsList(forViews="Simplest, Simple") // Combo only will be used for
@ManyToOne(fetch=FetchType.LAZY) // 'level' in Simplest and Simple views
private SellerLevel level;
The next table shows all OpenXava annotations for customizing the user
interface of entity members.
Annotation
|
Description
|
Apply on
|
@Action
|
Associates an action to a property or reference in the view
|
Properties and references
|
@AsEmbedded
|
Makes the behavior of a view for a reference (or collection)
to an entity be the same an embedded object (or collection of
entities with CascadeType.REMOVE)
|
References and collections
|
@Collapsed
|
The frame surrounding the member view will be initially closed
|
References and collections
|
@CollectionView
|
The view of the referenced object (each collection element)
which is used to display the detail
|
Collections
|
@Condition
|
Restricts the elements that appear in the collection
|
Collections
|
@DescriptionsList
|
To visualize a reference as a descriptions list (actually a
combo)
|
References
|
@DetailAction
|
Adds an action to the detail that is being edited in a
collection
|
Collections
|
@DisplaySize
|
The size in characters of the editor in the User Interface
used to display this property
|
Properties
|
@EditAction
|
Allows you to define your custom action to edit a collection
element
|
Collections
|
@EditOnly
|
The final user can modify existing elements in the collection,
but not add or remove collection elements
|
Collections
|
@Editor
|
Name of the editor to use for displaying the member in this
view
|
Properties, references and collections.
|
@HideDetailAction
|
In a collection it allows you to define your custom action to
hide the detail view
|
Collections
|
@LabelFormat
|
Format to display the label of this property or reference
(displayed as descriptions list)
|
Properties and references
|
@LabelStyle
|
Style to display the label
|
Properties and descriptions list references
|
@ListAction
|
To add actions to the list in a collection
|
Collections
|
@ListProperties
|
Properties to show in the list for visualization of a
collection
|
Collections
|
@NewAction
|
Allows you to define your custom action to start adding a new
element to a collection
|
Collections
|
@NoCreate
|
The final user cannot create new objects of the referenced
type from here
|
References and collections
|
@NoFrame
|
The reference is not displayed inside a frame
|
References
|
@NoModify
|
The final user cannot modify the current referenced object
from here
|
References and collections
|
@NoSearch
|
The user will not have a link to make searches with a list,
filters, etc.
|
References
|
@OnChange
|
Action to execute when the value of this property/reference
changes
|
Properties and references
|
@OnChangeSearch
|
Action to execute to do the search of a reference when the
user types the key value
|
References
|
@OnSelectElementAction
|
Allows you to define an action to be executed when an element
of the collection is selected or unselected
|
Collections
|
@ReadOnly
|
The member will never be editable by the final user in the
indicated views
|
Properties, references and collection
|
@ReferenceView
|
View of the referenced object used to display it in a
reference
|
References
|
@RemoveAction
|
Allows you to define a custom action to remove the element
from the collection
|
Collections
|
@RemoveSelectedAction
|
Allows you to define your custom action to remove the selected
elements from the collection
|
Collections
|
@RowAction
|
In collections to add an action in each row, but not in the
collection button bar
|
Collections
|
@RowStyle
|
For indicating the row style for list and collections
|
Entity (by means of @Tab) and collections
|
@SaveAction
|
Allows you to define a custom action to save the collection
element
|
Collections
|
@SearchAction
|
Allows you to specify your own action for searching
|
References
|
@SearchListCondition
|
Defines a condition to be used when showing list of selectable
items for adding elements to a collection or assigning value to
a reference
|
References and collections
|
@Tree
|
Displays the collection using a tree
|
Collections
|
@ViewAction
|
Allows you to define your custom action to view a collection
element
|
Collections
|
@XOrderBy
|
The eXtended version of @OrderBy (JPA)
|
Collections
|
You may think that these are a lot of annotations, but to your surprise
there are more yet because most of these annotations have a plural
version that allow you to declare different values for different views:
@DisplaySizes({ // To use several @DisplaySize
@DisplaySize(forViews="Simple", value=20), // name has 20 as display
// size in view Simple
@DisplaySize(forViews="Complete", value=40) // name has 40 as display
// size in view Complete
})
private String name;
Don't worry if you don't know how to use all these annotations yet.
You'll learn them as you develop OpenXava applications.
Learn
more about the user interface
This section introduces you briefly to the user interface with OpenXava.
Unfortunately, many interesting concepts still remain untouched such as
nested groups and sections, actions in views, table layout for groups
and sections, view inheritance, details about how to use all UI
annotations, etc. Don't worry, you will learn all these things.
Other
annotations
Apart from validation and user interface OpenXava has some other useful
annotations:
Annotation
|
Description
|
Apply on
|
@DefaultValueCalculator
|
For calculating the initial value
|
Properties and references
|
@Hidden
|
A hidden property has a meaning for the developer but not for
the user
|
Properties
|
@Depends
|
Declares that a property depends on other one(s)
|
Properties
|
@Stereotype
|
A stereotype is the way to determine a specific behavior of a
type
|
Properties
|
@Tab
|
Defines the behavior for tabular data presentation (list mode)
|
Entities
|
@SearchKey
|
A search key property or reference is used by the user to
search
|
Properties and references
|
Summary
You have seen how to use Java annotations to do declarative programming
with OpenXava. Things such as the user interface or validation that are
typically programming stuff, can be done just by annotating our code.
You can learn a lot of details about these annotations and even learn
about more annotations in
OpenXava
Reference Guide and in
OpenXava API Doc of org.openxava.annotations.
However, the best way to learn is by example. The rest of this course is
illustrative enough with pure examples that will show you how to use
these annotations.
Let's start learning.
Any problem with this lesson? Ask in the forum