I work on some kind of cache and from time to time, we need to prune the table to 500 records, based on a last_access_date
(only keep the 500 recently accessed rows).
With "plain" SQL, this could be done with:
DELETE FROM records WHERE id not in
(SELECT id FROM records ORDER BY last_access_date DESC LIMIT 500)
Now as there is no LIMIT
or something like ROWNUM
in JPQL, the only solution I found was in native SQL, which is suboptimal, because we run on multiple DBMS (at least Oracle and MSSQL).
Also, setMaxResults()
(JPQLs version of LIMIT
) doesn't seem valid for DELETE
statements.
Is there really no way to to this with JPQL?
You could do this:
String sql = "SELECT x.id FROM records x ORDER BY x.last_access_date DESC";
TypedQuery<Long> query = em.createQuery(sql, Long.class);
List<Long> ids = query.setMaxResults(500).getResultList();
String delete = "DELETE FROM records x where x.id not in :ids";
em.createQuery(delete).setParameter("ids", ids).executeUpdate();
I don't recall the exact syntax for the delete query, so you may have to put the :ids
between parenthesis, like:
String delete = "DELETE FROM records x where x.id not in (:ids)";
Edit: dkb proposed a faster solution on the comments (depends on unique dates for perfect accuracy on the amount of remaining rows):
String sql = "SELECT x.last_access_date FROM records x ORDER BY x.last_access_date DESC";
//If you're not using calendar, change to your specific date class
TypedQuery<Calendar> query = em.createQuery(sql, Calendar.class);
Calendar lastDate = query.setFirstResult(499).setMaxResults(1).getSingleResult();
String delete = "DELETE FROM records x where x.last_access_date < :lastDate";
em.createQuery(delete).setParameter("lastDate", lastDate, TemporalType.DATE).executeUpdate();