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
@Money
@Calculation("sum(detalles.importe) * porcentajeIVA / 100")
BigDecimal iva;
@ReadOnly
@Money
@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.