Si no te gustan los videos sigue
las instrucciones a continuación.
Propiedades
persistentes con @Calculation
A veces las propiedades calculadas no son la mejor opción. Imagínate que
tienes una propiedad calculada en
Factura, digamos
descuento:
// NO LO AÑADAS A TU CÓDIGO, ES SÓLO PARA ILUSTRAR
public BigDecimal getDescuento() {
return getImporte().multiply(new BigDecimal("0.10"));
}
Si necesitas procesar todas las facturas cuyo descuento sea mayor
de 1000, has de escribir un código como el siguiente:
// NO LO AÑADAS A TU CÓDIGO, ES SÓLO PARA ILUSTRAR
Query query = getManager().createQuery("from Factura"); // Sin condición en la consulta
for (Object o: query.getResultList()) { // Itera por todos los objetos
Factura f = (Factura) o;
if (f.getDescuento() // Pregunta a cada objeto
.compareTo(new BigDecimal("1000")) > 0) {
f.hacerAlgo();
}
}
No puedes usar una condición en la consulta para discriminar por
descuento,
porque
descuento no está en la base de datos, está sólo en el
objeto Java, por lo que has de instanciar todos y cada uno de los
objetos para poder preguntar por el
descuento. En algunos casos
esta forma es una buena opción, pero si tienes una cantidad inmensa de
facturas y sólo unas pocas tiene el
descuento mayor de 1000,
entonce tu proceso va a ser muy ineficiente. ¿Qué alternativas tenemos?
Nuestra alternativa es usar la anotación
@Calculation.
@Calculation
es una anotación OpenXava que permite asociar un cálculo simple a una
propiedad persistente. Puedes definir
descuento con
@Calculation
como se muestra en el siguiente código:
// NO LO AÑADAS A TU CÓDIGO, ES SÓLO PARA ILUSTRAR
@ReadOnly
@Calculation("importe * 0.10")
BigDecimal descuento;
Esto es una propiedad persistente convencional, es decir con una
columna correspondiente en la base de datos, pero tiene un cálculo
definido con
@Calculation. En este caso el cálculo es
importe
* 0.10, de tal manera que cuando el usuario cambia
importe
en la interfaz de usuario
descuento se recalcula
instantaneamente. El valor recalculado se graba en la base de datos
cuando el usuario pulsa en
Grabar, como con cualquier otra
propiedad persistente. También hemos anotado
descuento con
@ReadOnly,
por lo que parece y se comporta como una propiedad calculada, aunque
puedes omitir
@ReadOnly y así el usuario podría modificar el
valor calculado.
Lo más útil de las propiedades
@Calculation es que se pueden usar en las condiciones, por lo
que puedes reescribir el proceso de arriba como se muestra en el
siguiente código:
// NO LO AÑADAS A TU CÓDIGO, ES SÓLO PARA ILUSTRAR
Query query = getManager().createQuery("from Factura f where f.descuento > :descuento"); // Condición permitida
query.setParameter("descuento", new BigDecimal(1000));
for (Object o: query.getResultList()) { // Itera sólo por los objectos seleccionados
Factura f = (Factura) o;
f.hacerAlgo();
}
De esta manera ponemos el peso de seleccionar los registros en el
servidor de la base de datos y no en el servidor Java. Además, los
descuentos no se recalculan cada vez, sino que ya está calculados y
grabados.
Este hecho tiene también efecto en el modo lista, porque el usuario no
puede filtrar ni ordenar por las propiedades calculadas, pero sí que lo
puede hacer usando propiedades persistentes con
@Calculation:
@Calculation es una buena opción cuando necesitas filtrar y
ordenar, y un cálculo simple es suficiente. Una desventaja de las
propiedades con
@Calculation es que sus valores se recalculan
sólo cuando el usuario interactúa con el registro y cambia algún valor
de las propiedades usadas en el cálculo, por lo tanto cuando añades una
nueva propiedad
@Calculation a una entidad con datos existente
has de actualizar los valores de la nueva columna en la tabla usando
SQL. Por otra parte si necesitas un cálculo complejo, con bucles o
consultando otras entidades, todavía sigues necesitando una propiedad
calculada con tu lógica Java en el getter. En este último caso si además
necesitas ordenar y filtrar en modo lista por la propiedad calculada una
opción es tener ambas, la calculada y la persistente, y sincronizar sus
valores usando los métodos de retrollamada de JPA (hablaremos sobre los
métodos de retrollamada en próximas lecciones).
Propiedades
de total de una colección
También queremos añadir importes a
Pedido y
Factura.
Tener IVA, importe base e importe total es indispensable. Para hacerlo
sólo necesitas añadir unas pocas propiedades a la clase
DocumentoComercial.
La siguiente figura muestra la interfaz de usuario para estas propiedades:

Añade el siguiente código a la entidad
DocumentoComercial:
@Digits(integer=2, fraction=0) // Para indicar su tamaño
BigDecimal porcentajeIVA;
@ReadOnly
@Stereotype("DINERO")
@Calculation("sum(detalles.importe) * porcentajeIVA / 100")
BigDecimal iva;
@ReadOnly
@Stereotype("DINERO")
@Calculation("sum(detalles.importe) + iva")
BigDecimal importeTotal;
Fíjate como hemos escogido propiedades persistentes con
@Calculation
+ @ReadOnly en lugar de propiedades calculadas para
iva e
importeTotal,
porque los cálculos son simples, y filtrar y ordenar por ellos es muy
útil. También, puedes ver como en
@Calculation puedes usar
sum(detalles.importe)
para referirte a la suma de columna
importe de la colección
detalles,
de esta manera podemos prescindir de una propiedad
importeBase.
Por otra parte,
porcentajeIVA es un propiedad persistente
convencional. En este caso usamos
@Digits (una anotación de Bean
Validation, el estándar de validación de Java) como una alternativa a
@Column
para especificar su tamaño.
Ahora que ya has escrito las propiedades para los importes de
DocumentoComercial,
tienes que modificar la lista de propiedades de la colección
detalles
para mostrar las
propiedades
de total de
DocumentoComercial. Veámoslo:
abstract public class DocumentoComercial extends Identificable {
@ElementCollection
@ListProperties(
"producto.numero, producto.descripcion, cantidad, precioPorUnidad, " +
"importe+[" +
"documentoComercial.porcentajeIVA," +
"documentoComercial.iva," +
"documentoComercial.importeTotal" +
"]"
)
private Collection<Detalle> detalles;
...
}
Las propiedades de total son propiedades normales de la entidad (
DocumentoComercial
en este caso) que en la interfaz de usuario se localizan debajo de una
columna de una colección. Para eso, en
@ListProperties se usan
corchetes después de la propiedad para enumerarlas, algo así como
importe[documentoComercial.importeTotal].
Además, si simplemente quieres la suma de la columna no necesitas una
propiedad para ello, con un + después de la propiedad en
@ListProperties
es suficiente, como
importe+. En nuestro caso combinamos ambas
cosas, + y propiedades de total entre [ ].
Ahora puedes probar tu aplicación.
Debería funcionar casi como en la figura del inicio de esta sección.
“Casi” porque porcentajeIVA todavía no tiene un valor por
defecto. Lo añadiremos en la siguiente sección.