Cómo listar una entidad en wuija con prefiltrado
0. Índice de contenidos.
- 1. Introducción.
- 2. Añadir funcionabilidad extra y genérica sin alterar lo anterior.
- 3. Conclusiones.
1. Introducción
En este pequeño tutorial vamos a tratar de clarificar un poco cómo se podría prefiltrar un listado sobre una entidad en wuija.
Vamos a ponernos en contexto.
- ¿Qué tenemos hasta ahora?
- – Una entidad ( Car )anotada con @Entity persistida en base de datos. Esta entidad tiene un @ManyToOne Brand brand. Es decir un coche puede tener una marca, y una marca puede tener varios coches.
- – Nuestro controlador ( ListCars ) que extiende de ListController. ListController es un controlador genérico válido para cualquier tipo de clase ( ListController <T extends Object> ) que tiene en su interior un Widget ( CriteriaQueriedDataTable<T> ) que lo que hace al inicializarse ( en el init() ) es pintar una tabla con todos los registros que hay en base de datos de la entidad que le hemos pasado como parámetro
poniendo en la primera fila de la tabla los atributos de la entidad. Además nos pinta encima un buscador que nos permite buscar por los atributos ( sean del tipo que sean ) que le configuremos ( configureProperties() ) de la entidad y filtrar los resultados…
- – Una entidad ( Car )anotada con @Entity persistida en base de datos. Esta entidad tiene un @ManyToOne Brand brand. Es decir un coche puede tener una marca, y una marca puede tener varios coches.
- ¿Qué queremos?
- – Listar una entidad ( Cars )con «CriteriaQueriedDataTable» pero sólo queremos que nos salgan los coches de una marca en concreto, por ejemplo los de la marca Autentia y que sólo se pueda re-filtrar por
los que se muestran en la tabla, es decir, el buscador sería un filtro sobre un resultado ya filtrado.
- – Listar una entidad ( Cars )con «CriteriaQueriedDataTable» pero sólo queremos que nos salgan los coches de una marca en concreto, por ejemplo los de la marca Autentia y que sólo se pueda re-filtrar por
2. Añadir funcionabilidad extra sin alterar lo anterior
En el controlador genérico nos encontramos la siguiente lógica en el método init:
final EntityCriteria criteria = new EntityCriteria(entityClass); criteria.setMatchMode(MatchMode.ANY); entityDataTable = new CriteriaQueriedDataTable(propertiesOfList, criteria, new AdvancedQuery(propertiesOfQuery, criteria), dao, csvReportsService);
Podemos ver como al CriteriaQueriedDataTable y al AdvancedQuery ( buscador ) se le está pasando exactamente la misma criteria, que además está vacía, ya que la acabamos de crear, por lo que nos va a pintar todos los registros de la base de datos que se correspondan con la entidad.
Para añadir nuestro filtrado vamos a crear en nuestra Clase ListController un método que permita settear una precondición. Quedaría de la siguiente forma.
protected void setPreCondition(EntityCriteria criteria) { preCondition = criteria; }
Ahora es cuando en nuestra clase concreta tenemos que construir dicho filtrado o precondición. Lo vamos a hacer de la siguiente forma.
private EntityCriteria getCriteria() { final EntityCriteria criteria = new EntityCriteria(Car.class, MatchMode.ALL); Junction brand = new Junction(MatchMode.ANY); brand.add(new SimpleExpression("brand", Operator.EQUALS, "Autentia" )); criteria.add(brand); return criteria; }
Tal y como vemos tenemos un EntityCriteria para la clase Car, en donde le indicamos el MatchMode ALL. Esto quiere decir que todas las condiciones que se añadan a esta EntityCriteria van a ir separadas por un AND. Es decir, obtendrá los resultados que cumplan todas las restricciones.
A continuación instanciamos un Junction con el MatchMode ANY. Esto quiere decir que se recogerán los resultados que cumplan alguna de sus condiciones (OR). En nuestro caso le vamos a fijar una condición al Junction, esto es, una SimpleExpression donde indicamos que la marca (brand) tiene que ser igual a «Autentia».
Por último añadimos el Junction al Criteria.
Para que este prefiltrado esté presente en la clase genérica (ListController) vamos a utilizar el método que nos creamos anteriormente en el init de nuestra clase concreta de la siguiente forma:
@Override @PostConstruct void init() { setPreCondition(getCriteria()); super.init(); }
Ahora en el método init de ListController tendremos que preguntar si tenemos fijada ya una precondición. La lógica sería parecida a lo siguiente:
if (preCondition == null){ // hacemos lo que se hacía antes } else { final Junction modifiedByUserCriteria = new Junction(); preCondition.add(modifiedByUserCriteria); entityDataTable = new CriteriaQueriedDataTable(propertiesOfList, preCondition, new AdvancedQuery(propertiesOfQuery, modifiedByUserCriteria), dao, csvReportsService); }
Este punto es el más importante quizás. Si nos fijamos en la línea 5 estamos instanciando un Juntion vacío. En la línea 6 se lo añadimos a la criteria (preCondition), con lo que tenemos 2 Juntion. Juntion1 AND Juntion2. Como el segundo está vacío la única restricción que tenemos
es la del primero, es decir el prefiltrado por marca = Autentia. Al pasarle esta precondición al CriteriaQueriedDataTable, nos estará pintando justamente los resultados que queremos. Al buscador (AdvancedQuery), le pasamos ese Junction 2 (modifiedByUserCriteria) para que si se
deseara aplicar otro filtrado, los resultados que se van a mostrar cumplirán los 2 filtrados.
3. Conclusiones
La idea de hacer funcionabilidad genérica es muy buena. Por eso cuando la completamos, no debemos vincular lo genérico a lo específico. También es importante dejar la lógica antigua tal y como estaba. Para esto tenemos
dos opciones:
1. Crearnos una nueva clase que extienda de la genérica, si es que la funcionabilidad tiene suficiente importancia como para estar en otra clase. De esta manera conservaríamos la lógica anterior y no tendríamos riesgo de haber alterado otras cosas.
2. Añadir la funcionabilidad en la propia clase genérica, ya que sigue siendo funcionabilidad genérica, teniendo mucho cuidado de no alterar lo anterior.