3

I have following MySql dependent code ( ORDER BY RAND() ) . I would like to know if there is hibernate HQL alternative for it (admin is boolean tag indicating that the user as an admin). This is working code:

public long getRandomAdmin() {
    Session session = getSession();
    Query selectQuery = session.createSQLQuery("SELECT user_id FROM users WHERE admin = '1' ORDER BY RAND()");
    selectQuery.setMaxResults(1);

    List<BigInteger> list = null;
    try {
        list = selectQuery.list();
    } catch (HibernateException e) {
        log.error(e);
        throw SessionFactoryUtils.convertHibernateAccessException(e);
    }

    if (list.size() != 1) {
        log.debug("getRandomAdmin didn't find any user");
        return 0;
    }
    log.debug("found: " + list.get(0));

    return list.get(0).longValue();
}
MatBanik
  • 26,356
  • 39
  • 116
  • 178
  • possible duplicate of [Hibernate Criteria API: get n random rows](http://stackoverflow.com/questions/2810693/hibernate-criteria-api-get-n-random-rows) – Simon Tower Aug 31 '15 at 23:23

2 Answers2

2

See this link: http://www.shredzone.de/cilla/page/53/how-to-fetch-a-random-entry-with-hibernate.html

Criterion restriction = yourRestrictions;
Object result = null;  // will later contain a random entity
Criteria crit = session.createCriteria(Picture.class);
crit.add(restriction);
crit.setProjection(Projections.rowCount());
int count = ((Number) crit.uniqueResult()).intValue();
if (0 != count) {
  int index = new Random().nextInt(count);
  crit = session.createCriteria(Picture.class);
  crit.add(restriction);
  result = crit.setFirstResult(index).setMaxResults(1).uniqueResult();
}

This is what you want. Keep Hibernate as an abstraction layer while still being able to query a random object. Performance suffers a bit, though.

Although I've been using Hibernate a lot, I don't know a more elegant way that is easy to use. Imho you should wrap that method behind a facade.

rogerdpack
  • 62,887
  • 36
  • 269
  • 388
Falcon
  • 3,150
  • 2
  • 24
  • 35
  • 1
    hard to believe hibernate doesn't hide these implementations cross database..anyway this way works [and might even be faster than doing a straight order by rand() http://www.webtrenches.com/post.cfm/avoid-rand-in-mysql ] though it only returns you one random row, so you'd have to do X queries if you want X random entries, FWIW. Another option would be to create your own custom hibernate dialect that registers the "rand" function for Oracle as "dbms_random.value" – rogerdpack Dec 02 '14 at 16:13
  • http://stackoverflow.com/questions/2810693/hibernate-criteria-api-get-n-random-rows has a simpler implementation – Simon Tower Aug 31 '15 at 23:24
  • This might not return any record if you have missing ids between 0 and maxId. – vadim Dec 29 '16 at 15:56
1

Since the Criterion used in the accepted answer is deprecated, I figured out how to do it with the CriteriaBuilder & CriteriaQuery and just wanted to share it here. I used the pattern described here to extend my repository by the custom method:

@Repository
public class UserRepositoryCustomImpl implements UserRepositoryCustom {
@Autowired
EntityManager em;

public User findRandomUserInCountry(String countryCode) throws NotFoundException {
    CriteriaBuilder qb = em.getCriteriaBuilder();
    CriteriaQuery<Long> cqCount = qb.createQuery(Long.class);

    Root<User> userCountRoot = cqCount.from(User.class);

    cqCount.select(qb.count(userCountRoot)).where(qb.equal(userCountRoot.get("countryCode"), countryCode));

    int count = em.createQuery(cqCount).getSingleResult().intValue();

    System.out.println("Count of users: " + count);

    if (0 != count) {
        int index = new Random().nextInt(count);
        CriteriaQuery<User> cqUser = qb.createQuery(User.class);
        Root<User> userRoot = cqUser.from(User.class);
        cqUser.select(userRoot).where(qb.equal(userRoot.get("countryCode"), countryCode));

        User randomUser = em.createQuery(cqUser).setFirstResult(index).setMaxResults(1)
                .getSingleResult();

        System.out.println("Random user: " + randomUser.getName());

        return randomUser;
    } else {
        throw new NotFoundException("No users available in repository for country: " + countryCode);
    }
}

}
tietze111
  • 321
  • 1
  • 2
  • 15