So we need to decide how to best represent the identifier for the entity. That's ultimately the disconnect here. Hibernate historically had 3 ways to represent ids:
* simple - these were explicitly basic type ids: longs, ints, strings, etc. == <hbm:id/> * aggregated composite - essentially @EmbeddedId * non-aggregated composite - like @IdClass cases, but without the @IdClass; the entity itself was the identifier value. Notice a few things: 1) A simple identifier could never be an association in legacy Hibernate. A "key-many-to-one" was ALWAYS wrapped in a composite, even if it was the attribute on the composite. Now Hibernate also has the concept of a 1-1 with a "foreign generator" which is essentially the same as the JPA feature of `@Id @ManyToOne` or `@Id @OneToOne`. Recently I made some significant changes to "simple derived ids" such that they no longer get wrapped in a virtual embedded composite. 2) For composite ids, JPA requires either a EmbeddedId or a IdClass. Legacy Hibernate does not, via its "embedded (virtual) identifier". While I think we should continue to support non-aggregated composite without an IdClass, I do think we should warn users when they do this. So at the moment we really have the following ways to represent an id: * simple basic id * simple derived id ( @Id + @OneToOne or @ManyToOne ) * aggregated composite * non-aggregated composite * non-aggregated composite + IdClass 3) JPA allows EmbeddedId to have (singular) associations. IdClass cannot; in cases where IdClass maps one or more associations.. well the convoluted rules in "derived identities" section kick in. But the important take away is that the structure of the 2 id representations is very different. For example: @Entity class Customer { @Id Integer id; ... } class OrderIdClass implements Serializable { Integer customer; int orderNumber; } @Entity @IdClass(OrderIdClass.class) class Order implements Serializable { @Id @ManyToOne ... Customer customer; @Id int orderNumber; ... } Looking at Order, the internal representation of its identity (used to cache it within the Session ,etc) is the Order itself - Hibernate's legacy "embedded identifier" which includes the many-to-one. The representation used in lookups is OrderIdClass which does not contain the many-to-one. Note that EmbeddedId can follow the same paradigm according to JPA, which is another use-case of MapsId: @Embeddable class OrderId implements Serializable { Integer customer; int orderNumber; } @Entity class Order implements Serializable { @EmbeddedId OrderId id; @MapsId @ManyToOne ... Customer customer; ... } Ugh. On Tue, Apr 8, 2014 at 6:32 PM, Gail Badner <gbad...@redhat.com> wrote: > Hi Steve, > > Problem happens when a @ManyToOne is assocated with an entity that has an > @IdClass. > > I looked for a core test that reproduces the problem, but it looks like > the core tests using @IdClass that fail for a different reason before this > problem shows up. > > The following envers tests reproduce the problem: > > > org.hibernate.envers.test.integration.onetoone.bidirectional.ids.MulIdBidirectional > > org.hibernate.envers.test.integration.onetomany.detached.BasicDetachedSetWithMulId > org.hibernate.envers.test.integration.onetomany.BasicSetWithMulId > org.hibernate.envers.test.integration.query.ids.MulIdOneToManyQuery > > When binding the JdbcDataType for the ID: > > EntityType.getIdentifierOrUniqueKeyType( metadataCollector() ) is called, > which ends up calling: > > InFlightMetadataCollectorImpl.getIdentifierType( associatedEntityName ) > which returns > > > entityBinding.getHierarchyDetails().getEntityIdentifier().getAttributeBinding() > .getHibernateTypeDescriptor() > .getResolvedTypeMapping(); > > which returns an EmbeddedComponentType; its PojoComponentTuplizer is > expecting to get/set values in the entity object. > > InFlightMetadataCollectorImpl.getIdentifierType( associatedEntityName ) > should be returning the ComponentType for the @IdClass. Problem is the > ComponentType for the @IdClass is not built until the persisters are being > built and PropertyFactory.buildIdentifierProperty() is called. This is > because I didn't think it was needed until then. Now I see that it is > needed when binding the JdbcDataType. > > The test ultimately fails due to PropertyAccessException when trying to > insert the many-to-one. This is because the ID value for the associated > entity is gotten from the EntityEntry and it is an instance of the @IdClass > class, while the PojoComponentTuplizer for the ID is expecting an entity > instance. > > Gail > > ----- Original Message ----- > > From: "Steve Ebersole" <st...@hibernate.org> > > To: "hibernate-dev" <hibernate-dev@lists.jboss.org> > > Sent: Monday, April 7, 2014 4:45:18 PM > > Subject: Re: [hibernate-dev] Metamodel - Entity primary key mappings > > > > What I am thinking at the moment is to change up > > org.hibernate.metamodel.spi.binding.EntityIdentifier to look like: > > > > public class EntityIdentifier { > > private final EntityBinding entityBinding; > > > > private EntityIdentifierNature nature; > > private IdentifierAttributeBindings attributeBindings; > > private IdClassBinding idClassBinding; > > private Map<String,String> derivedIdMap; > > private IdentifierGenerator generator; > > } > > > > > > 1) IdentifierAttributeBindings would play sort of the same role > > as > > > org.hibernate.metamodel.spi.binding.EntityIdentifier.EntityIdentifierBinding. > > Essentially it would hold the binding state for each of the identifier > > attributes (those marked with @Id or @EmbeddedId). Still not sure the > best > > external representation of this. Exposing a simple > > List<SingularAttributeBinding> for all identifier natures versus > > specialized IdentifierAttributeBindings for each nature are running a > > neck-and-neck race atm. Thoughts? Votes? > > > > 2) IdClassBinding would be a > > specialized > org.hibernate.metamodel.spi.binding.EmbeddableBindingContributor > > for describing the @IdClass mapping > > > > 3) `derivedIdMap` holds the various @MapsId mappings for the entity. I > was > > tempted to move this off to IdClassBinding except that @MapsId also > refers > > to @EmbeddedId attributes. > > > > Thoughts? Worries? Concerns? > > > > > > > > > > On Mon, Apr 7, 2014 at 10:27 AM, Steve Ebersole <st...@hibernate.org> > wrote: > > > > > I've been spending a lot of time the last 2 weeks trying to get a good > > > "mental model" as to how to best model the information pertaining to an > > > entity's primary key. Most of this effort went into trying to > understand > > > JPA's "derived identity" support. > > > > > > First and foremost I want to get away from modelling this (in the > > > metamodel) as a singular attribute. This is unnatural in the > > > "non-aggregated composite id" case forcing us to build a "virtual" > > > attribute. I think that this is very doable with the distinction I > > > recently added to the metamodel between Embedded/Embeddable. > > > > > > Next is to finish up support for IdClass, which should be close to > done. > > > Gail, I know you had mentioned a case where that support was lacking. > > > Could you send me the specifics so I make sure we get that case > covered? > > > > > > Beyond that is mainly incorporating support for JPA "derived > identities". > > > With that, I want to share some of my understanding and see if I > missed > > > anything... > > > > > > "Derived identity" support is essentially Hibernate's much older > "foreign" > > > identifier generator, namely that the child entity gets (all of or > part of) > > > its identifier from a to-one association defined on it, from its > "foreign > > > key" value. But of course the spec verbosely covers all the ways this > > > might happen. > > > > > > The very first thing I noticed is that the examples in the spec come > in 2 > > > distinct top-level flavors. Examples 1-3 are cases where the "parent > id" > > > is simply part of the "derived (child) id". Examples 4-6 are cases > where > > > the parent id *is* the child id (shared pk). I am not sure how > important > > > this distinction is in practice, but I also noticed that @MapsId is > only > > > pertinent wrt the second set of cases where we have the shared pk. > This > > > was the first time I have noticed that distinction. > > > > > > The one monkey wrench that JPA throws into the works here is that there > > > are essentially multiple views of an entity's PK. As one example, > take the > > > spec's "Example 4.a": > > > > > > @Entity > > > public class Person { > > > @Id String ssn; > > > ... > > > } > > > > > > @Entity > > > public class MedicalHistory { > > > @Id > > > @OneToOne > > > @JoinColumn(name="FK") > > > Person patient; > > > ... > > > } > > > > > > Ultimately, the primary key of MedicalHistory is the `ssn` of its > > > associated Person. Thus both of these are valid: > > > > > > entityManager.find( MedicalHistory.class, somePerson ); > > > entityManager.find( MedicalHistory.class, somePerson.ssn ); > > > > > > For those who have seen it, this is the reason for the wonkiness inside > > > Hibernate's runtime engine wrt incoming id type while doing a load. > > > > > > > > > I am still going through all the use cases (ours plus JPA) to make > sure we > > > get everything covered. But I wanted to hopefully get some discussion > > > started around this and get any thoughts y'all might have. > > > > > > > > > > > _______________________________________________ > > 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