Re: [hibernate-dev] Cache key containing composite ID with a many-to-one
Hi Steve, I like your ideas about loading an entity with multiple ID attributes without @IdClass. It's much nicer than passing an instance of the entity with ID attributes initialized to Session#get. Getting back to the cache issue -- it sounds like disassembling the ID is worth pursuing. Please confirm... Thanks, Gail On Thu, Dec 5, 2019 at 9:33 AM Steve Ebersole wrote: > > JPA does not allow mixing of @IdClass and @EmbeddedId. But I think >> that's a valid option. In fact in our original work on 6 where we >> completely replaced the mapping model (persisters) i had added support for >> this. We could add an improvement to pull this feature to 6 proper? >> >> There would be some rough edges (what would >> org.hibernate.Session#getIdentifier return, the embedded id or the idclass? >> What would Session.load accept? What would be the declared type of the ID >> in JPA Criteria?). But that could be a valuable feature for people who >> really want composite IDs. And it could solve the caching problem. >> However, if we need to solve the caching problem in ORM 5 and are not in >> a position to declare that caching isn't supported for such identifiers... >> then we'll have to work on some hack in ORM 5. So maybe it's not a priority >> in ORM 6. >> > > `Session#getIdentifier` would return the embedded-id, imo. The > embedded-id is the "real" id. The id-class is just a simplified "syntactic > sugar" used only for an alternative form of loading. In fact for an entity > with an id-class you can load using either approach, though the legacy form > of loading an entity with a non-aggregated composite id is truly painful. > > Just spit-balling, one option would be to expose a even more simplified > loading for these entities using `Session#byId` (or a new similar method) > allowing specifying the simplified, basic "maps-id" values for the load. > Just for illustration, something like: > > session.byCompositeId( EntityWithCompositeId.class ) > .using( "idAttribute1", "idAttribute1-value" ) > .using( "keyManyToOne", "keyManyToOne-basic-id-value" ) > ... > .load(); > > That's geared toward non-aggregated composite id cases, though really it > could be available in aggregated composite id cases as well. > > We could also allow a form in normal load-by-id using a Map maybe? I only > mention this as even though its still a "new feature", it does not require > any API changes or new APIs. > > final Map idValues = new HashMap(); > idValues.put( "idAttribute1", "idAttribute1-value" ); > idValues.put( "keyManyToOne", "keyManyToOne-basic-id-value" ); > ... > session.get( EntityWithCompositeId.class, idValues ); > > Or even an "array Map" option to avoid the Map instantiation: > > final Object[] idValues = new Object[] { > "idAttribute1", "idAttribute1-value", > "keyManyToOne", "keyManyToOne-basic-id-value", > ... > }; > assert idValues.length %2 == 0; > session.get( EntityWithCompositeId.class, idValues ); > > Of course, if the embedded-id could also have an id-class you'd use that. > > I'd like to see all of these as options btw, not one-or-the-other. > Though I'm fine if no-one else likes the `Session#byCompositeId` idea. > It was just a spur-of-the-moment idea. > ___ hibernate-dev mailing list hibernate-dev@lists.jboss.org https://lists.jboss.org/mailman/listinfo/hibernate-dev
Re: [hibernate-dev] Cache key containing composite ID with a many-to-one
> > > JPA does not allow mixing of @IdClass and @EmbeddedId. But I think > that's a valid option. In fact in our original work on 6 where we > completely replaced the mapping model (persisters) i had added support for > this. We could add an improvement to pull this feature to 6 proper? > > There would be some rough edges (what would > org.hibernate.Session#getIdentifier return, the embedded id or the idclass? > What would Session.load accept? What would be the declared type of the ID > in JPA Criteria?). But that could be a valuable feature for people who > really want composite IDs. And it could solve the caching problem. > However, if we need to solve the caching problem in ORM 5 and are not in a > position to declare that caching isn't supported for such identifiers... > then we'll have to work on some hack in ORM 5. So maybe it's not a priority > in ORM 6. > `Session#getIdentifier` would return the embedded-id, imo. The embedded-id is the "real" id. The id-class is just a simplified "syntactic sugar" used only for an alternative form of loading. In fact for an entity with an id-class you can load using either approach, though the legacy form of loading an entity with a non-aggregated composite id is truly painful. Just spit-balling, one option would be to expose a even more simplified loading for these entities using `Session#byId` (or a new similar method) allowing specifying the simplified, basic "maps-id" values for the load. Just for illustration, something like: session.byCompositeId( EntityWithCompositeId.class ) .using( "idAttribute1", "idAttribute1-value" ) .using( "keyManyToOne", "keyManyToOne-basic-id-value" ) ... .load(); That's geared toward non-aggregated composite id cases, though really it could be available in aggregated composite id cases as well. We could also allow a form in normal load-by-id using a Map maybe? I only mention this as even though its still a "new feature", it does not require any API changes or new APIs. final Map idValues = new HashMap(); idValues.put( "idAttribute1", "idAttribute1-value" ); idValues.put( "keyManyToOne", "keyManyToOne-basic-id-value" ); ... session.get( EntityWithCompositeId.class, idValues ); Or even an "array Map" option to avoid the Map instantiation: final Object[] idValues = new Object[] { "idAttribute1", "idAttribute1-value", "keyManyToOne", "keyManyToOne-basic-id-value", ... }; assert idValues.length %2 == 0; session.get( EntityWithCompositeId.class, idValues ); Of course, if the embedded-id could also have an id-class you'd use that. I'd like to see all of these as options btw, not one-or-the-other. Though I'm fine if no-one else likes the `Session#byCompositeId` idea. It was just a spur-of-the-moment idea. ___ hibernate-dev mailing list hibernate-dev@lists.jboss.org https://lists.jboss.org/mailman/listinfo/hibernate-dev
Re: [hibernate-dev] Cache key containing composite ID with a many-to-one
> I disagree to an extent here. Assuming I think composite ids are a good idea anyway (I dont) then I actually think this is the most natural way to model this. I personally think JPA's MapsId is the monstrosity here ;) Fair enough. @MapsId definitely has its own set of problems. > JPA does not allow mixing of @IdClass and @EmbeddedId. But I think that's a valid option. In fact in our original work on 6 where we completely replaced the mapping model (persisters) i had added support for this. We could add an improvement to pull this feature to 6 proper? There would be some rough edges (what would org.hibernate.Session#getIdentifier return, the embedded id or the idclass? What would Session.load accept? What would be the declared type of the ID in JPA Criteria?). But that could be a valuable feature for people who really want composite IDs. And it could solve the caching problem. However, if we need to solve the caching problem in ORM 5 and are not in a position to declare that caching isn't supported for such identifiers... then we'll have to work on some hack in ORM 5. So maybe it's not a priority in ORM 6. Yoann Rodière Hibernate Team yo...@hibernate.org On Thu, 5 Dec 2019 at 14:16, Steve Ebersole wrote: > A few things... > > On Thu, Dec 5, 2019 at 1:17 AM Yoann Rodiere wrote: > > 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. >> > > I disagree to an extent here. Assuming I think composite ids are a good > idea anyway (I dont) then I actually think this is the most natural way to > model this. I personally think JPA's MapsId is the monstrosity here ;) > FWIW however, JPA does not define support for @EmbeddedId with > a @ManyToOne. So in that sense we'd be "ok" simply saying "ok, we support > mapping those, but they cannot be cached" if we need to go that route. > > We already support a form of loading an entity with MapsId using the basic > form of the associated id if the associated entity as just a simple id. > It's not a stretch to extend that to composite ids I think which would be a > good feature. Consider: > > @Entity > public class Person { > @Id String ssn; > > ... > } > > @Entity > public class MedicalHistory { > @Id > @OneToOne > @JoinColumn(name="FK") > Person patient; > > ... > } > > You can load a MedicalHistory here using the Person id. E.g. ` > session.get( MedicalHistory.class, "123-45-6789" );` > > If you consider a case like: > > @Entity > @IdClass(EmployeeId.class) > public class Employee { > @Id String firstName > @Id String lastName > > ... > } > > public class DependentId { > String name; > EmployeeId emp; > } > > @Entity > @IdClass(DependentId.class) > public class Dependent { > @Id String name; > @Id > @JoinColumns({ > @JoinColumn(name="FK1", referencedColumnName="firstName"), > @JoinColumn(name="FK2", referencedColumnName="lastName") > }) > @ManyToOne Employee emp; > > ... > } > > It would be nice to allow loading a Dependent like: `session.load( > Dependent.class, > new Object[] {"Steve","Ebersole"} );` The trouble there is the ordering > of the values compared to the attribute order. > > This is all beyond the original question, but your point about loading > these made me think about this. Ofc serializing does not disassemble, so > this is a moot point in regards to that. For JSON, depends on your > marshalling > > > 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. >> > > JPA does not allow mixing of @IdClass and @EmbeddedId. But I think that's > a valid option. In fact in our original work on 6 where we completely > replaced the mapping model (persisters) i had added support for this. We > could add an improvement to pull this feature to 6 proper? > > ___ hibernate-dev mailing list hibernate-dev@lists.jboss.org https://lists.jboss.org/mailman/listinfo/hibernate-dev
Re: [hibernate-dev] Cache key containing composite ID with a many-to-one
A few things... On Thu, Dec 5, 2019 at 1:17 AM Yoann Rodiere wrote: 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. > I disagree to an extent here. Assuming I think composite ids are a good idea anyway (I dont) then I actually think this is the most natural way to model this. I personally think JPA's MapsId is the monstrosity here ;) FWIW however, JPA does not define support for @EmbeddedId with a @ManyToOne. So in that sense we'd be "ok" simply saying "ok, we support mapping those, but they cannot be cached" if we need to go that route. We already support a form of loading an entity with MapsId using the basic form of the associated id if the associated entity as just a simple id. It's not a stretch to extend that to composite ids I think which would be a good feature. Consider: @Entity public class Person { @Id String ssn; ... } @Entity public class MedicalHistory { @Id @OneToOne @JoinColumn(name="FK") Person patient; ... } You can load a MedicalHistory here using the Person id. E.g. `session.get( MedicalHistory.class, "123-45-6789" );` If you consider a case like: @Entity @IdClass(EmployeeId.class) public class Employee { @Id String firstName @Id String lastName ... } public class DependentId { String name; EmployeeId emp; } @Entity @IdClass(DependentId.class) public class Dependent { @Id String name; @Id @JoinColumns({ @JoinColumn(name="FK1", referencedColumnName="firstName"), @JoinColumn(name="FK2", referencedColumnName="lastName") }) @ManyToOne Employee emp; ... } It would be nice to allow loading a Dependent like: `session.load( Dependent.class, new Object[] {"Steve","Ebersole"} );` The trouble there is the ordering of the values compared to the attribute order. This is all beyond the original question, but your point about loading these made me think about this. Ofc serializing does not disassemble, so this is a moot point in regards to that. For JSON, depends on your marshalling 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. > JPA does not allow mixing of @IdClass and @EmbeddedId. But I think that's a valid option. In fact in our original work on 6 where we completely replaced the mapping model (persisters) i had added support for this. We could add an improvement to pull this feature to 6 proper? ___ hibernate-dev mailing list hibernate-dev@lists.jboss.org https://lists.jboss.org/mailman/listinfo/hibernate-dev
Re: [hibernate-dev] Cache key containing composite ID with a many-to-one
I think Sanne meant relying on equals/hasCode of the embeddable itself, which we do not do as you pointed out. On Wed, Dec 4, 2019 at 5:19 PM Gail Badner 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 > 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 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 >
Re: [hibernate-dev] Cache key containing composite ID with a many-to-one
This all sounds s familar. But I was not able to find the Jira. Seems to be there was a reason that we don't disassemble the id, though that reason escapes me atm. That does seem like the logical thing to do. Regardless of why I think we may have decided not to, it would still be interesting to see if you could make that work On Wed, Dec 4, 2019 at 1:40 PM Gail Badner 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@lists.jboss.org > https://lists.jboss.org/mailman/listinfo/hibernate-dev > > ___ hibernate-dev mailing list hibernate-dev@lists.jboss.org https://lists.jboss.org/mailman/listinfo/hibernate-dev
Re: [hibernate-dev] Cache key containing composite ID with a many-to-one
> 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 yo...@hibernate.org On Thu, 5 Dec 2019 at 00:19, Gail Badner 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 > 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 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. >
Re: [hibernate-dev] Cache key containing composite ID with a many-to-one
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 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 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-infinispa
Re: [hibernate-dev] Cache key containing composite ID with a many-to-one
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 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@lists.jboss.org > https://lists.jboss.org/mailman/listinfo/hibernate-dev > ___ hibernate-dev mailing list hibernate-dev@lists.jboss.org https://lists.jboss.org/mailman/listinfo/hibernate-dev