[hibernate-dev] Cache key containing composite ID with a many-to-one

Yoann Rodiere yoann at hibernate.org
Thu Dec 5 02:15:41 EST 2019


> Perhaps the best solution is to constrain this, and warn that such a
> model is not a good fit for caching?

This. Embedded IDs containing an association are a monstrosity anyway, and
lead to problems even on the user side.
E.g. you can't serialize (to a String, JSON, ...) and more importantly
deserialize such ID without a session, in particular when using IDs in URL
paths or in JSON content.

For these IDs to make sense, we would need to force users to define a
separate, association-free class (using @IdClass?) to represent the
"serializable" form of the embedded ID, which would also be used in
Session.find() et. al. This would probably help when it comes to caching,
too.

But of course if it requires an API/behavior change, it would be a
deal-breaker for most people relying on this feature.
If we can't do it, IMHO it's perfectly reasonable to just give up on
caching and display a warning.


Yoann Rodière
Hibernate Team
yoann at hibernate.org


On Thu, 5 Dec 2019 at 00:19, Gail Badner <gbadner at redhat.com> wrote:

> Hi Sanne,
>
> By default, the cache key is of type CacheKeyImplementation [1]. As long as
> a composite key is a ComponentType (not a CustomType),
> CacheKeyImplementation#equals and #hashCode uses ComponentType#equals and
> #hashCode, not the custom implementation of the embeddable class methods.
>
> IIRC, a CustomType cannot contain an association, so disassembling a custom
> type should not be necessary.
>
> Steve, does that sound right to you?
>
> I believe that what you mentioned above does apply to "simple" cache keys,
> where the cache key is the ID itself. There are other cases where a simple
> cache key is not appropriate (e.g., multiple entity classes with the same
> type of ID). I don't remember offhand if there are warnings logged in those
> cases. We could warn if an application attempted to use a simple cache key
> with a cacheable entity that has a composite key with an association.
>
> Regarding B -- it is not possible to assemble the ID when
> calling CacheKeyImplementation#equals or #hashCode, because
> a CacheKeyImplementation does not have a reference to a Session, so it
> cannot resolve an associated entity. In any case, there should be no need
> to assemble an associated entity. It should be sufficient to for the
> disassembled entity ID to be used for #equals and #hashCode operations.
>
> [1]
>
> https://github.com/hibernate/hibernate-orm/blob/master/hibernate-core/src/main/java/org/hibernate/cache/internal/CacheKeyImplementation.java
>
>
>
> On Wed, Dec 4, 2019 at 2:03 PM Sanne Grinovero <sanne at hibernate.org>
> wrote:
>
> > Hi Gail,
> >
> > going for a disassembled ID would seem logical, but we'll need some
> > special care to deal with custom implementations of equals/hashcode.
> >
> > Clearly a composite ID object would require the users to implement a
> > custom equals(); going for a solution based on a disassembled ID we
> > would need to either:
> >
> > A# trust the equals implementation is "the obvious one"
> >
> > B# hydrate both sides of the equals check for each time there's need
> > to invoke an equals (and same for hashcode)
> >
> > I'm afraid only B would be backwards compatible and bullet-proof, and
> > yet its performance overhead would make it a terrible choice.
> >
> > Perhaps the best solution is to constrain this, and warn that such a
> > model is not a good fit for caching?
> >
> > Unless someone has a better idea; thinking out of the box: could be
> > interesting to explore not allowing users to implement custom equality
> > contracts as that's the root of many problems, but that would require
> > much more careful thought, and for sure a significant breaking change.
> >
> > Or allow people to implement any custom equals, but ignore them and
> > apply "the obvious one" consistently.
> >
> > Thanks,
> > Sanne
> >
> >
> >
> > On Wed, 4 Dec 2019 at 19:40, Gail Badner <gbadner at redhat.com> wrote:
> > >
> > > When an entity is cached with a composite ID containing a many-to-one
> > > association, the cache key will contain the many-to-one associated
> > entity.
> > > If the associated entity is not enhanced, then it could be an
> > uninitialized
> > > proxy.
> > >
> > > I've created a test case [1] that illustrates this using Infinispan.
> The
> > > test case is for 5.1 branch, since hibernate-infinispan is still
> included
> > > in that branch. The same would happen for master as well.
> > >
> > > Aside from the obvious issue with increased memory requirements to
> store
> > an
> > > entity, there are other problems as well.
> > >
> > > What I've found so far is that caching a key with an uninitialized
> entity
> > > proxy can cause some big problems:
> > >
> > > 1) A lookup will never find this key unless the lookup is done with a
> > cache
> > > key containing the same entity proxy instance.
> > >
> > > 2) Calling EntityManager#find with a composite ID containing a detached
> > > uninitialized entity proxy will result in a
> LazyInitializationException.
> > > This does not happen with second-level cache disabled.
> > >
> > > 3) Calling EntityManager#find with a composite ID containing a managed
> > > uninitialized entity proxy will result in the proxy being initialized.
> > This
> > > does not happen with second-level cache disabled.
> > >
> > > I have not looked into what happens when the associated entity is
> > enhanced
> > > and uninitialized (i.e., enhancement-as-proxy).
> > >
> > > IIUC, disassembling the ID that gets stored in the cache key would be
> far
> > > more efficient, and would avoid these issues. We would only want to do
> > this
> > > when a composite ID contains an association. This would require changes
> > in
> > > some SPIs though, to account for the disassembled ID type.
> > >
> > > I've been discussing necessary changes with Scott to cache a
> > > disassembled ID. Before we get too far down this road, I'd like to get
> > some
> > > feedback.
> > >
> > > In the first place, should an entity instance be stored in a cache key?
> > >
> > > Is disassembling primary keys that contain an association the
> appropriate
> > > way to go? If so, I'll continue with a POC for doing this.
> > >
> > > Thanks,
> > > Gail
> > >
> > > [1]
> > >
> >
> https://github.com/gbadner/hibernate-core/blob/753e36edf5137296d28b2a07cee3daffc16c6d1e/hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/CacheKeysWithEagerEntityFactoryTest.java
> > > _______________________________________________
> > > hibernate-dev mailing list
> > > hibernate-dev at lists.jboss.org
> > > https://lists.jboss.org/mailman/listinfo/hibernate-dev
> > >
> >
> >
> _______________________________________________
> hibernate-dev mailing list
> hibernate-dev at lists.jboss.org
> https://lists.jboss.org/mailman/listinfo/hibernate-dev
>
>


More information about the hibernate-dev mailing list