I want to fetch random records using Spring data JPA. I was using @Query for the same.But it is taking a long time.
@Query("select que from Question que order by RAND()")
public List<Question> findRandamQuestions();
Which is the efficient way for doing the same?Please help!
PagingAndSortingRepository provides methods to do pagination and sort records. JpaRepository provides JPA related methods such as flushing the persistence context and delete records in a batch.
In order to define SQL to execute for a Spring Data repository method, we can annotate the method with the @Query annotation — its value attribute contains the JPQL or SQL to execute. The @Query annotation takes precedence over named queries, which are annotated with @NamedQuery or defined in an orm. xml file.
Its findById method retrieves an entity by its id. The return value is Optional<T> . Optional<T> is a container object which may or may not contain a non-null value. If a value is present, isPresent returns true and get returns the value.
JPA FetchType The fetch attribute can be either FetchType. LAZY or FetchType. EAGER . By default, @OneToMany and @ManyToMany associations use the FetchType.
The problem with select que from Question que order by RAND()
is that your DB will order all records before return one item. So it's expensive in large data sets.
A cheaper way to achieve this goal consist in two steps:
To do that in MySql for example, you can do:
select count(*) from question;
// using any programming language, choose a random number between 0 and count-1 (let's save this number in rdn), and finally
select * from question LIMIT $rdn, 1;
Ok, but to do that in spring data you need to create some native queries...
Fortunately, we can use pagination in order to resolve that. In your Repository Interface, create the methods (some repositories has this without need to define it):
Long count();
Page<Question> findAll(Pageable pageable);
And in your Service you can user your repository in the following way:
public Question randomQuestion() {
Long qty = questionRepository.countAll();
int idx = (int)(Math.random() * qty);
Page<Question> questionPage = questionRepository.findAll(new PageRequest(idx, 1));
Question q = null;
if (questionPage.hasContent()) {
q = questionPage.getContent().get(0);
}
return q;
}
You could do this post fetch.
Get a list of all the questions and just get random ones from those.
public List<Question> getRandomQuestions(List<Questions> questions, int numberOfQuestions) {
List<Question> randomQuestions = new ArrayList<>();
List<Question> copy = new ArrayList<>(questions);
SecureRandom rand = new SecureRandom();
for (int i = 0; i < Math.min(numberOfQuestions, questions.size()); i++) {
randomQuestions.add( copy.remove( rand.nextInt( copy.size() ) );
}
return randomQuestions;
}
Or if your list was really large and you knew the IDs beforehand, you could do the same thing and just fetch the Questions Ids that you needed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With