setMaxResults para la anotación Spring-Data-JPA?

127

Estoy tratando de incorporar Spring-Data-JPA en mi proyecto. Una cosa que me confunde es cómo puedo lograr setMaxResults (n) por anotación.

por ejemplo, mi código:

public interface UserRepository extends CrudRepository<User , Long>
{
  @Query(value="From User u where u.otherObj = ?1 ")
  public User findByOhterObj(OtherObj otherObj);
}

Solo necesito devolver one (and only one)User de otherObj, pero no puedo encontrar una manera de anotar maxResults. ¿Alguien puede darme una pista?

(mysql se queja:

com.mysql.jdbc.JDBC4PreparedStatement@5add5415: select user0_.id as id100_, user0_.created as created100_ from User user0_ where user0_.id=2 limit ** NOT SPECIFIED **
WARN  util.JDBCExceptionReporter - SQL Error: 0, SQLState: 07001
ERROR util.JDBCExceptionReporter - No value specified for parameter 2

)

Encontré un enlace: https://jira.springsource.org/browse/DATAJPA-147 , lo intenté pero fallé. Parece que no es posible ahora? ¿Por qué una característica tan importante no está integrada en Spring-Data?

Si implemento esta función manualmente:

public class UserRepositoryImpl implements UserRepository

Tengo que implementar toneladas de métodos predefinidos CrudRepository, esto sería terrible.

entornos: spring-3.1, spring-data-jpa-1.0.3.RELEASE.jar, spring-data-commons-core-1.1.0.RELEASE.jar

smallufo
fuente

Respuestas:

176

A partir de Spring Data JPA 1.7.0 (tren de lanzamiento de Evans).

Puede utilizar las palabras clave recientemente introducidas Topy Firstque le permiten definir métodos de consulta como este:

findTop10ByLastnameOrderByFirstnameAsc(String lastname);

Spring Data limitará automáticamente los resultados al número que definió (omitiendo a 1 si se omite). Tenga en cuenta que el orden de los resultados se vuelve relevante aquí (ya sea a través de una OrderBycláusula como se ve en el ejemplo o al pasar un Sortparámetro al método). Lea más sobre eso en la publicación del blog que cubre las nuevas características del tren de lanzamiento de Spring Data Evans o en la documentación .

Para versiones anteriores

Para recuperar solo segmentos de datos, Spring Data utiliza la abstracción de paginación que viene con una Pageableinterfaz en el lado solicitante, así como una Pageabstracción en el lado del resultado de las cosas. Entonces podrías comenzar con

public interface UserRepository extends Repository<User, Long> {

  List<User> findByUsername(String username, Pageable pageable);
}

y úsalo así:

Pageable topTen = new PageRequest(0, 10);
List<User> result = repository.findByUsername("Matthews", topTen);

Si necesita conocer el contexto del resultado (¿qué página es en realidad? ¿Es la primera? ¿Cuántas hay en total?), Use Pagecomo tipo de retorno:

public interface UserRepository extends Repository<User, Long> {

  Page<User> findByUsername(String username, Pageable pageable);
}

El código del cliente puede hacer algo como esto:

Pageable topTen = new PageRequest(0, 10);
Page<User> result = repository.findByUsername("Matthews", topTen);
Assert.assertThat(result.isFirstPage(), is(true));

No es que activemos una proyección de conteo de la consulta real que se ejecutará en caso de que use Pagecomo tipo de retorno, ya que necesitamos averiguar cuántos elementos hay en total para calcular los metadatos. Más allá de eso, asegúrese de equipar la PageRequestinformación de clasificación para obtener resultados estables. De lo contrario, podría activar la consulta dos veces y obtener resultados diferentes incluso sin que los datos hayan cambiado debajo.

Oliver Drotbohm
fuente
14
Gracias, pero todavía espero una solución 'basada en anotaciones'. Porque 'Page / Pageable' es demasiado exagerado en este caso. Y el cliente tiene que crear un Pageable / Page para recuperar el resultado 'uno y solo uno' ... No es fácil de usar. Y parece que está a cargo de Spring-Data, espero que pueda investigar más este problema: stackoverflow.com/questions/9276461/… . ¿Puedes confirmar si es un error de Spring-Data?
smallufo
1
+ esto funciona, gracias, pero una solución basada en anotaciones sería mejor en versiones más recientes
azerafati
1
aquí se dispara una consulta de conteo que es totalmente innecesaria si queremos una consulta limitada.
azerafati
1
No lo es si lo usa Listcomo el tipo de retorno del método.
Oliver Drotbohm
3
Creo, ¿ Topy las Firstpalabras clave no son aplicables a los métodos que usan @Queryanotaciones? ¿Solo aquellos que utilizan la generación de consultas basada en el nombre del método?
Ruslan Stelmachenko
106

Si está utilizando Java 8 y Spring Data 1.7.0, puede usar métodos predeterminados si desea combinar una @Queryanotación con la configuración de resultados máximos:

public interface UserRepository extends PagingAndSortingRepository<User,Long> {
  @Query("from User u where ...")
  List<User> findAllUsersWhereFoo(@Param("foo") Foo foo, Pageable pageable);

  default List<User> findTop10UsersWhereFoo(Foo foo) {
    return findAllUsersWhereFoo(foo, new PageRequest(0,10));
  }

}
Wim Deblauwe
fuente
3
potencialmente útil para un solo resultadoStream<User> ... {...} default Optional<User> ... { ...findAny() }
xenoterracide
28

Hay una forma en que puede proporcionar el equivalente de "un setMaxResults (n) por anotación" como en el siguiente:

public interface ISomething extends JpaRepository<XYZ, Long>
{
    @Query("FROM XYZ a WHERE a.eventDateTime < :before ORDER BY a.eventDateTime DESC")
    List<XYZ> findXYZRecords(@Param("before") Date before, Pageable pageable);
}

Esto debería hacer el truco, cuando se envía un pageable como parámetro. Por ejemplo, para obtener los primeros 10 registros, debe establecer que se pueda paginar a este valor:

new PageRequest(0, 10)
ncaralicea
fuente
Si está usando Pagable, entonces su página de retorno <XYZ> no
aparece en la
@Arundev está mal: Listfunciona perfectamente (y evita una countconsulta innecesaria como ya se mencionó en un comentario anterior )
Janaka Bandara
21

Utilice Spring Data Evans (LIBERACIÓN 1.7.0)

La nueva versión de Spring Data JPA con otra lista de módulos juntos llamada Evans tiene la característica de usar palabras clave Top20y Firstlimitar el resultado de la consulta,

para que ahora puedas escribir

List<User> findTop20ByLastname(String lastname, Sort sort);

o

List<User> findTop20ByLastnameOrderByIdDesc(String lastname);

o por un solo resultado

List<User> findFirstByLastnameOrderByIdDesc(String lastname);
azerafati
fuente
55
Opcional <User> findFirstByLastnameOrderByIdDesc (String lastname);
krmanish007
Después de obtener los 20 mejores resultados, ¿cómo obtendría los próximos 20 resultados cuando vaya a la página siguiente en el sitio web utilizando la paginación?
kittu
@kittu, esa es, por supuesto, otra pregunta, pero debe pasar un Pageableobjeto. echa un vistazo a la documentación de primavera
azerafati
¿Por qué hicieron que el método devolviera una maldita lista si se especifica PRIMERO?
Vasile Surdu
13

La mejor opción para mí es la consulta nativa:

@Query(value="SELECT * FROM users WHERE other_obj = ?1 LIMIT 1", nativeQuery = true)
User findByOhterObj(OtherObj otherObj);
Grigory Kislin
fuente
No estoy seguro si el límite es compatible con Hibernate: forum.hibernate.org/viewtopic.php?f=9&t=939314
Witold Kaczurba
2
Rompe todo el concepto! ¡Mejor use EntityManager en su lugar!
Spektakulatius
@ Code.IT Propago el concepto KISS. Aparte de ese nativeQuery parámetro de anotación JPA agregado no para ignorar.
Grigory Kislin
@GKislin que tiene que cambiar el nombre de su método como findLimitOneByOtherObj. También ha olvidado agregar un OrderBy que conduce a un resultado incorrecto si other_obj no es único. De lo contrario, una declaración where en una columna única debería ser suficiente sin un límite uno
Spektakulatius
La palabra clave LIMIT no es hibernate sino postgres / mysql
Acewin
5

Si su clase se @Repositoryextiende JpaRepository, puede usar el siguiente ejemplo.

int limited = 100;
Pageable pageable = new PageRequest(0,limited);
Page<Transaction> transactionsPage = transactionRepository.findAll(specification, pageable);
return transactionsPage.getContent();

getContent return a List<Transaction>.

Luis Camargo
fuente
¿Qué pasa si tengo 2 donde las condiciones en la consulta como, transacciónRepository.findByFirstNameAndLastName (nombre, apellido)? Funcionará ? Recibo esta excepción org.springframework.data.mapping.PropertyReferenceException: no se encontró ninguna propiedad de creación para el tipo Board.
Shailesh
4

También es posible usar @QueryHints. El siguiente ejemplo usa org.eclipse.persistence.config.QueryHints # JDBC_MAX_ROWS

@Query("SELECT u FROM User u WHERE .....")
@QueryHints(@QueryHint(name = JDBC_MAX_ROWS, value = "1"))
Voter findUser();
kraken
fuente
He probado este código pero no funciona. No hay excepción, pero no hay un resultado adecuado más cercano. Además, la documentación es muy escasa sobre QueryHints. Vea abajo. docs.spring.io/spring-data/jpa/docs/1.7.1.RELEASE/reference/…
bogdan.rusu
IIRC, no hay garantías de que se siga una sugerencia de consulta. Es solo una forma de pasar "recomendaciones amigables" a la implementación.
Priidu Neemre
Lo intento con @QueryHints({@QueryHint(name = org.hibernate.annotations.FETCH_SIZE, value = "1")})Hibernate, pero el alias :( no funciona
Grigory Kislin
2
org.hibernate.annotations.FETCH_SIZE su tamaño de recuperación, no es límite, no es el resultado máximo. Hiber cant
Zhuravskiy Vitaliy
4

new PageRequest(0,10)no funciona en las nuevas versiones de Spring (estoy usando 2.2.1.RELEASE). Básicamente, el constructor obtuvo un parámetro adicional como Sorttipo. Además, el constructor es protectedasí que debe usar una de sus clases secundarias o llamar a su ofmétodo estático:

PageRequest.of(0, 10, Sort.sort(User.class).by(User::getFirstName).ascending()))

También puede omitir el uso del Sortparámetro y usar implícitamente el orden predeterminado (ordenar por pk, etc.):

PageRequest.of(0, 10)

Su declaración de función debería ser algo como esto:

List<User> findByUsername(String username, Pageable pageable)

y la función será:

userRepository.findByUsername("Abbas", PageRequest.of(0,10, Sort.sort(User.class).by(User::getLastName).ascending());
Abbas Hosseini
fuente