Primefaces dispone de un amplio conjunto de componentes que podemos utilizar en nuestros proyectos, uno de ellos es DataTable que se utiliza para mostrar datos en un formato tabular. Este componente, pone a disposición de los programadores funcionalidades muy interesantes tales como: paginación, búsqueda, filtrado, selección, ordenamiento, entre otras.
Para ello debemos implementar la consulta (query) utilizando Criteria API, dentro del EJB correspondiente, ya que ella nos permite construir una query en tiempo de ejecución.
Nuestra base de datos cuenta con una sola tabla llamada persona, cuya estructura es la siguiente:
Luego mediante JPA debemos mapear esta tabla a una clase java llamada Persona.java
@Entity
@Table(name = "persona")
public class Persona implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Integer id;
@Column(name = "nombre_apellido")
private String nombreApellido;
@Column(name = "email")
private String email;
// Constructor
// Setters/Getters
}
En la interface local PersonaFacadeLocal.java, debemos definir dos métodos, el primero de ellos llamado findAll, cuenta con los siguientes parámetros:
- start - Indica la posición inicial del cojunto de datos
- size - Indica la cantidad de registros a mostrar
- sortField - Campo por el que se desea ordenar la colección
- sortOrder- Tipo de ordenamiento (ASC | DESC)
- filters - Criterios de búsqueda
y el segundo llamado count con un solo parámetro de tipo Map, que sera el encargado de retornar la cantidad de registros encontrados en base a los filtros aplicados.
PersonaFacadeLocal.java
Luego se debe realizar la implementación de ambos métodos dentro del session bean PersonaFacade como se observa a continuación:
PersonaFacade.java
El método findAll comienza con la interfaces CriteriaBuilder y CriteriaQuery, ya que ellas nos permitiran construir una query dinámica de manera segura sin ser víctimas de la inyección SQL, ya que se valida su contrucción y los parámetros que recibe.
CriteriaQuery<Persona> criteriaQuery = criteriaBuilder.createQuery(Persona.class);
Root<Persona> root = criteriaQuery.from(Persona.class);
CriteriaQuery<Persona> select = criteriaQuery.select(root);
List<Predicate> listaPredicados = new ArrayList<>();
List<Persona> listaPersonas = new ArrayList<>();
En esta sección verificamos si existe o no algún criterio de ordenamiento y su tipo.
criteriaQuery.orderBy(sortOrder == SortOrder.ASCENDING
? criteriaBuilder.asc(root.get(sortField))
: criteriaBuilder.desc(root.get(sortField)));
}
Luego, verificamos si el usuario especificó algun criterio de búsqueda para filtrar los resultados. Recordemos que la tabla persona cuenta con los campos id, nombre_apellido, email, por lo que el usuario puede aplicar cualquiera de estos 3 criterios. En el caso de haya alguno se lo añade a una lista de predicados, caso contrario se ignoran.
Es
importante destacar que la búsqueda por id debe retornar una sola fila, y
en los restantes casos el usuario puede ir escribiendo letra por letra y
el filtro debe ir encontrando las coincidencias en "tiempo real", por lo que podría retornar más de un resultado o ninguno.
En el CDI Controller llamado por ejemplo PersonaDataController.java y con un scope de tipo vista, debemos realizar las siguientes tareas:
- Inyectar una referencia a la interface PersonaFacadeLocal para poder utilizar sus métodos:
PersonaFacadeLocal personaEJB;
- Definir una variable llamada listaPersonas cuyo tipo de dato es LazyDataModel especialidada en Persona, con sus respectivos métodos set y get.
- Implementar los métodos init e iniciar acompañados de la anotación @PostConstruct, para que al momento en que el usuario acceda a la vista el listado este disponible.
listaPersonas = new LazyDataModel<Persona>() {
@Override
public List<Persona> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, FilterMeta> filterBy) {
List<Persona> listaDePersonas = personaEJB.findAll(first, pageSize, sortField, sortOrder, filterBy);
listaPersonas.setRowCount(personaEJB.count(filterBy));
refreshTableState();
return listaDePersonas;
}
@Override
public Object getRowKey(Persona object) {
return object.getId();
}
@Override
public Persona getRowData(String rowKey) {
Persona persona = null;
try {
persona = personaEJB.find(Integer.parseInt(rowKey));
} catch (NumberFormatException e) {
System.out.println("ocurrio un error : " + e.getLocalizedMessage());
}
return persona;
}
};
}
El método iniciar() es muy interesante, ya que en su interior se instancia el objeto listaPersonas, y en su interior se sobreescriben los métodos load(), getRowKey(), getRowData().
El método load(), es el encargado de acceder a la base de datos para obtener el listado mediante el método findAll() y a su vez "setea" la cantidad de registros obtenidos mediante setRowCount.
El metodo getRowKey(), se encarga de retornar el campo que identifica de manera univoca a cada registro en el listado.
Por su parte, el metodo getRowData(), tiene como objetivo buscar un objeto por su clave primaria y retornarlo al usuario.
Estos dos últimos método solo son necesarios en caso de que se desee implementar la selección de registros en el componente DataTable.
Finalmente en la vista (archivo index.xhtml), implementamos el componente DataTable de la siguiente manera:
Para
que esta implementación funcione, es muy importante que el valor del
atributo lazy sea true (lazy=true), y que se haya definido el rowKey.
De este modo, nuestro componente DataTable divide el cojunto de registros en varias paginas cargando en la memoria del navegador solo 10 filas y permite que el usuario pueda ordenar de manera ascendente o descendente los registros y filtrarlos de acuerdo a su criterio de búsqueda en tiempo de ejecución.
Seguramente haya otras formas de implementación, pero hasta el momento, a mi me funcionó de maravillas y no experimente ningún tipo de retraso en las operaciones en una base de datos poblada con una cantidad enorme de registros.
Las sugerencias y/o comentarios siempre son bienvenidos, asi que estaré muy agradecido si alguien puede aportar su feedback. Al fin de cuentas, al conocimiento lo hacemos entre todos.
El proyecto completo se encuentra disponible en: https://github.com/Francisco-Castillo/primefaces-lazy-loading.git














































