通常、休止状態では、s と多くの親子関係がありますHashSet
。したがって、親には子のセットがあります。これは、休止状態のコレクション キャッシュを使用して、2 番目のレベルのキャッシュに簡単にキャッシュできます。オブジェクトをセットに追加または削除すると、キャッシュが無効になります。
ここで、親オブジェクトの set リレーションシップを削除します。これは、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 (名前など) の 2 つがあります。
hibernate は、save() や delete() などのリポジトリ メソッドを使用したエンティティの追加または削除を認識しないため、2 つの naturalIds のうち 1 つだけを使用すると、naturalIds を使用した hibernate クエリ キャッシュは機能しません。
なし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;
}
}
ここで使用するトリックは、コレクションごとに 1 つのキャッシュ領域を持つことです。
保存または削除に関しては、このキャッシュ領域を削除します。問題なく動作しますが、いくつかの欠点があります。
- 多くのキャッシュ領域が必要です。これまでのところ、いくつかの制限やパフォーマンスに関する考慮事項があるかどうかはわかりません。
- 要素の最大数や存続時間の設定など、キャッシュ領域を構成する前にキャッシュ領域の名前がわからないため
要約: 親にセットは必要ありません。エンティティが追加または削除された場合に常に無効化されるとは限らない (通常のクエリ キャッシュの場合と同様)、第 2 レベルのコレクション キャッシュが必要です。
セット内の親子関係にマップされていないコレクション キャッシュを持つ適切な方法を知っていますか?