setMaxResults для аннотации Spring-Data-JPA?

128

Я пытаюсь включить Spring-Data-JPA в свой проект. Меня смущает одна вещь: как добиться setMaxResults (n) с помощью аннотации?

например, мой код:

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

Мне нужно только вернуть one (and only one)User из otherObj, но я не могу найти способ аннотировать maxResults. Кто-нибудь может мне намекнуть?

(mysql жалуется:

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

)

Я нашел ссылку: https://jira.springsource.org/browse/DATAJPA-147 , попробовал, но не удалось. Кажется, сейчас это невозможно? Почему такая важная функция не встроена в Spring-Data?

Если я реализую эту функцию вручную:

public class UserRepositoryImpl implements UserRepository

Мне нужно реализовать множество предопределенных методов CrudRepository, это было бы ужасно.

среды: Spring-3.1, Spring-data-jpa-1.0.3.RELEASE.jar, Spring-data-commons-core-1.1.0.RELEASE.jar

smallufo
источник

Ответы:

176

Начиная с Spring Data JPA 1.7.0 (поезд выпуска Evans).

Вы можете использовать вновь вводимые Topи Firstключевые слова , которые позволяют определить методы запроса , как это:

findTop10ByLastnameOrderByFirstnameAsc(String lastname);

Spring Data автоматически ограничит результаты указанным вами числом (по умолчанию 1, если не указано). Обратите внимание, что здесь становится актуальным порядок результатов (либо с помощью OrderByпредложения, как показано в примере, либо путем передачи Sortпараметра в метод). Подробнее об этом читайте в сообщении блога, посвященном новым функциям выпуска Spring Data Evans, или в документации .

Для предыдущих версий

Чтобы получить только фрагменты данных, Spring Data использует абстракцию разбиения на страницы, которая поставляется с Pageableинтерфейсом на стороне запроса, а также Pageабстракцией на стороне результата. Итак, вы можете начать с

public interface UserRepository extends Repository<User, Long> {

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

и используйте это так:

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

Если вам нужно знать контекст результата (какая это страница на самом деле? Это первая? Сколько их всего?), Используйте в Pageкачестве возвращаемого типа:

public interface UserRepository extends Repository<User, Long> {

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

Затем клиентский код может делать что-то вроде этого:

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

Не то, чтобы мы запускали проекцию подсчета фактического запроса, который должен быть выполнен, если вы используете в Pageкачестве возвращаемого типа, поскольку нам нужно выяснить, сколько всего элементов существует для расчета метаданных. Помимо этого, убедитесь, что вы действительно снабдили его PageRequestинформацией для сортировки, чтобы получить стабильные результаты. В противном случае вы можете запустить запрос дважды и получить разные результаты даже без изменения данных внизу.

Оливер Дротбом
источник
14
Спасибо, но я все еще надеюсь на решение, основанное на аннотациях. Потому что в этом случае "Page / Pageable" слишком много. И клиент должен создать Pageable / Page для получения «одного и только одного» результата ... Это очень неприятно для использования. И кажется, что вы отвечаете за Spring-Data, я надеюсь, вы сможете подробнее изучить эту проблему: stackoverflow.com/questions/9276461/… . Можете ли вы подтвердить, что это ошибка Spring-Data?
smallufo
1
+ это работает, спасибо, но решение на основе аннотаций было бы лучше в более новых версиях
azerafati
1
здесь запускается подсчетный запрос, который совершенно не нужен, если нам нужен ограниченный запрос.
azerafati
1
Это не так, если вы используете Listв качестве возвращаемого типа метода.
Оливер Дротбом
3
Думаю, Topа Firstключевые слова неприменимы к методам, использующим @Queryаннотации? Только те, которые используют генерацию запросов на основе имени метода?
Руслан Стельмаченко
106

Если вы используете Java 8 и Spring Data 1.7.0, вы можете использовать методы по умолчанию, если хотите объединить @Queryаннотацию с установкой максимальных результатов:

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

}
Вим Деблаув
источник
3
потенциально полезно для единственного результатаStream<User> ... {...} default Optional<User> ... { ...findAny() }
xenoterracide 02
28

Существует способ предоставить эквивалент «setMaxResults (n) по аннотации», как показано ниже:

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

Это должно сработать, когда страница отправляется как параметр. Например, чтобы получить первые 10 записей, вам нужно установить pageable для этого значения:

new PageRequest(0, 10)
ncaralicea
источник
Если вы используете Pagable, то его возврат Page <XYZ> не List <XYZ>
Arundev
@Arundev неправильно - Listработает отлично (и позволяет избежать ненужных countзапросов, как уже упоминалось в предыдущем комментарии )
Джанака Бандара
21

Используйте Spring Data Evans (ВЫПУСК 1.7.0)

новый выпуск Spring Data JPA с другим списком модулей вместе под названием Evans имеет функцию использования ключевых слов Top20и Firstограничения результата запроса,

чтобы ты мог написать

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

или

List<User> findTop20ByLastnameOrderByIdDesc(String lastname);

или для единственного результата

List<User> findFirstByLastnameOrderByIdDesc(String lastname);
azerafati
источник
5
Необязательный <Пользователь> findFirstByLastnameOrderByIdDesc (String lastname);
krmanish007
После получения первых 20 результатов, как мне получить следующие 20 результатов, когда я перейду на следующую страницу веб-сайта с помощью нумерации страниц?
kittu
@kittu, это, конечно, другой вопрос, но вам нужно передать Pageableобъект. ознакомьтесь с весенней документацией
azerafati
почему они заставили метод возвращать чертов список, если он указан ПЕРВЫМ ??
Василе Сурду
13

Лучший выбор для меня - это собственный запрос:

@Query(value="SELECT * FROM users WHERE other_obj = ?1 LIMIT 1", nativeQuery = true)
User findByOhterObj(OtherObj otherObj);
Григорий Кислин
источник
Не уверен, поддерживается ли ограничение в Hibernate: forum.hibernate.org/viewtopic.php?f=9&t=939314
Витольд Качурба
2
Разрушает всю концепцию! Лучше используйте EntityManager!
Spektakulatius 07
@ Code.IT Я пропагандирую концепцию KISS. Кроме того, nativeQuery добавлен параметр аннотации JPA, который нельзя игнорировать.
Григорий Кислин
@GKislin, чем вам нужно переименовать свой метод, например findLimitOneByOtherObj. Также вы забыли добавить OrderBy, что приведет к неправильному результату, если other_obj не уникален. В противном случае оператора where для уникального столбца должно быть достаточно без ограничения
Spektakulatius
Ключевое слово LIMIT не в спящем режиме, а в postgres / mysql
Acewin
5

Если ваш класс @Repositoryрасширяется, JpaRepositoryвы можете использовать приведенный ниже пример.

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

getContent вернет a List<Transaction>.

Луис Камарго
источник
что, если у меня есть 2 условия в запросе, например transactionRepository.findByFirstNameAndLastName (имя, фамилия)? это будет работать ? Я получаю это исключение org.springframework.data.mapping.PropertyReferenceException: для типа Board не найдено создание свойства.
Шайлеш
4

Также возможно использование @QueryHints. В приведенном ниже примере используется 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();
Кракен
источник
Я тестировал этот код, но он не работает. Никакого исключения, но и правильного результата нет. Также документация по QueryHints очень тонкая. Увидеть ниже. docs.spring.io/spring-data/jpa/docs/1.7.1.RELEASE/reference/…
bogdan.rusu
IIRC, нет никаких гарантий, что подсказка запроса будет соблюдена. Это просто способ передать «дружеские рекомендации» реализации.
Priidu Neemre
Я пробую это @QueryHints({@QueryHint(name = org.hibernate.annotations.FETCH_SIZE, value = "1")})в Hibernate, но псевдоним :( не работает
Григорий Кислин
2
org.hibernate.annotations.FETCH_SIZE размер выборки, а не ограничение, а не максимальные результаты. Hiber cant
Журавский Виталий
4

new PageRequest(0,10)не работает в более новых версиях Spring (я использую 2.2.1.RELEASE). По сути, конструктор получил дополнительный параметр как Sortтип. Более того, конструктор таков, protectedчто вам нужно либо использовать один из его дочерних классов, либо вызывать его ofстатический метод:

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

Вы также можете не использовать Sortпараметр и неявно использовать сортировку по умолчанию (сортировку по pk и т. Д.):

PageRequest.of(0, 10)

Объявление вашей функции должно быть примерно таким:

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

и функция будет:

userRepository.findByUsername("Abbas", PageRequest.of(0,10, Sort.sort(User.class).by(User::getLastName).ascending());
Аббас Хоссейни
источник