Curso:
1.
Primeros pasos |
2.
Modelo básico del dominio (1) |
3.
Modelo básico del dominio (2) |
4.
Refinar la interfaz de usuario |
5.
Desarrollo ágil |
6.
Herencia de superclases mapedas |
7.
Herencia de entidades |
8.
Herencia de vistas |
9.
Propiedades Java |
10.
Propiedades calculadas |
11.
@DefaultValueCalculator en colecciones |
12.
@Calculation y totales de colección |
13.
@DefaultValueCalculator desde archivo |
14.
Evolución del esquema manual |
15.
Cálculo de valor por defecto multiusuario |
16.
Sincronizar propiedades persistentes y calculadas |
17. Lógica desde la base de datos
|
18.
Validando con @EntityValidator |
19.
Alternativas de validación |
20.
Validación al borrar |
21. Anotación Bean Validation propia |
22.
Llamada REST desde una validación |
23.
Atributos en anotaciones |
24.
Refinar el comportamiento predefinido |
25.
Comportamiento y lógica de negocio |
26.
Referencias y colecciones |
A.
Arquitectura y filosofía |
B.
Java Persistence API |
C. Anotaciones |
D.
Pruebas automáticas
Las anotaciones son la herramienta que Java provee para definir metadatos
en tus aplicaciones, en otras palabras, es la forma de hacer desarrollo
declarativo con Java, donde dices el “qué” y no el “cómo”.
En esta lección verás las anotaciones que puedes usar en una aplicación
OpenXava para definir validaciones, la interfaz de usuario y otros
aspectos para ajustar la aplicación a tus necesidades.
El objetivo de esta lección es introducirte a estas anotaciones, pero no
te muestra todas su sutilezas y posibles casos de uso; lo cual requeriría
varios libros de los gordos.
Validación
OpenXava incluye un marco de validación fácil de usar y extensible. Además
soporta
Bean
Validation e
Hibernate Validator.
Validación
declarativa
La forma preferida de hacer validación en OpenXava es mediante
anotaciones, es decir, de manera declarativa. Por ejemplo, sólo has de
marcar una propiedad como
@Required:
@Required // Esto fuerza a validar esta propiedad como requerida al grabar
private String nombre;
Y OpenXava hará la validación correspondiente al grabar:
Validaciones
predefinidas
Las anotaciones de validación que OpenXava tiene incluidas (en
org.openxava.annotations)
son:
Anotación
|
Aplica a
|
Validación
|
@Required
|
Propiedad
|
Comprueba si la propiedad tiene valor
|
@PropertyValidator
|
Propiedad
|
Permite definir una lógica de validación personalizada
|
@EntityValidator
|
Entidad
|
Permite definir una lógica de validación personalizada
|
@RemoveValidator
|
Entidad
|
Permite definir una lógica de validación personalizada al borrar
|
Las anotaciones de Bean Validation (
javax.validation.constraints)
son reconocidas por OpenXava, por tanto puedes usar todas las anotaciones
predefinidas de Bean Validation en tus aplicaciones OpenXava:
Anotación
|
Aplica a
|
Validación
|
@Max(value=)
|
Propiedad (numérica)
|
Comprueba que el valor sea igual o menor al máximo
|
@Min(value=)
|
Propiedad (numérica)
|
Comprueba que el valor sea igual o mayor al mínimo
|
@DecimalMax(value=)
|
Propiedad (numérica o cadena representando un valor numérico)
|
Comprueba que el valor sea igual o menor al máximo el cual puede
contener decimales
|
@DecimalMin(value=)
|
Propiedad (numérica o cadena representando un valor numérico)
|
Comprueba que el valor sea igual o mayor al mínimo el cual puede
contener decimales
|
@NotNull
|
Propiedad
|
Comprueba que el valor no sea nulo
|
@Null
|
Propiedad
|
Comprueba que el valor sea nulo
|
@Past
|
Propiedad (fecha o calendario)
|
Comprueba que la fecha esté en el pasado
|
@Future
|
Propiedad (fecha o calendario)
|
Comprueba que la fecha esté en el futuro
|
@Pattern(regexp="regexp", flags=)
|
Propiedad (cadena)
|
Comprueba que la propiedad cumpla la expresión regular dado un match
flag
|
@Size(min=, max=)
|
Propiedad (cadena, array, colección, mapa)
|
Comprueba que la cantidad de elementos esté entre min y max
(ambos incluidos)
|
@AssertFalse
|
Propiedad
|
Comprueba que el método se evalúe a falso (útil para
restricciones expresadas con código en vez de por anotaciones)
|
@AssertTrue
|
Propiedad
|
Comprueba que el método se evalúe a cierto (útil para
restricciones expresadas con código en vez de por anotaciones)
|
@Digits(integer=, fraction=)
|
Propiedad (numérica o cadena representando un valor numérico)
|
Comprueba que la propiedad sea un número con como máximo los
enteros indicados en integer y los decimales indicados
en fraction
|
A partir de OpenXava 6.1 se soporta Bean Validation 2.0, por lo que
también tenemos disponibles las siguientes anotaciones en
javax.validation.constraints:
Anotación
|
Aplica a
|
Validación
|
@Email
|
Propiedad (cadena) |
Comprueba que la cadena cumpla con la especificación de formato
para una dirección de correo electrónico |
@NotEmpty
|
Propiedad (cadena, array, colección, mapa)
|
Comprueba que el valor no sea nulo ni esté vacío |
@NotBlank
|
Propiedad (cadena) |
Comprueba que la cadena no sea nulo y su longitud sea mayor de
cero después de hacer un trim() |
@Positive
|
Propiedad (numérica)
|
Comprueba si el valor es positivo
|
@PositiveOrZero
|
Propiedad (numérica)
|
Comprueba si el valor es positivo o cero
|
@Negative
|
Propiedad (numérica)
|
Comprueba si el valor es negativo
|
@NegativeOrZero
|
Propiedad (numérica)
|
Comprueba si el valor es negativo o cero
|
@PastOrPresent
|
Propiedad (fecha u hora)
|
Comprueba si la fecha u hora está en el presente o en el pasado
|
@FutureOrPresent |
Propiedad (fecha u hora)
|
Comprueba si la fecha u hora está en el presente o en el futuro
|
También puedes usar las anotaciones de Hibernate Validator (
org.hibernate.validator.constraints):
Anotación
|
Aplica a
|
Validación
|
@Length(min=, max=)
|
Propiedad (cadena)
|
Comprueba que la longitud de la cadena esté dentro del rango
|
@NotEmpty Obsoleto, usa el @NotEmpty estándar en su lugar
|
Propiedad (cadena, array, colección, mapa)
|
Comprueba que el valor no sea nulo ni esté vacío
|
@Range(min=, max=)
|
Propiedad (numérica o cadena representando un valor numérico)
|
Comprueba que el valor este entre min y max (ambos incluidos)
|
@Email Obsoleto, usa el @Email estándar en su lugar
|
Propiedad (cadena)
|
Comprueba que la cadena cumpla con la especificación de formato
para una dirección de correo electrónico
|
@CreditCardNumber(ignoreNonDigitCharacters=)
|
Propiedad (cadena)
|
Comprueba si la cadena es un número de tarjeta de crédito bien
formateado (derivado del algoritmo del Luhn)
|
@EAN
|
Propiedad (cadena)
|
Comprueba que la cadena sea un código EAN o UPC-A correctamente
formateado
|
@LuhnCheck(startIndex=, endIndex=, checkDigitIndex=,
ignoreNonDigitCharacters=)
|
Propiedad (cadena)
|
Comprueba que los dígitos en la cadena pasen el algoritmo de
Luhn
|
@Mod10Check(multiplier=, weight=, startIndex=, endIndex=,
checkDigitIndex=, ignoreNonDigitCharacters=)
|
Propiedad (cadena)
|
Comprueba que los dígitos en la cadena pasen el algoritmo
genérico mod 10
|
@Mod11Check(threshold=, startIndex=, endIndex=,
checkDigitIndex=, ignoreNonDigitCharacters=, treatCheck10As=,
treatCheck11As=)
|
Propiedad (cadena)
|
Comprueba que los dígitos en la cadena pasen el algoritmo
genérico mod 11
|
@NotBlank Obsoleto, usa el @NotBlank estándar en su lugar
|
Propiedad (cadena)
|
Comprueba que la cadena no sea nulo y su longitud sea mayor de
cero después de hacer un trim()
|
@SafeHtml(whitelistType=, additionalTags=,
additionalTagsWithAttributes=) No disponible desde la
v7.4.3
|
Propiedad (cadena)
|
Comprueba que la cadena no contenga fragmentos potencialmente
peligrosos como <script/>
|
@ScriptAssert(lang=, script=, alias=)
|
Propiedad (cualquier tipo)
|
Comprueba que el script provisto pueda ser evaluado con éxito
contra el valor de la propiedad
|
@URL(protocol=, host=, port= regexp=, flags=)
|
Propiedad (cadena)
|
Comprueba que la cadena sea una URL válida según RFC2396
|
A partir de OpenXava 6.1 se soporta Hibernate Validator 6.0, por lo que
también tenemos disponibles las siguiente anotaciones en
org.hibernate.validator.constraints:
Anotación
|
Aplica a
|
Validación
|
@CodePointLength
|
Propiedad (cadena) |
Comprueba si la longitud de punto de código está entre min y max
ambos incluidos |
@Currency
|
Propiedad (MonetaryAmount de javax.money)
|
Comprueba si la MonetaryAmount tiene que estar en la
CurrencyUnit correcta |
@ISBN
|
Propiedad (cadena) |
Comprueba si la cadena es un ISBN válido. La longitud del número
y el dígito de control se verifican |
@UniqueElements
|
Propiedad (colección)
|
Comprueba que cada uno de los objetos de la colección es único,
es decir, no podemos encontrar 2 elementos iguales en la colección
|
Validación
propia
Añadir tu propia lógica de validación a tu entidad es muy fácil porque las
anotaciones
@PropertyValidator,
@EntityValidator y
@RemoveValidator
te permiten indicar una clase (el validador) con la lógica de validación.
Por ejemplo, si quieres tu propia lógica para validar una propiedad
precioUnitario,
has de escribir algo parecido a esto:
@PropertyValidator(ValidadorPrecioUnitario.class) // Contiene la lógica de validación
private BigDecimal precioUnitario;
Y ahora puedes escribir la lógica que quieras dentro de la clase
ValidadorPrecioUnitario:
public class ValidadorPrecioUnitario
implements IPropertyValidator { // Tiene que implementar IPropertyValidator (1)
public void validate( // Requerido por IPropertyValidator (2)
Messages errores, // Aquí añades los mensajes de error(3)
Object objeto, // El valor a validar
String nombreObjeto, // El nombre de entidad, normalmente para usar en el mensaje
String nombrePropiedad) // El nombre de propiedad, normalmente para usar en el mensaje
{
if (objeto == null) return;
if (!(objeto instanceof BigDecimal)) {
errores.add( // Si añades un error la validación fallará
"tipo_esperado", // Id de mensaje en el archivo i18n
nombrePropiedad, // Argumentos para el mensaje i18n
nombreObjeto,
"bigdecimal");
return;
}
BigDecimal n = (BigDecimal) objeto;
if (n.intValue() > 1000) {
errors.add("no_mayor_de_1000"); // Id de mensaje en el archivo i18n
}
}
}
Como ves tu clase validador ha de implementar
IPropertyValidator
(1), esto te obliga a tener un método
validate() (2) que recibe
un objeto
Messages, que llamamos
errores (3); que es
un contenedor de los mensajes de error. Solo necesitas añadir algún
mensaje de error para hacer que falle la validación.
Esta es una forma sencilla de hacer tus propias validaciones, además la
lógica de validación en tus validadores puede reutilizarse por toda tu
aplicación. Aunque, si lo que quieres es crear validaciones reutilizables
una opción mejor es crear tu propia anotación de validación usando Bean
Validation; es más largo que usar una clase validador, pero es más
elegante si reutilizas la validación muchas veces.
Interfaz
de usuario
Aunque OpenXava genera automáticamente una interfaz de usuario bastante
funcional a partir de una entidad JPA desnuda, esto es solo útil para
casos muy básicos. En aplicaciones de la vida real es necesario refinar la
manera en que la interfaz de usuario es generada. En OpenXava esto se hace
con anotaciones que, con un nivel de abstracción alto, definen el aspecto
de la interfaz de usuario.
La
interfaz de usuario por defecto
Por defecto, OpenXava genera una interfaz de usuario que muestra todos los
miembros de la entidad en secuencia. Con una entidad como esta:
@Entity
public class Comercial {
@Id @Column(length=3)
private int numero;
@Column(length=40) @Required
private String nombre;
@OneToMany(mappedBy="comercial")
private Collection<Cliente> clientes;
// Getters y setters
...
}
OpenXava producirá para ti la siguiente interfaz de usuario:
Como ves, muestra los miembros (
numero,
nombre y
clientes
en este caso) en el mismo orden en que fueron declarados en el código
Java. OpenXava usa las anotaciones JPA y de validación para generar la
mejor interfaz de usuario posible, por ejemplo, determina el tamaño del
editor a partir de
@Column(length), muestra la llavecita de
clave para la propiedad con
@Id y muestra un icono para indicar
que es requerido si la propiedad está marcada con
@Required y
así por el estilo.
Esta interfaz por defecto es útil para casos simples, pero para interfaces
de usuario más avanzadas necesitas una forma de personalizar. OpenXava te
proporciona anotaciones para hacerlo, tal como
@View para
definir la disposición de los miembros.
La
anotación @View
@View sirve para definir la disposición de los miembros en la
interfaz de usuario. Se define a nivel de entidad:
@Entity
@View(members=
"ano, numero, fecha;" + // Coma indica en la misma línea
"descuentos [" + // Entre corchetes indica dentro de un marco
" descuentoCliente, descuentoTipoCliente;" +
"];" +
"observaciones;" + // Punto y coma indica nueva línea
"cliente { cliente }" + // Entre llaves indica dentro de una pestaña
"detalles { detalles }" +
"importes { sumaImportes; porcentajeIVA; iva }" +
"albaranes { albaranes }"
)
public class Factura {
La interfaz de usuario resultante es:
Como ves, definir la disposición de los miembros es fácil, solo necesitas
enumerarlos dentro de una cadena, usando comas para separar los elementos,
punto y coma para salto de línea, corchetes para grupos (marcos),
llavecitas para secciones (pestañas) y así por el estilo.
Puedes tener varias vistas por cada entidad, para eso usa la anotación
@Views,
dando un nombre a cada vista:
@Entity
@Views({
@View(
members="codigo, nombre; direccion; facturas"
),
@View(
name="Simple", members="codigo, nombre"
)
})
public class Cliente {
Si dejas una vista sin nombre, será la vista por defecto. Los nombres de
vista se usarán desde otras partes de la aplicación para escoger que vista
usar.
Con
@View defines la distribución, pero también necesitas
definir la forma en que cada miembro es visualizado, para eso, OpenXava te
proporciona un conjunto de anotaciones bastante útiles que verás en la
siguiente sección.
Refinando
la presentación de los miembros
OpenXava te permite refinar la interfaz de usuario para cualquier
propiedad, referencia o colección de infinidad de maneras. Sólo necesitas
añadir la anotación correspondiente. Por ejemplo, por defecto una
referencia (una relación
@ManyToOne) se visualiza usando un
marco con una vista detallada, si quieres mostrar esa referencia usando un
combo solo has de anotar la referencia con
@DescriptionsList:
Puede que quieras el efecto de una anotación solo para algunas vistas.
Para ello usa el atributo
forViews disponible en todas las
anotaciones de interfaz de usuario:
@Views ({ // Tienes varias vistas para Comercial
@View(members=" ... "),
@View(name="Simplisima", members=" ... "),
@View(name="Simple", members=" ... "),
@View(name="Completa", members=" ... ")
})
public class Comercial {
@DescriptionsList(forViews="Simplisima, Simple") // El combo solo se usará
@ManyToOne(fetch=FetchType.LAZY) // para 'nivel' en la vistas Simplisima y Simple
private NivelComercial nivel;
La siguiente tabla muestra todas las anotaciones OpenXava para
personalizar la interfaz de usuario de los miembros de una entidad:
Anotación
|
Descripción
|
Aplica a
|
@Action
|
Asocia una acción a una propiedad o referencia en la vista
|
Propiedades y referencias
|
@AsEmbedded
|
Hace que comportamiento en la vista de una referencia (o
colección) a entidad sea como en el caso de un objeto incrustado
(o colección de entidades con CascadeType.REMOVE)
|
Referencias y colecciones
|
@Collapsed
|
El marco que envuelve al miembro estará cerrado inicialmente
|
Referencias y colecciones
|
@CollectionView
|
La vista del objeto referenciado (cada elemento de la colección)
que será usada para visualizar el detalle
|
Colecciones
|
@Condition
|
Restringe los elementos que aparecen en la colección
|
Colecciones
|
@DescriptionsList
|
Para visualizar una referencia como una lista de descripciones
(un combo)
|
Referencias
|
@DetailAction
|
Añade una acción al detalle que está siendo editado en una
colección
|
Colecciones
|
@DisplaySize
|
El tamaño en caracteres del editor en la interfaz de usuario
usado para visualizar esta propiedad
|
Propiedades
|
@EditAction
|
Permite definir una acción propia para editar el elemento de la
colección
|
Colecciones
|
@EditOnly
|
El usuario final podrá modifica los elementos existentes en la
colección, pero no añadir o quitar elementos
|
Colecciones
|
@Editor
|
Nombre del editor a usar para visualizar el miembro en esta
vista
|
Propiedades, referencias y colecciones.
|
@HideDetailAction
|
En una colección permite definir una acción propia para ocultar
la vista de detalle
|
Colecciones
|
@LabelFormat
|
Formato para visualizar la etiqueta de esta propiedad o
referencia (visualizada como lista descripciones)
|
Propiedades y referencias
|
@LabelStyle
|
Estilo para visualizar la etiqueta
|
Propiedades y referencias como lista descripciones
|
@ListAction
|
Para añadir acciones a la lista en una colección
|
Colecciones
|
@ListProperties
|
Propiedades a mostrar en la lista que visualiza la colección
|
Colecciones
|
@NewAction
|
Permite definir una acción propia para añadir un nuevo elemento
a la colección
|
Colecciones
|
@NoCreate
|
El usuario final no podrá crear nuevos objetos del tipo
referenciado desde aquí
|
Referencias y colecciones
|
@NoFrame
|
La referencia no se visualizará dentro de un marco
|
Referencias
|
@NoModify
|
El usuario final no podrá modificar el objeto actual
referenciado desde aquí
|
Referencias y colecciones
|
@NoSearch
|
El usuario no tendrá el vínculo para hacer búsquedas con una
lista, filtros, etc.
|
Referencias
|
@OnChange
|
Acción a ejecutar cuando el valor de la propiedad o referencia
cambia
|
Propiedades y referencias
|
@OnChangeSearch
|
Acción a ejecutar para hacer la búsqueda de la referencia cuando
el usuario teclea los valores clave
|
Referencias
|
@OnSelectElementAction
|
Permite definir una acción a ejecutar cuando un elemento de la
colección es seleccionado o deseleccionado
|
Colecciones
|
@ReadOnly
|
El miembro nunca será editable por el usuario final en las
vistas indicadas
|
Propiedades, referencias y colecciones
|
@ReferenceView
|
Vista del objeto referenciado a usar para visualizar esta
referencia
|
Referencia
|
@RemoveAction
|
Permite definir una acción propia para quitar el elemento de la
colección
|
Colecciones
|
@RemoveSelectedAction
|
Permite definir una acción propia para quitar los elementos
seleccionados de la colección
|
Colecciones
|
@RowAction
|
En las colecciones añade una acción en cada fila, pero no en la
barra de botones de la colección
|
Colecciones
|
@RowStyle
|
Para indicar el estilo de la fila para la lista y colecciones
|
Entidad (mediante @Tab) y colecciones
|
@SaveAction
|
Permite definir una acción propia para grabar el elemento de la
colección
|
Colecciones
|
@SearchAction
|
Permite definir una acción propia para buscar
|
Referencias
|
@SearchListCondition
|
Define una condición para ser usada cuando se muestra la lista
de elementos seleccionables para añadir a una colección o asignar
a una referencia
|
Referencias y colecciones
|
@Tree
|
Visualiza la colección usando un árbol
|
Colecciones
|
@ViewAction
|
Permite definir una acción propia para visualizar el elemento de
la colección
|
Colecciones
|
@XOrderBy
|
La versión eXtendida de @OrderBy (JPA)
|
Colecciones
|
Puedes pensar que hay muchas anotaciones, pero en realidad hay todavía
más, porque la mayoría de estas anotaciones tienen una versión en plural
para definir diferentes valores para diferentes vistas:
@DisplaySizes({ // Para usar varias veces @DisplaySize
@DisplaySize(forViews="Simple", value=20), // nombre tiene 20 para display
// size en la vista Simple
@DisplaySize(forViews="Completa", value=40) // nombre tiene 40 para display
// size en la vista Complete
})
private String nombre;
No te preocupes si aún no sabes como usar todas las anotaciones. Las irás
aprendiendo poco a poco a medida que desarrolles aplicaciones OpenXava.
Aprende
más acerca de la interfaz de usuario
Esta sección te introduce brevemente a la generación de interfaz de
usuario con OpenXava. Por desgracia, muchas e interesantes cosas se nos
han quedado en el tintero, como por ejemplo, los grupos y secciones
anidados, la herencia de vistas, detalles sobre como usar todas las
anotaciones de IU, etc.
A través de este curso aprenderás técnicas avanzadas sobre la interfaz de
usuario.
Otras
anotaciones
Aparte de las validaciones y de la interfaz de usuario, OpenXava dispone
de algunas otras anotaciones interesantes:
Anotación
|
Descripción
|
Aplica a
|
@DefaultValueCalculator
|
Para calcular el valor inicial
|
Propiedades y referencias
|
@Hidden
|
Una propiedad oculta tiene significado para el desarrollador
pero no para el usuario
|
Propiedades
|
@Depends
|
Declara que una propiedad depende de otra(s)
|
Propiedades
|
@Stereotype
|
Un estereotipo es la forma de determinar un comportamiento
específico para un tipo
|
Propiedades
|
@Tab
|
Define el comportamiento para la presentación de los datos
tabulares (modo lista)
|
Entidades
|
@SearchKey
|
Una propiedad o referencia marcada como clave de búsqueda se
usará por el usuario para buscar
|
Propiedades y referencias
|
Resumen
Este capítulo ha mostrado como usar anotaciones Java para hacer
programación declarativa con OpenXava. Cosas como la interfaz de usuario o
las validaciones, que típicamente son pura programación, se pueden hacer
con tan solo anotar nuestro código.
Puedes aprender más detalles sobre estas anotaciones e incluso aprender
más anotaciones en
la
Guía de Referencia de OpenXava y en
OpenXava API Doc de org.openxava.annotations.
Sin embargo, la mejor forma de aprender es con ejemplos, y el resto del
curso es justo eso, un conjunto de ejemplos y más ejemplos que te
mostrarán como usar estas anotaciones.
Empecemos a aprender.
¿Problemas con la lección? Pregunta en el foro