Tabla de Contenidos

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:

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:

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:

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:

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:

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:

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:

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:

Si ningúno de los parámetros anteriores aparece, entonces la consulta a ejecutar, es la consulta por defecto.

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.

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


JUAN MIGUEL BERNAL GONZALEZ 30/11/2018 09:19