Consulta de Spring Data JPA por ejemplo

1. Introducción

En este tutorial, aprenderemos cómo consultar datos con la API Spring Data Query by Example .

Primero, definiremos el esquema de los datos que queremos consultar. A continuación, examinaremos algunas de las clases relevantes de Spring Data. Y luego, repasaremos algunos ejemplos.

¡Empecemos!

2. Los datos de prueba

Nuestros datos de prueba son una lista de los nombres de los pasajeros, así como el asiento que ocuparon.

Primer nombre Apellido Número de asiento
Jill Herrero 50
Víspera Jackson 94
Fred Bloggs 22
Ricki Bobbie 36
Siya Kolisi 85

3. Dominio

Creemos el repositorio de datos de Spring que necesitamos y proporcionemos nuestra clase de dominio y tipo de identificación.

Para empezar, hemos modelado a nuestro Passenger como una entidad JPA:

@Entity class Passenger { @Id @GeneratedValue @Column(nullable = false) private Long id; @Basic(optional = false) @Column(nullable = false) private String firstName; @Basic(optional = false) @Column(nullable = false) private String lastName; @Basic(optional = false) @Column(nullable = false) private int seatNumber; // constructor, getters etc. }

En lugar de usar JPA, podríamos haberlo modelado como otra abstracción.

4. Consulta por API de ejemplo

En primer lugar, echemos un vistazo a la interfaz de JpaRepository . Como podemos ver, extiende la interfaz QueryByExampleExecutor para admitir consultas por ejemplo:

public interface JpaRepository extends PagingAndSortingRepository, QueryByExampleExecutor {}

Esta interfaz presenta más variantes del método find () con el que estamos familiarizados en Spring Data. Sin embargo, cada método también acepta una instancia de Example :

public interface QueryByExampleExecutor {  Optional findOne(Example var1);  Iterable findAll(Example var1);  Iterable findAll(Example var1, Sort var2);  Page findAll(Example var1, Pageable var2);  long count(Example var1);  boolean exists(Example var1); }

En segundo lugar, la interfaz Example expone métodos para acceder a la sonda y al ExampleMatcher .

Es importante darse cuenta de que la sonda es la instancia de nuestra Entidad :

public interface Example { static  org.springframework.data.domain.Example of(T probe) { return new TypedExample(probe, ExampleMatcher.matching()); } static  org.springframework.data.domain.Example of(T probe, ExampleMatcher matcher) { return new TypedExample(probe, matcher); } T getProbe(); ExampleMatcher getMatcher(); default Class getProbeType() { return ProxyUtils.getUserClass(this.getProbe().getClass()); } }

En resumen, nuestra sonda y nuestro ExampleMatcher juntos especifican nuestra consulta.

5. Limitaciones

Como todas las cosas, la API de consulta por ejemplo tiene algunas limitaciones. Por ejemplo:

  • No se admiten declaraciones de agrupamiento y anidamiento, por ejemplo: ( firstName =? 0 and lastName =? 1) or seatNumber =? 2
  • La concordancia de cadenas solo incluye inicios, finales, contenidos y expresiones regulares exactos, que no distinguen entre mayúsculas y minúsculas
  • Todos los tipos distintos de String son solo de coincidencia exacta

Ahora que estamos un poco más familiarizados con la API y sus limitaciones, veamos algunos ejemplos.

6. Ejemplos

6.1. Coincidencia sensible a mayúsculas y minúsculas

Comencemos con un ejemplo simple y hablemos sobre el comportamiento predeterminado:

@Test public void givenPassengers_whenFindByExample_thenExpectedReturned() { Example example = Example.of(Passenger.from("Fred", "Bloggs", null)); Optional actual = repository.findOne(example); assertTrue(actual.isPresent()); assertEquals(Passenger.from("Fred", "Bloggs", 22), actual.get()); }

En particular, el método estático Example.of () crea un Example usando ExampleMatcher.matching () .

En otras palabras, se realizará una coincidencia exacta en todas las propiedades no nulas de Passenger . Por lo tanto, la coincidencia distingue entre mayúsculas y minúsculas en las propiedades de String .

Sin embargo, no sería demasiado útil si todo lo que pudiéramos hacer fuera una coincidencia exacta en todas las propiedades no nulas.

Aquí es donde entra en juego el ExampleMatcher . Al construir nuestro propio ExampleMatcher , podemos personalizar el comportamiento para satisfacer nuestras necesidades.

6.2. Coincidencia que no distingue entre mayúsculas y minúsculas

Con eso en mente, echemos un vistazo a otro ejemplo, esta vez usando withIgnoreCase () para lograr una coincidencia que no distinga entre mayúsculas y minúsculas:

@Test public void givenPassengers_whenFindByExampleCaseInsensitiveMatcher_thenExpectedReturned() { ExampleMatcher caseInsensitiveExampleMatcher = ExampleMatcher.matchingAll().withIgnoreCase(); Example example = Example.of(Passenger.from("fred", "bloggs", null), caseInsensitiveExampleMatcher); Optional actual = repository.findOne(example); assertTrue(actual.isPresent()); assertEquals(Passenger.from("Fred", "Bloggs", 22), actual.get()); }

En este ejemplo, observe que primero llamamos ExampleMatcher.matchingAll () - tiene el mismo comportamiento que ExampleMatcher.matching () , que usamos en el ejemplo anterior.

6.3. Coincidencia personalizada

También podemos ajustar el comportamiento de nuestro comparador por propiedad y hacer coincidir cualquier propiedad usando ExampleMatcher.matchingAny () :

@Test public void givenPassengers_whenFindByExampleCustomMatcher_thenExpectedReturned() { Passenger jill = Passenger.from("Jill", "Smith", 50); Passenger eve = Passenger.from("Eve", "Jackson", 95); Passenger fred = Passenger.from("Fred", "Bloggs", 22); Passenger siya = Passenger.from("Siya", "Kolisi", 85); Passenger ricki = Passenger.from("Ricki", "Bobbie", 36); ExampleMatcher customExampleMatcher = ExampleMatcher.matchingAny() .withMatcher("firstName", ExampleMatcher.GenericPropertyMatchers.contains().ignoreCase()) .withMatcher("lastName", ExampleMatcher.GenericPropertyMatchers.contains().ignoreCase()); Example example = Example.of(Passenger.from("e", "s", null), customExampleMatcher); List passengers = repository.findAll(example); assertThat(passengers, contains(jill, eve, fred, siya)); assertThat(passengers, not(contains(ricki))); }

6.4. Ignorando propiedades

Por otro lado, es posible que también queramos consultar solo un subconjunto de nuestras propiedades .

Logramos esto ignorando algunas propiedades usando ExampleMatcher.ignorePaths (String… rutas) :

@Test public void givenPassengers_whenFindByIgnoringMatcher_thenExpectedReturned() { Passenger jill = Passenger.from("Jill", "Smith", 50); Passenger eve = Passenger.from("Eve", "Jackson", 95); Passenger fred = Passenger.from("Fred", "Bloggs", 22); Passenger siya = Passenger.from("Siya", "Kolisi", 85); Passenger ricki = Passenger.from("Ricki", "Bobbie", 36); ExampleMatcher ignoringExampleMatcher = ExampleMatcher.matchingAny() .withMatcher("lastName", ExampleMatcher.GenericPropertyMatchers.startsWith().ignoreCase()) .withIgnorePaths("firstName", "seatNumber"); Example example = Example.of(Passenger.from(null, "b", null), ignoringExampleMatcher); List passengers = repository.findAll(example); assertThat(passengers, contains(fred, ricki)); assertThat(passengers, not(contains(jill)); assertThat(passengers, not(contains(eve)); assertThat(passengers, not(contains(siya)); }

7. Conclusión

En este artículo, hemos demostrado cómo utilizar la API de consulta por ejemplo.

Hemos demostrado cómo usar Example y ExampleMatcher junto con la interfaz QueryByExampleExecutor para consultar una tabla usando una instancia de datos de ejemplo.

En conclusión, puede encontrar el código en GitHub.