2010-09-24 7 views
4

J'ai une relation un-à-plusieurs: enregistrement parent avec n enregistrements enfants. Ces enregistrements sont fréquemment utilisés et en lecture seule et sont de bons candidats pour la mise en cache.Éviter les sélections n + 1 avec les associations Hibernate mises en cache ou les mises en cache dans leur ensemble

Voici une approximation de mon mapping Hibernate:

`<class name="Parent" table="Parent> 
    <cache usage="read-only"/> 
    <id name="primary_key"/> 
    <property name="natural_key"/> 

    <set name="children" lazy="false" fetch="join"> 
     <cache usage="read-only"/> 
     <key-column name="parent_id"/> 
     <one-to-many class="Child"/> 
    </set> 
</class> 

<class name="Child" table="Child"> 
    <cache usage="read-only"/> 
    <id name="primary_key"/> 
    <property name="parent_id"/> 
</class>` 

Je vais chercher souvent le parent par une clé naturelle, plutôt que d'une clé primaire, donc je dois activer le cache de requêtes afin de tirer profit de la Cache de second niveau (j'utilise ehcache).

Voici le problème: lorsque je récupère un parent et obtiens un hit dans le cache de requête, il devient une requête "récupérer par clé primaire". C'est bien pour le "un" fin de mon un-à-plusieurs. Si le parent n'est pas trouvé dans le cache, il est extrait de la base de données. Si mes n Enregistrements enfants ne sont pas trouvés dans le cache, Hibernate les récupère en utilisant n requêtes select suivantes. N + 1 sélectionnez le problème.

Ce que je veux est un moyen de mettre en cache la collection d'objets enfant, avec le parent_id. Je veux qu'Hibernate cherche ma collection dans le cache dans son ensemble, plutôt que comme un ensemble d'enregistrements individuels. Si la collection n'est pas trouvée, je veux que Hibernate récupère la collection en utilisant 1 instruction select - récupère tous les enfants avec parent_id = x.

Est-ce trop demander à Hibernate + ehcache?

Répondre

5

J'ai trouvé ma propre réponse - il est possible de configurer Hibernate + ehcache pour faire ce que j'ai décrit ci-dessus. En déclarant mon enfant comme un type de valeur plutôt qu'un type d'entité (je crois que ce sont les termes utilisés par la communauté Hibernate), je peux essentiellement traiter mon enfant comme un composant de Parent plutôt qu'une entité distincte. Voici un exemple de ma cartographie révisée:

<class name="Parent" table="Parent"> 
    <cache usage="read-only"/> 
    <id name="primary_key"/> 
    <property name="natural_key"/> 

    <set name="children" lazy="false" fetch="join" table="Child"> 
     <cache usage="read-only"/> 
     <key-column name="parent_id"/> 
     <composite-element class="Child"> 
     <property name="property1" column="PROP1" type="string"> 
     <property name="property2" column="PROP2" type="string"> 
     </composite-element> 
    </set> 
</class> 

Le comportement de mes objets enfant est un peu différent dans cette configuration que précédemment - il n'y a pas de clé primaire séparée définie pour enfant maintenant, pas de références partagées, et pas champs/colonnes nullables. Voir le Hibernate docs pour plus de détails à ce sujet. Mon parent et mon enfant sont tous les deux en lecture seule et je veux seulement accéder aux instances de l'enfant par l'intermédiaire d'un parent - Je n'utilise pas l'enfant indépendamment du parent, donc le traitement de type valeur convient parfaitement à mon utilisation Cas.

La grande victoire pour moi était de savoir comment la collection est mise en cache sous ma nouvelle configuration. Le cache de la collection met maintenant en cache ma collection dans son ensemble, avec clé par parent_id. Il n'est plus possible que certains, mais pas tous, de ma collection soient dans le cache. La collection est mise en cache et expulsée dans son ensemble. Plus important encore, si Hibernate va chercher ma collection dans le cache de second niveau et obtient un échec, il récupère la collection entière de la base de données avec une seule requête select.

Voici ma configuration ehcache:

<ehcache> 
    <cache name="query.Parent" 
     maxElementsInMemory="10" 
     eternal="false" 
     overflowToDisk="false" 
     timeToIdleSeconds="0" 
     timeToLiveSeconds="43200" 
    </cache> 
    <cache name="Parent" 
     maxElementsInMemory="10" 
     eternal="false" 
     overflowToDisk="false" 
     timeToIdleSeconds="0" 
     timeToLiveSeconds="43200" 
    </cache> 
    <cache name="Parent.children" 
     maxElementsInMemory="10" 
     eternal="false" 
     overflowToDisk="false" 
     timeToIdleSeconds="0" 
     timeToLiveSeconds="43200" 
    </cache> 
<ehcache> 

Espérons que cet exemple aide quelqu'un d'autre.

+0

Merci d'avoir posté une réponse détaillée. +1 tout autour. –

Questions connexes