Entender y Utilizar los DAS
Los DAS son la herramienta que tenemos para facilitar el trabajo con JPA. Es el medio por el cual los servicios realizan las modiciaciones en el contexto de persistencia.
Un DAS principalmente tiene que implementar la interface DataAccessService<T> y extender la clase DataAccessServiceImpl<T>. Vamos a repasar los diferentes métodos que contiene:
- En la interface DataAccessService<T> tenemos definidos los pricipales métodos para poder crear, borrar, actualizar, refrescar, etc. sobre las entidades.
- En la clase DataAccessServiceImpl<T> tenemos implementados (además de los métodos de la interface) los principales métodos para poder realizar consultas sobre la clase referencia (la identificada como T).
Implementar un DAS
Un DAS esta compuesto por una interface e impementación.
Definir la Interface
Un DAS tiene que definir una interface que comenzara con el nombre de la clase (entidad o DTO) que va a manipular. Por ejemplo, si estamos trabajando con la entidad Anuncio, el nombre de esta interface será AnuncioDataAccessService, en principio el contenido que tendra seran los metodos publicos a exponer a los servicios.
También tiene que definir una constante que especifica nombre del componente Seam, que se utilizara en la clase implementación. Este nombre suele ser igual que el nombre de la interface, pero empezando con letra minúscula.
La interface del DAS debe extender la interface DataAccessService. Ejemplo:
package es.um.atica.prueba.security.authorization.das.interfaces; import java.util.List; import es.um.atica.jpa.das.DataAccessService; import es.um.atica.jpa.das.ResultQuery; import es.um.atica.prueba.entities.Anuncio; public interface AnuncioDataAccessService extends DataAccessService<Anuncio> { public static final String NAME = "anuncioDataAccessService"; ResultQuery<Anuncio> obtenerAnuncios(Map<String, Object> parameters, int firstResult, int resultLimit, String sortField, String sortOrder) }
Definir la Implementación
La clase implementación del DAS comenzara igual que la interface, pero añadiendo el postfijo Impl. En nuestro ejemplo seria AnuncioDataAccessServiceImpl.
La clase implementación del DAS, debe extender la clase DataAccessServiceImpl e implementar la interface creada en el punto anterior (en nuestro ejemplo DataAccessService). Ejemplo:
package es.um.atica.prueba.security.authorization.das.interfaces; import java.util.List; import javax.ejb.Local; import javax.ejb.Stateless; import org.jboss.seam.annotations.Name; import es.um.atica.jpa.das.DataAccessServiceImpl; import es.um.atica.prueba.entities.Anuncio; @Local( AnuncioDataAccessService.class ) @Stateless @Name( AnuncioDataAccessService .NAME ) public class AnuncioDataAccessServiceImpl extends DataAccessServiceImpl<Anuncio> implements AnuncioDataAccessService { // Filtros Dinamicos private static final String[] RESTRICTIONS_ANUNCIO = { UtilString.cmpNumberTextFilterEjbql("entity.id", ":anuncioId"), UtilString.cmpTextFilterEjbql("entity.nombre", ":anuncioNombre"), UtilString.cmpTextFilterEjbql("entity.descripcion", ":anuncioDescripcion"), UtilString.cmpDateTextFilterEjbql("entity.fechaPublicacion", ":anuncioFechaPublicacion"), }; public ResultQuery<Anuncio> obtenerAnuncios(Map<String, Object> parameters, int firstResult, int resultLimit, String sortField, String sortOrder) { return super.resultsByEntityQueryWithDinamicFilter(Arrays.asList(RESTRICTIONS_ANUNCIO), parameters, firstResult, resultLimit, sortField, sortOrder, null, null); } }
En este ejemplo, hemos hecho un método público (para que sea accedido por un servicio o LazyDataModel), que permite hacer consultas páginadas (se devuelven partes de la consulta, donde el primer elemento lo indica el parámetro firstResult y el total a devolver el parámetro resultLimit. Además, la consulta tiene filtros dinámicos (que aparecen o no en la consulta, dependiendo de si el parámetro es nulo o no).
Ejemplo, si el parámetro parameters contiene el valor para el parámetro de consulta anuncioId (y sólo ese parámetro), en la clausula WHERE de la consulta, aparecera la restricción que crea UtilString.cmpNumberTextFilterEjbql(“entity.id”, “:anuncioId”).
La consulta JPQL quedaría de la siguiente manera:
SELECT entity FROM Anuncio entity WHERE LOWER(entity.id) LIKE LOWER(:anuncioId)
En el ejemplo de conulta JPQL anterior, no se ha tenido encuenta aun, los parámetros firstResult, resultLimit, sortField y sortOrder.
Métodos Disponibles
En el DAS tendremos los métodos directos para gestionar la persistencia y métodos para hacer consultas. Muchos ya vienen implementados en la clase DataAccessServiceImpl<T>.
Gestion de la Persistencia
En la interface DataAccessService<T> tenemos definidos los pricipales métodos para poder crear, borrar, actualizar, refrescar, etc. sobre las entidades.
Consultas
Tenemos diferenties tipos de métodos para realizar consultas y consultas por defecto definidas.
Consulta por Defecto
Las consultas por defecto la tenemos definidas en las constantes ABSTRACT_QUERY y ABSTRACT_COUNT_QUERY de la clase DataAccessServiceImpl.
Esta consulta se puede hacer efectiva mediante los métodos:
- getConcreteQuery(): es una consulta JPQL y devuelve la consulta si utilizamos nuestro ejemplo: select entity from Anuncio entity.
- getConcreteCountQuery(): es una consulta JPQL y devuelve la consulta si utilizamos nuestro ejemplo: select count(*) from Anuncio entity.
- getConcreteNativeQuery(): es una consulta SQL Nativa y devuelve la consulta si utilizamos nuestro ejemplo: select * from Anuncio entity.
- getConcreteCountNativeQuery(): es una consulta SQL Nativa y devuelve la consulta si utilizamos nuestro ejemplo: select count(*) from Anuncio entity.
Los métodos anteriores, se pueden utilizar por ejemplo, para establecer el parámetro query en lo métodos que se explican en los siguientes apartados.
También hay que indicar que los métodos que no tienen parámetro query o namedQuery, utilizan internamente los métodos anteriores, para obtener la consulta por defecto que más se ajusta.
Como podeis observar, en las consultas por defecto y en la constante RESTRICTIONS_ANUNCIO de la clase de ejemplo AnuncioDataAccessServiceImpl, se utiliza el alias entity. Este alias se puede cambiar utilizando el método protected void setEntityAlias( String entityAlias ).
Métodos que Devuelven una Lista de Objetos
Estos métodos empiezan con el prefijo find, despues viene la palabra By que indica el tipo de consulta, donde tenemos:
- Query: es una consulta JPQL que devuelve una lista de entidades.
- NativeQuery: es una consulta SQL Nativa que devuelve una lista de entidades.
- MapNativeQuery: es una consulta SQL Nativa que devuelve una lista de Map donde cada clave del mapa, corresponde con una columna del SELECT de la consulta.
- NamedQuery: es una consulta nombrada JPQL o SQL Nativa que devuelve una lista de entidades.
Las consultas que ejecutan son completas o nombradas.
Los métodos de este tipo, que tienen el parámetro QueryType queryType en primer lugar, no es necesario utilizarlo, ya que hay otros métodos que cumplen su función. Esos métodos son finales y de uso más interno.
Métodos que Devuelven el Número de Elementos
Parecidos a los anteriores métodos, pero que devuelven el número de elementos de la consulta.
Estos métodos empiezan con el prefijo countTotalRecord, despues viene la palabra By que indica el tipo de consulta, donde tenemos:
- Query: es una consulta JPQL que devuelve el numero de elementos recuperado por la consulta.
- NativeQuery: es una consulta SQL Nativa que devuelve el numero de elementos recuperado por la consulta.
- NamedQuery: es una consulta nombrada JPQL o SQL Nativa que devuelve el numero de elementos recuperado por la consulta.
Los métodos de este tipo, que tienen el parámetro QueryType queryType en primer lugar, no es necesario utilizarlo, ya que hay otros métodos que cumplen su función. Esos métodos son finales y de uso más interno.
Métodos que Devuelven una Lista de Objetos con Filtros Dinámicos
Igual que los anteriores pero con filtros dinámicos. Un filtro dinámico es un filtro que se añade a la clausula WHERE de la consulta si el parámetro asociado tiene valor, si es nulo, no se añade. Los filtros dinámicos se pasan al método en el parámetro restrictions.
Estos métodos empiezan con el prefijo find, despues viene la palabra By que indica el tipo de consulta, donde tenemos:
- EntityQuery: es una consulta JPQL que devuelve una lista de entidades.
- EntityNamedQuery: es una consulta nombrada JPQL (definida mediante @NamedQuery) que devuelve una lista de entidades.
- EntityNativeQuery: es una consulta SQL Nativa que devuelve una lista de entidades.
- EntityNamedNativeQuery: es una consulta SQL Nativa (definida mediante la @NamedNativeQuery) que devuelve una lista de entidades.
- DtoNamedNativeQuery: es una consulta SQL Nativa (definida mediante la @NamedNativeQuery) que devuelve una lista de DTOs (POJOs).
- MapNativeQuery: es una consulta SQL Nativa que devuelve una lista de Map donde cada clave del mapa, corresponde con una columna del SELECT de la consulta.
- MapNamedNativeQuery: es una consulta SQL Nativa que devuelve una lista de Map (definida mediante la @NamedNativeQuery) donde cada clave del mapa, corresponde con una columna del SELECT de la consulta.
Y terminan con el postfijo WithDinamicFilter.
Los métodos de este tipo, que tienen el parámetro QueryWithDinamicFilterType queryType en primer lugar, no es necesario utilizarlo, ya que hay otros métodos que cumplen su función. Esos métodos son finales y de uso más interno.
Métodos que Devuelven el Número de Elementos con Filtros Dinámicos
Parecidos a los anteriores métodos, pero que devuelven el número de elementos de la consulta.
Estos métodos empiezan con el prefijo countTotalRecord, despues viene la palabra By que indica el tipo de consulta, donde tenemos:
- EntityQuery: es una consulta JPQL que devuelve el numero de elementos recuperado por la consulta.
- EntityNamedQuery: es una consulta nombrada JPQL (definida mediante @NamedQuery) que devuelve el numero de elementos recuperado por la consulta.
- EntityNativeQuery: es una consulta SQL Nativa que devuelve el numero de elementos recuperado por la consulta.
- EntityNamedNativeQuery: es una consulta SQL Nativa (definida mediante la @NamedNativeQuery) que devuelve el numero de elementos recuperado por la consulta.
- DtoNativeQuery: es una consulta SQL Nativa que devuelve una lista de DTOs (POJOs) que devuelve el numero de elementos recuperado por la consulta.
- DtoNamedNativeQuery: es una consulta SQL Nativa (definida mediante la @NamedNativeQuery) que devuelve el numero de elementos recuperado por la consulta.
- MapNativeQuery: es una consulta SQL Nativa que devuelve el numero de elementos recuperado por la consulta.
- MapNamedNativeQuery: es una consulta SQL Nativa que devuelve el numero de elementos recuperado por la consulta.
Los métodos de este tipo, que tienen el parámetro QueryWithDinamicFilterType queryType en primer lugar, no es necesario utilizarlo, ya que hay otros métodos que cumplen su función. Esos métodos son finales y de uso más interno.
Métodos que Devuelven un objeto ResultQuery con Filtros Dinámicos
Igual que los anteriores con filtros dinámicos, pero que devuelven un objeto de la clase ResultQuery. Esta clase contiene la lista de objetos y el número de elementos totales de la consulta.
Estos métodos empiezan con el prefijo results, despues viene la palabra By que indica el tipo de consulta, donde tenemos:
- EntityQuery: es una consulta JPQL que devuelve un objeto de la clase ResultQuery (dentro tiene una lista de entidades).
- EntityNamedQuery: es una consulta nombrada JPQL (definida mediante @NamedQuery) que devuelve un objeto de la clase ResultQuery (dentro tiene una lista de entidades).
- EntityNativeQuery: es una consulta SQL Nativa que devuelve un objeto de la clase ResultQuery (dentro tiene una lista de entidades).
- EntityNamedNativeQuery: es una consulta SQL Nativa (definida mediante la @NamedNativeQuery) que devuelve un objeto de la clase ResultQuery (dentro tiene una lista de entidades).
- DtoNativeQuery: es una consulta SQL Nativa que devuelve un objeto de la clase ResultQuery (dentro tiene una lista de DTOs (POJOs).
- DtoNamedNativeQuery: es una consulta SQL Nativa (definida mediante la @NamedNativeQuery) que devuelve un objeto de la clase ResultQuery (dentro tiene una lista de DTOs (POJOs).
- MapNativeQuery: es una consulta SQL Nativa que devuelve un objeto de la clase ResultQuery (dentro tiene una lista de Map donde cada clave del mapa, corresponde con una columna del SELECT de la consulta).
- MapNamedNativeQuery: es una consulta SQL Nativa que devuelve un objeto de la clase ResultQuery (dentro tiene una lista de Map donde cada clave del mapa, corresponde con una columna del SELECT de la consulta).
Y terminan con el postfijo WithDinamicFilter.
Los métodos de este tipo, que tienen el parámetro QueryWithDinamicFilterType queryType en primer lugar, no es necesario utilizarlo, ya que hay otros métodos que cumplen su función. Esos métodos son finales y de uso más interno.
Información a Tener en Cuenta
Los parámetros más destacados en los métodos de consulta son:
- query: de tipo String, es la cadena de caracteres con la consulta.
- namedQuery: de tipo String, es el nombre de la consulta nombrada, de la cual obtenemos la cadena de caracteres con la consulta.
Si ningúno de los parámetros anteriores aparece, entonces la consulta a ejecutar, es la consulta por defecto.
- restrictions: de tipo List<String>, son las restricciones dinámicas a aplicar en la clausula WHERE de la consulta.
- parameters: de tipo Map<String, Object> parameters, son los parámetros a aplicar en la consulta.
- firstResult: de tipo Integer, el primer elemento a devolver cuando la consulta página. Por defecto es 0, si es null o menor que 0, se aplica el valor por defecto.
- resultLimit: de tipo Integer, el tamaño de la lista a devolver, si es null o menor que 1, se ignora.
- sortField: de tipo String, el campo o columna que se utiliza para realizar la ordenación. Ordenación simple.
- sortOrder: de tipo String, el tipo de la ordenación, admite los valores: ASCENDING, ASC, DESCENDING, DESC o UNSORTED, por defecto UNSORTED. Siempre aparece junto al parámetro sortField.
- order: de tipo String, contiene la ordenación y el tipo de esta, cuando se ordena por más de un campo o columna. Cada dupla columna-ordenacion se separa de la siguiente mediante comas (,).
Los parámetros sortField y sortOrder, siempre aparecen juntos. Forman la ordenación simple.
El parámetro order NUNCA aparece junto con los parámetros sortField y sortOrder. Se utiliza para la ordenación múltiple.
- logicOperator: de tipo String, contiene el operador logico para las restricciones. Los valores disponibles: AND y OR, por defecto AND.
- hints: de tipo Map<String, String>, contiene los JPA hints de configuración a aplicar a la consulta.
- wherePriority: de tipo QueryPriority, indica que clausula WHERE se utiliza en la consulta, cuando esta esta definida tanto en la propia consulta y además definimos filtros dinámicos (en el parámetro restrictions). Los valores disponibles son: QUERY, JAVA y BOTH. El valor por defecto BOTH.
- orderPriority: de tipo QueryPriority, indica que clausula ORDER BY se utiliza en la consulta, cuando esta esta definida tanto en la propia consulta y además en los parámetros (en la dupla de parámetros sortField y sortOrder, o en el parámetro order). Los valores disponibles son: QUERY, JAVA y BOTH. El valor por defecto JAVA.
- groupPriority: de tipo QueryPriority, indica que clausula GROUP BY se utiliza en la consulta, cuando esta esta definida tanto en la propia consulta y además en los parámetros (en la dupla de parámetros sortField y sortOrder, o en el parámetro order). Los valores disponibles son: QUERY, JAVA y BOTH. El valor por defecto JAVA.
Los parámetros que son de tipo QueryPriority, pueden tener los valores:
- QUERY: se toma el valor definido en la consulta para configurar la clausula indicada.
- JAVA: se toma el valor definido en los parámetros del método para configurar la clausula indicada.
- BOTH: se toma tanto el valor definido en la consulta como el valor definido en los parámetros del método para configurar la clausula indicada.
El Parámetro useWildcardAsCountQuerySubject
El parámetro useWildcardAsCountQuerySubject es un valor booleano (valor por defecto true), que permite indicar si se utiliza o no el wildcard * dentro del COUNT al construir la consulta count asociada, que es la opción por defecto.
Cuando se utilizan clausulas como DISCTINC en el select de una consulta, si el valor de useWildcardAsCountQuerySubject es true (que es el valor por defecto), la consulta count asociada, devolvera un valor incorrecto.
Para obtener el valor correcto en la consulta count asociada, tenemos que poner el valor false para el parámetro useWildcardAsCountQuerySubject, para que dentro de la clausula COUNT aparezca lo que tenemos en el select de la consulta filtrada.
Vamos un ejemplo. La consulta SELECT a FROM Author a JOIN FETCH a.books obtiene los autores de los libros, pero si un autor aparece en más de un libro, el autor aparecera repetido en el resultado.
La consulta SELECT DISTINCT a FROM Author a JOIN FETCH a.books obtiene los autores de los libros, pero si un autor aparece en mas de un libro, solo parece una vez en el resultado.
Si el valor del parámetro useWildcardAsCountQuerySubject es true (valor por defecto) para ambas consultas, la consulta count asociada será SELECT COUNT(*) FROM Author a JOIN FETCH a.books y devolvera el mismo valor, cuando es evidente que no devuelven el mismo número de elementos.
Para la segunda consulta, deberemos poner el valor false al parámetro useWildcardAsCountQuerySubject. Y obtendremos la consulta SELECT COUNT(DISTINCT a) FROM Author a JOIN FETCH a.books, obteniendo el valor correcto.
Lecturas Recomendadas
- fdw2.0/fundeweb2.0/gt/gt-entender-das.txt
- Última modificación: 14/01/2019 10:45
- por JUAN MIGUEL BERNAL GONZALEZ