Performance issue with Blobs in 6x

We have recently upgraded from Hibernate 5.6.15 to Hibernate 6.2.6. I have a series of performance tests I run against my application and found that almost every metric was slowed by a factor of 3. I took one operation and looked at each query. All were pretty much the same except one query where we need to pull the contents of a document from a BLOB. The query before took approx. 10ms. In Hibernate 6 it takes 50ms. I have been trying to find why this is happening but have had no luck. My query, using Criteria, is as follows:

Session session = getSessionFactory().getCurrentSession();
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<DocContent> docContentCriteriaQuery = builder.createQuery(DocContent.class);
Root<DocumentContent> docContentRoot = docContentCriteriaQuery.from(DocContent.class);
docContentCriteriaQuery.where(builder.equal(docContentRoot.get(docId), docId));
Query<DocContent> query = session.createQuery(docContentCriteriaQuery);
DocContent content = query.getSingleResult();
return content;

DocContent is a simple Java object containing a String and a Blob object. Are there any suggestions you can give me to improve our performance?

One big thing that changed ORM 6 is the way criteria queries are handled behind the scenes. Since criteria queries are not translated to HQL first and then re-parsed, but instead translated directly, query plans are not cached anymore. So this is a potential slowdown. To still allow the caching, you can mark the criteria query as immutable with a session property session.setProperty(AvailableSettings.CRITERIA_COPY_TREE, false) and cache the CriteriaQuery in e.g. a volatile variable, but maybe it’s easier to just use HQL instead: session.createSelectionQuery("from DocContent d where d.docId = :docId").setParameter("docId", docId).getSingleResult();

I believe you are on the right track here. I applied the session property to this one query. Performance improved from 50ms to 15ms. This is more like what we normally saw in hibernate 5.x. I attempted to set this in my hibernate properties but it didn’t seem to take. When I ran the entire suite of tests performance was still poor. I am convinced we are on the right track, but just did not put it in the right place. Is there a global setting I can use for the entire system? Or must I add it to every query?

To get query plan caching you have to reuse the same CriteriaQuery instance and hence cache the constructed query somewhere. There is no config property (yet) that you can configure for query plan caching to happen. We are thinking about offering it, but query plan caching for dynamic queries might not be very useful and essentially trash your cache. I’ve seen a lot of OOM cases in the past that happened because the query plan cache filled with all sort of criteria query variants.

I created [HHH-17002] - Hibernate JIRA for this improvement which you can track.

How does HHH-17002 compare with this one: HHH-16782 which is marked resolved?

Prior to [HHH-16782] - Hibernate JIRA it wasn’t possible to cache criteria query plans at all, so the fix introduced an option for caching the plan for the same criteria query instance.