Participante
s usando su campo nombre
) y en nuestro proyecto seguramente habrá aspectos que no podamos solucionar con lo visto hasta ahora. Como ejemplo nos gustaría recuperar los Partido
s de un Participante
con un texto en su nombre
. En este caso tenemos un problema: en la tabla PARTIDOS
no están los nombres de los participantes, sólo sus ids y ya no es suficiente con usar los Query Methods de JPA para solucionarlo.En este caso necesitamos hacer un método personalizado para nuestro repositorio.
Para implementar esta funcionalidad vamos a hacer lo siguiente:
- Crear una búsqueda para buscar los participantes conteniendo un texto (eso ya lo tenemos:
ParticipanteDAO.findByNombreIgnoreCaseContaining
) - Con los participantes recuperados buscaremos los partidos en los que aparecen
- Añadiremos todos los partidos de esos participantes a una lista
- Esa lista será el payload de nuestra respuesta HTTP
JpaRepository
. Esas interfaces tendrán los métodos que queremos añadir. En nuestro caso me creo la interface EventoDAOCustom
:public interface EventoDAOCustom<T extends Evento> {
List<T> getEventosConParticipanteConTexto(String txt);
}
De esta forma esta interface me servirá para otro tipo de eventos, no sólo para partidos.Ahora me creo una clase que implemente este método. Lo más importante es que debe tener el sufijo "
Impl
" e implementar nuestra nueva interface. Nos quedará así:class PartidoDAOImpl implements EventoDAOCustom<PartidoConId> {
@Autowired
ParticipanteDAO participanteDAO;
@PersistenceContext
EntityManager entityManager;
@Override
public List<PartidoConId> getEventosConParticipanteConTexto(String txt) {
List<Participante> participantes = participanteDAO.findByNombreIgnoreCaseContaining(txt);
Set<PartidoConId> partidos = new HashSet<PartidoConId>();
Query query = entityManager.createNativeQuery(
"SELECT p.* FROM partidos as p " +
"WHERE p.local = ?1 OR p.visitante = ?1", PartidoConId.class);
participantes.forEach(p -> {
query.setParameter(1, p.getIdentificador());
partidos.addAll(query.getResultList());
});
return new ArrayList<PartidoConId>(partidos);
}
}
Para implementar este método usamos ParticipanteDAO
para recuperar los participantes y luego una Query
que es bastante parecida a lenguaje SQL que ya conocemos como se ve en el ejemplo.
NOTA: la query necesita del
EntityManager
y lo inyectamos con @PersistenceContext
.
Usamos la query para ir llamando a nuestra BD con cada participante que cumpla con el texto pasado y lo añadimos a un
Set
para que no se repita. Finalmente devolvemos todos los partidos. Podrían hacerse más cosas como ordenar por algún campo, etc...Sólo nos queda añadir la interface a nuestro DAO para que Spring lo complete:
public interface PartidoDAO extends JpaRepository<PartidoConId, Long>, EventoDAOCustom<PartidoConId> {...}
Lo importante de todo esto es que puedo utilizar código propio para implementar aquello que ya se escapa de los Query Methods de JPA. Sin necesidad de añadirlo como bean ni nada parecido, Data Rest va a escanear las clases con el sufijo
Impl
(se puede configurar) buscando la implementación de EventoDAOCustom
que no puede hacer automáticamente. Así irá formando con fragmentos de código el DAO completo.Ahora probamos que todo funciona correctamente en nuestro
main
:partidoDAO.getEventosConParticipanteConTexto("m").stream()
.map(PartidoConId::toString)
.forEach(log::trace);
Para que esto funcione de esta forma debemos indicar que este método es transaccional. Como habitualmente estas consultas personalizadas son lecturas de datos, marcamos toda la clase como sólo lectura.@Transactional(readOnly = true)
class PartidoDAOImpl implements EventoDAOCustom<PartidoConId> {...}
NOTA: ver más sobre cómo funciona
@PersistentContext
y @Transactional
.
Puedes conseguir el código hasta aquí en su repositorio.
Añadida la funcionalidad a nuestra persistencia, lo que nos falta es exponerla en nuestra API. En la siguiente entrada vamos a ver cómo hacerlo.
Práctica: mejorar cómo se consiguen los participantes
Ahora mismo si buscamos un equipo donde su nombre incluya una tilde en el fragmento buscado y no coincida en el valor de
txt
(ejemplo: buscando "?txt=atletico
" no devolverá "Atlético...") parece que no obtenemos el resultado esperado. Se pide modificar el código para que no sólo sea IgnoreCase
sino que también ignore las tildes.
NOTA: aparte de
ParticipanteDAO
tenemos otro origen de datos disponible para ello en forma de bean. Para facilitar el tratamiento de las tildes existe un método en lanyu.commons
que se encarga de ello: StringUtils.eliminarTildes
.
La solución está en github.
No hay comentarios:
Publicar un comentario