Hi,
In our project, I observed that when we use Native SQL query for any DML statement, the entities in second level cache were evicted. Could someone please provide some insight into avoiding this scenario without any performance impact?
From my analysis:
When I used a profiling tool to identify the cause, I observed that calls originating from the executeUpdate on a query implementation of native SQL resulted in eviction of entities from second level cache.
From Hibernate code walk through,
1. When creating a Native SQL using session instance, by default queryspaces is set to null
2. When invoking executeUpdate on the query, it deletes all the entities from cache if the queryspaces is null.
3. I also understand that using addSynchronizedEntityClass method on the query class should add queryspaces thus avoiding eviction from second level cache. But I am sceptical of using this as it would be doing additional conditional check in this case as evident from the code below code from BulkOperationCleanupAction class
Code:
public BulkOperationCleanupAction(SessionImplementor session, Set querySpaces)
{
this.session = session;
Set tmpSpaces = new HashSet(querySpaces);
SessionFactoryImplementor factory = session.getFactory();
Iterator iterator = factory.getAllClassMetadata().entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry entry = (Map.Entry)iterator.next();
String entityName = (String)entry.getKey();
EntityPersister persister = factory.getEntityPersister(entityName);
Serializable[] entitySpaces = persister.getQuerySpaces();
if (affectedEntity(querySpaces, entitySpaces)) {
if (persister.hasCache()) {
this.affectedEntityNames.add(persister.getEntityName());
}
Set roles = session.getFactory().getCollectionRolesByEntityParticipant(persister.getEntityName());
if (roles != null) {
this.affectedCollectionRoles.addAll(roles);
}
for (int y = 0; y < entitySpaces.length; y++) {
tmpSpaces.add(entitySpaces[y]);
}
}
}
this.spaces = ((Serializable[])(Serializable[])tmpSpaces.toArray(new Serializable[tmpSpaces.size()]));
}
private boolean affectedEntity(Set querySpaces, Serializable[] entitySpaces)
{
if ((querySpaces == null) || (querySpaces.isEmpty())) {
return true;
}
for (int i = 0; i < entitySpaces.length; i++) {
if (querySpaces.contains(entitySpaces[i])) {
return true;
}
}
return false;
}
Correct me if my understanding is wrong with respect to Hibernate code flow. It would be great if someone can advice on how to use Native SQL without evicting the objects from second level cache. Meanwhile, we are trying to use HQL or update on attached entities to avoid this behavior.