通常在休眠中,我们与 s 有很多父/子关系HashSet
。所以父母有一组孩子。这可以通过休眠集合缓存轻松缓存在二级缓存中。当您向集合中添加或删除对象时,缓存将失效。
现在我们要删除父对象上的设置关系。这是因为遵循 DDD(域驱动设计)语义,子级不是父级聚合根的一部分。孩子本身就是一个聚合根。但是子类仍然与父类有关系。
因此,我们开始使用存储库通过以下方法访问子代:
public Set<Child> getChildren(Parent parent) {
Criteria criteria = getSession().createCriteria(Child.class);
criteria.add(Restrictions.naturalId().set("parent", parent));
criteria.setCacheable(true); // does not work here!
return criteria.list();
}
问题是setCacheable
. 因为我们的孩子有两个 naturalId 是父母和它自己的 naturalId (例如一个名字)。
如果我只使用两个 naturalId 中的一个,则带有 naturalIds 的休眠查询缓存不起作用,因为 hibernate 无法识别使用 save() 或 delete() 等存储库方法添加或删除更多实体。
当我们不使用它时,Restrictions-naturalId()
我们可以使用普通的查询缓存,但是如果添加或修改任何子缓存,这个缓存就会失效。这是查询缓存的常见问题,并非特定于我们的问题。
因此,我们尝试编写自己的 CollectionRepository,最终得到这样的类:
public abstract class CollectionRepositoryHibernate<T extends PersistableEntity, E extends PersistableEntity> extends StandardRepositoryHibernate<T> implements
CollectionRepository<T, E>
{
private Class<T> entityClass = getGenericClass();
private Class<E> parentClass = getGenericParentClass();
@Override
public void save ( T persistableEntity )
{
evictCache(persistableEntity);
super.save(persistableEntity);
}
@Override
@SuppressWarnings("unchecked")
public List<T> getCollection ( E persistableEntity )
{
return getNaturalCollectionCriteria(persistableEntity).list();
}
protected Criteria getNaturalCollectionCriteria ( E persistableEntity )
{
Criteria criteria = getSession().createCriteria(entityClass);
criteria.add(Restrictions.naturalId().set(getPropertyName(), persistableEntity));
criteria.setCacheable(true);
criteria.setCacheRegion(getCacheRegion(persistableEntity.getId()));
return criteria;
}
private String getPropertyName ( )
{
ClassMetadata classMetadata = getSession().getSessionFactory().getClassMetadata(entityClass);
int[] naturalIdentifierProperties = classMetadata.getNaturalIdentifierProperties();
String[] propertyNames = classMetadata.getPropertyNames();
for (int i : naturalIdentifierProperties)
{
String propertyName = propertyNames[i];
try
{
Field field = entityClass.getDeclaredField(propertyName);
Class<?> type = field.getType();
if (type.equals(parentClass))
{
return propertyName;
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
throw new IllegalStateException("No property name found");
}
private void evictCache ( T persistableEntity )
{
Cache cache = getSession().getSessionFactory().getCache();
String cacheRegion = getCacheRegion(getCollectionId(persistableEntity));
cache.evictQueryRegion(cacheRegion);
}
protected String getCacheRegion ( Integer id )
{
return entityClass + "#" + id;
}
@Override
public void delete ( T persistableEntity )
{
evictCache(persistableEntity);
super.delete(persistableEntity);
}
protected Integer getCollectionId ( T persistableEntity )
{
try
{
Field field = persistableEntity.getClass().getDeclaredField(getPropertyName());
field.setAccessible(true);
PersistableEntity parentEntity = (PersistableEntity) field.get(persistableEntity);
return parentEntity.getId();
}
catch (Exception e)
{
throw new IllegalStateException("no collection id found, e");
}
}
@SuppressWarnings("unchecked")
protected Class<E> getGenericParentClass ( )
{
Class<E> result = null;
Type type = this.getClass().getGenericSuperclass();
if (type instanceof ParameterizedType)
{
ParameterizedType pt = (ParameterizedType) type;
Type[] fieldArgTypes = pt.getActualTypeArguments();
result = (Class<E>) fieldArgTypes[1];
}
return result;
}
}
我们在这里使用的技巧是每个集合有一个缓存区域。
当涉及到保存或删除时,我们会驱逐这个缓存区域。它工作正常,但有一些缺点:
- 我们需要很多缓存区域。到目前为止,我们不知道是否存在一些限制或性能考虑。
- 由于在无法配置缓存区域之前不知道缓存区域的名称,例如设置最大元素数或生存时间
摘要:我们不希望在我们的父母中设置一个集合。我们需要有二级集合缓存,如果添加或删除实体,它并不总是无效(就像通常的查询缓存一样)。
您是否知道拥有未与集合中的父/子关系映射的集合缓存的正确方法?