2

通常在休眠中,我们与 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;
    }
}

我们在这里使用的技巧是每个集合有一个缓存区域。

当涉及到保存或删除时,我们会驱逐这个缓存区域。它工作正常,但有一些缺点:

  • 我们需要很多缓存区域。到目前为止,我们不知道是否存在一些限制或性能考虑。
  • 由于在无法配置缓存区域之前不知道缓存区域的名称,例如设置最大元素数或生存时间

摘要:我们不希望在我们的父母中设置一个集合。我们需要有二级集合缓存,如果添加或删除实体,它并不总是无效(就像通常的查询缓存一样)。

您是否知道拥有未与集合中的父/子关系映射的集合缓存的正确方法?

4

0 回答 0