JPA: выбрать случайную строку

Это мой JPA ENTITY

@Entity
@NamedQueries({  
        @NamedQuery(name = "Question.randQuestion", query = "SELECT q FROM Question AS q ORDER BY     RANDOM")
})
@Table(name = "questions")
public class Question implements Serializable {
.....
}

Проблема в:

Eclipse дает мне ошибку для этого namedQuery. В нем говорится: "Идентификационная переменная RANDOM не определена в предложении FROM"

Я пробовал также с RAND() вместо RANDOM, а также NEWID().

Благодарю.

3 ответа

Чтобы получить случайную строку, сначала получите список вопросов и получите любой.

public Question  getRandomQuestion(EntityManager em) {
  Query countQuery = em.createNativeQuery("select count(*) from Question");
  long count = (Long)countQuery.getSingleResult();

  Random random = new Random();
  int number = random.nextInt((int)count);

  Query selectQuery = em.createQuery("select q from Question q");
  selectQuery.setFirstResult(number);
  selectQuery.setMaxResults(1);
  return (Question)selectQuery.getSingleResult();
}

Примечание. Возможно, вам потребуется реализовать логику, чтобы избежать дублирования при вызове метода более одного раза.

Мне пришлось решить конкретный случай этой проблемы, когда мне пришлось выбирать случайные записи из набора записей, соответствующих определенным входным критериям. Решение также должно было поддерживать лимит. Я опишу свое решение ниже, начиная с предположений.

Предположения:

  • Учитывая набор критериев в качестве входных данных, можно подсчитать количество записей, которые соответствуют критериям выбора, как это поддерживается org.springframework.data.querydsl.QueryDslPredicateExecutor<T>.count(Predicate predicate) метод.

  • Страницы с нулевой индексацией.

  • Можно запросить конкретную страницу, поддерживаемую org.springframework.data.domain.PageRequest(int page, int size) метод.

Алгоритм

  1. Подсчитайте все записи, соответствующие входным критериям.

  2. Рассчитать общее количество страниц на основе количества и указанного лимита.

  3. Генерация случайного индекса страницы в диапазоне [0, всего страниц).

  4. Запросите страницу с индексом, сгенерированным на предыдущем шаге.

  5. Перемешать элементы на возвращаемой странице.

Код

Long totalRecords = someRepository.count(somePredicate);
Long totalPages =
    (totalRecords % someLimit == 0)
        ? (totalRecords / someLimit)
        : ((totalRecords / someLimit) + 1);
int pageIndex = (int) (Math.random() * totalPages);

PageRequest pageRequest = new PageRequest(pageIndex, someLimit);
Page<T> somePage = someRepository.findAll(somePredicate, pageRequest);
List<T> someList;
if (somePage.getTotalElements() > 0) {
  someList = new ArrayList<>(somePage.getContent());
} else {
  someList = new ArrayList<>();
}

Collections.shuffle(someList);

Второй случай - убедиться, что записи на странице также рандомизированы. Общий случай этого решения состоит в том, что нет никаких критериев, и поэтому count() должен быть вызван без predicate таким образом получая количество всех строк в таблице.

Насколько я понимаю, вы хотите выбрать случайный вопрос из таблицы. Вы бы предпочли использовать предложение WHERE с предоставлением некоторого параметра из вашего кода, например:

SELECT q FROM Question AS q WHERE id = :id

Затем в коде, который создает ваш запрос, вы должны сгенерировать случайный идентификатор для выбора:

query.setParam("id", getRandomId());

И чтобы получить случайный идентификатор, вы можете запросить количество строк из БД и использовать java.util.Random.nextInt(rowsCount) (если все идентификаторы есть, конечно).

Кстати, нечто подобное описано здесь: http://www.shredzone.de/cilla/page/53/how-to-fetch-a-random-entry-with-hibernate.html

Другие вопросы по тегам