setMaxResults para anotação Spring-Data-JPA?

127

Estou tentando incorporar o Spring-Data-JPA no meu projeto. Uma coisa que me confunde é como obter setMaxResults (n) por anotação?

por exemplo, meu código:

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

Eu só preciso retornar one (and only one)User de otherObj, mas não consigo encontrar uma maneira de anotar os maxResults. Alguém pode me dar uma dica?

(o mysql reclama:

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

)

Encontrei um link: https://jira.springsource.org/browse/DATAJPA-147 , tentei, mas falhei. Parece que não é possível agora? Por que um recurso tão importante não é incorporado ao Spring-Data?

Se eu implementar esse recurso manualmente:

public class UserRepositoryImpl implements UserRepository

Eu tenho que implementar toneladas de métodos predefinidos CrudRepository, isso seria terrível.

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

smallufo
fonte

Respostas:

176

A partir do Spring Data JPA 1.7.0 (trem de liberação de Evans).

Você pode usar as palavras Top- Firstchave e recém-introduzidas que permitem definir métodos de consulta como este:

findTop10ByLastnameOrderByFirstnameAsc(String lastname);

O Spring Data limitará automaticamente os resultados ao número que você definiu (o padrão é 1 se omitido). Observe que a ordem dos resultados se torna relevante aqui (por meio de uma OrderBycláusula como vista no exemplo ou pela entrega de um Sortparâmetro ao método). Leia mais sobre isso na postagem do blog que aborda novos recursos do trem de lançamento do Spring Data Evans ou na documentação .

Para versões anteriores

Para recuperar apenas fatias de dados, o Spring Data usa a abstração de paginação que vem com uma Pageableinterface no lado solicitante, bem como uma Pageabstração no lado do resultado. Então você pode começar com

public interface UserRepository extends Repository<User, Long> {

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

e use-o assim:

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

Se você precisar conhecer o contexto do resultado (qual página é realmente? É a primeira? Quantas existem no total?), Use Pagecomo tipo de retorno:

public interface UserRepository extends Repository<User, Long> {

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

O código do cliente pode fazer algo assim:

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

Não que disparemos uma projeção de contagem da consulta real a ser executada, caso você use Pagecomo tipo de retorno, pois precisamos descobrir quantos elementos existem no total para calcular os metadados. Além disso, certifique-se de equipar as PageRequestinformações de classificação para obter resultados estáveis. Caso contrário, você poderá acionar a consulta duas vezes e obter resultados diferentes, mesmo sem os dados serem alterados abaixo.

Oliver Drotbohm
fonte
14
Obrigado, mas ainda espero por uma solução 'baseada em anotação'. Como 'Página / Paginável' é um exagero demais nesse caso. E o cliente precisa criar uma página / paginável para recuperar o resultado 'um e apenas um' ... É muito pouco amigável de usar. E parece que você é responsável pelo Spring-Data, espero que você possa analisar melhor esse problema: stackoverflow.com/questions/9276461/… . Você pode confirmar se é o bug do Spring-Data?
Smallfo
1
+ Isso funciona, obrigado, mas uma solução anotação baseada seria melhor em versões mais recentes
azerafati
1
uma consulta de contagem é acionada aqui, o que é totalmente desnecessário se queremos uma consulta limitada.
Azerafati
1
Não é se você usar Listcomo o tipo de retorno do método.
Oliver Drotbohm
3
Eu acho, Tope as Firstpalavras - chave não são aplicáveis ​​aos métodos que usam @Queryanotações? Somente aqueles que usam geração de consulta baseada em nome do método?
Ruslan Stelmachenko
106

Se você estiver usando o Java 8 e o Spring Data 1.7.0, poderá usar métodos padrão se desejar combinar uma @Queryanotação com a definição 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
fonte
3
potencialmente útil para um único resultadoStream<User> ... {...} default Optional<User> ... { ...findAny() }
xenoterracide
28

Existe uma maneira de fornecer o equivalente a "um setMaxResults (n) por anotação", como no seguinte:

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);
}

Isso deve funcionar, quando um paginável é enviado como parâmetro. Por exemplo, para buscar os 10 primeiros registros, você precisa definir paginável para este valor:

new PageRequest(0, 10)
ncaralicea
fonte
Se você estiver usando o Pagable, seu retorno não <Listar <XYZ> não <XYZ>
Arundev 4/17/17
@Arundev errado - Listfunciona perfeitamente (e evita uma desnecessária countconsulta como já mencionado em um comentário anterior )
Janaka Bandara
21

Use o Spring Data Evans (1.7.0 RELEASE)

a nova versão do Spring Data JPA com outra lista de módulos chamada Evans possui o recurso de usar palavras Top20- chave e Firstlimitar o resultado da consulta,

agora você pode escrever

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

ou

List<User> findTop20ByLastnameOrderByIdDesc(String lastname);

ou para um único resultado

List<User> findFirstByLastnameOrderByIdDesc(String lastname);
azerafati
fonte
5
<Usuário> opcional findFirstByLastnameOrderByIdDesc (String lastname);
krmanish007
Depois de obter os 20 melhores resultados, como obter os próximos 20 resultados quando for para a próxima página do site usando paginação?
Kittu
@kittu, é claro que é outra questão, mas você precisa passar um Pageableobjeto. confira a documentação primavera
azerafati
por que eles fizeram o método retornar uma lista maldita se foi especificado PRIMEIRO?
Vasile Surdu
13

A melhor opção para mim é a consulta nativa:

@Query(value="SELECT * FROM users WHERE other_obj = ?1 LIMIT 1", nativeQuery = true)
User findByOhterObj(OtherObj otherObj);
Grigory Kislin
fonte
Não tenho certeza se o limite é suportado no Hibernate: forum.hibernate.org/viewtopic.php?f=9&t=939314
Witold Kaczurba
2
Quebra todo o conceito! Melhor usar o EntityManager!
Spektakulatius
@ Code.IT Propago o conceito KISS. Além do nativeQuery parâmetro de anotação JPA adicionado, não para ignorar.
Grigory Kislin
@GKislin do que você precisa renomear seu método como findLimitOneByOtherObj. Além disso, você esqueceu de adicionar um OrderBy que leva ao resultado errado se o other_obj não for exclusivo. Caso contrário, uma declaração onde em uma coluna exclusiva deve ser suficiente sem um limite de um
Spektakulatius
A palavra-chave LIMIT não é hibernada, mas postgres / mysql
Acewin
5

Se sua turma @Repositoryse estender, JpaRepositoryvocê pode usar o exemplo abaixo.

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

retorno getContent a List<Transaction>.

Luis Camargo
fonte
e se eu tiver 2 onde condições na consulta como, transactionRepository.findByFirstNameAndLastName (nome, sobrenome)? será que vai dar certo ? Estou recebendo essa exceção org.springframework.data.mapping.PropertyReferenceException: nenhuma propriedade criada foi encontrada para o tipo Board.
Shailesh
4

Também é possível usar o @QueryHints. O exemplo a seguir 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
fonte
Eu testei esse código, mas não está funcionando. Sem exceção, mas sem resultados adequados mais recentes. Além disso, a documentação é muito fina sobre o QueryHints. Ver abaixo. docs.spring.io/spring-data/jpa/docs/1.7.1.RELEASE/reference/…
bogdan.rusu
IIRC, não há garantias de que uma dica de consulta será seguida. É apenas uma maneira de passar "recomendações amigáveis" para a implementação.
Priidu Neemre
I tentar com @QueryHints({@QueryHint(name = org.hibernate.annotations.FETCH_SIZE, value = "1")})no Hibernate, mas apelido :( não funciona
Grigory Kislin
2
org.hibernate.annotations.FETCH_SIZE seu tamanho de busca, seu limite não, resultados máximos. Hiber não é possível
Zhuravskiy Vitaliy
4

new PageRequest(0,10)não funciona nas versões mais recentes do Spring (estou usando 2.2.1.RELEASE). Basicamente, o construtor obteve um parâmetro adicional como Sorttipo. Além disso, o construtor é protectedpara que você precise usar uma de suas classes filho ou chamar seu ofmétodo estático:

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

Você também pode omitir o uso do Sortparâmetro e usar implicitamente a classificação padrão (classificar por pk, etc.):

PageRequest.of(0, 10)

Sua declaração de função deve ser algo como isto:

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

e a função será:

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