简体   繁体   中英

Hibernate / JPA: QueryException when hibernate generates metamodel for @ElementCollection

I try to persist an @ElementCollection within an entity with hibernate 4.3.1.Final. The entity looks like this:

import javax.persistence.*;
import java.util.Map;
@Entity
public class MyEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    @ElementCollection
    private Map<CompositeKey, CompositeValue> map;

    @Embeddable
    public static class CompositeKey {
        @Basic
        private Integer key1;
        @Basic
        private Integer key2;
    }

    @Embeddable
    public static class CompositeValue {
        @Basic
        private Integer val1;
        @Basic
        private Integer val2;
    }
}

Hibernate generates the schema correctly:

create table MyEntity (id integer not null, primary key (id)) ENGINE=InnoDB;
create table MyEntity_map (MyEntity_id integer not null, val1 integer, val2 integer, key1 integer, key2 integer, primary key (MyEntity_id, key1, key2)) ENGINE=InnoDB;
create table ReferencedEntity (id integer not null, primary key (id)) ENGINE=InnoDB;
alter table MyEntity_map add constraint FK_mhu8q8dtieguddm0w4gxfwhnc foreign key (MyEntity_id) references MyEntity (id);

生成的架构

But when I change CompositeKey to reference another Entity , I get a QueryException when hibernate generates the metamodel on startup.

The changed code:

@Embeddable
public static class CompositeKey {
    @ManyToOne
    private ReferencedEntity key1;
    @Basic
    private Integer key2;
}

The referenced Entity :

import javax.persistence.*;
@Entity
public class ReferencedEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
}

The Exception:

Caused by: org.hibernate.QueryException: could not resolve property: key1 of: component[val1,val2]
    at org.hibernate.persister.entity.AbstractPropertyMapping.propertyException(AbstractPropertyMapping.java:83) ~[AbstractPropertyMapping.class:4.3.1.Final]
    at org.hibernate.persister.entity.AbstractPropertyMapping.toColumns(AbstractPropertyMapping.java:98) ~[AbstractPropertyMapping.class:4.3.1.Final]
    at org.hibernate.persister.collection.AbstractCollectionPersister.toColumns(AbstractCollectionPersister.java:1625) ~[AbstractCollectionPersister.class:4.3.1.Final]
    at org.hibernate.loader.plan.build.internal.spaces.CompositePropertyMapping.toColumns(CompositePropertyMapping.java:124) ~[CompositePropertyMapping.class:4.3.1.Final]
    at org.hibernate.loader.plan.build.internal.spaces.CompositeQuerySpaceImpl.toAliasedColumns(CompositeQuerySpaceImpl.java:52) ~[CompositeQuerySpaceImpl.class:4.3.1.Final]
    at org.hibernate.loader.plan.build.internal.spaces.JoinImpl.resolveAliasedLeftHandSideJoinConditionColumns(JoinImpl.java:79) ~[JoinImpl.class:4.3.1.Final]
    at org.hibernate.loader.plan.exec.internal.LoadQueryJoinAndFetchProcessor.addJoins(LoadQueryJoinAndFetchProcessor.java:261) ~[LoadQueryJoinAndFetchProcessor.class:4.3.1.Final]
    at org.hibernate.loader.plan.exec.internal.LoadQueryJoinAndFetchProcessor.renderEntityJoin(LoadQueryJoinAndFetchProcessor.java:193) ~[LoadQueryJoinAndFetchProcessor.class:4.3.1.Final]
    at org.hibernate.loader.plan.exec.internal.LoadQueryJoinAndFetchProcessor.renderJoin(LoadQueryJoinAndFetchProcessor.java:158) ~[LoadQueryJoinAndFetchProcessor.class:4.3.1.Final]
    at org.hibernate.loader.plan.exec.internal.LoadQueryJoinAndFetchProcessor.processQuerySpaceJoin(LoadQueryJoinAndFetchProcessor.java:137) ~[LoadQueryJoinAndFetchProcessor.class:4.3.1.Final]
    at org.hibernate.loader.plan.exec.internal.LoadQueryJoinAndFetchProcessor.processQuerySpaceJoins(LoadQueryJoinAndFetchProcessor.java:132) ~[LoadQueryJoinAndFetchProcessor.class:4.3.1.Final]
    at org.hibernate.loader.plan.exec.internal.LoadQueryJoinAndFetchProcessor.processQuerySpaceJoin(LoadQueryJoinAndFetchProcessor.java:138) ~[LoadQueryJoinAndFetchProcessor.class:4.3.1.Final]
    at org.hibernate.loader.plan.exec.internal.LoadQueryJoinAndFetchProcessor.processQuerySpaceJoins(LoadQueryJoinAndFetchProcessor.java:132) ~[LoadQueryJoinAndFetchProcessor.class:4.3.1.Final]
    at org.hibernate.loader.plan.exec.internal.LoadQueryJoinAndFetchProcessor.processQuerySpaceJoins(LoadQueryJoinAndFetchProcessor.java:113) ~[LoadQueryJoinAndFetchProcessor.class:4.3.1.Final]
    at org.hibernate.loader.plan.exec.internal.AbstractLoadQueryDetails.generate(AbstractLoadQueryDetails.java:171) ~[AbstractLoadQueryDetails.class:4.3.1.Final]
    at org.hibernate.loader.plan.exec.internal.BasicCollectionLoadQueryDetails.<init>(BasicCollectionLoadQueryDetails.java:60) ~[BasicCollectionLoadQueryDetails.class:4.3.1.Final]
    at org.hibernate.loader.plan.exec.internal.BatchingLoadQueryDetailsFactory.makeCollectionLoadQueryDetails(BatchingLoadQueryDetailsFactory.java:101) ~[BatchingLoadQueryDetailsFactory.class:4.3.1.Final]
    at org.hibernate.loader.collection.plan.AbstractLoadPlanBasedCollectionInitializer.<init>(AbstractLoadPlanBasedCollectionInitializer.java:77) ~[AbstractLoadPlanBasedCollectionInitializer.class:4.3.1.Final]
    at org.hibernate.loader.collection.plan.CollectionLoader.<init>(CollectionLoader.java:112) ~[CollectionLoader.class:4.3.1.Final]
    at org.hibernate.loader.collection.plan.CollectionLoader$Builder.byKey(CollectionLoader.java:105) ~[CollectionLoader$Builder.class:4.3.1.Final]
    at org.hibernate.loader.collection.plan.AbstractBatchingCollectionInitializerBuilder.buildNonBatchingLoader(AbstractBatchingCollectionInitializerBuilder.java:45) ~[AbstractBatchingCollectionInitializerBuilder.class:4.3.1.Final]
    at org.hibernate.loader.collection.BatchingCollectionInitializerBuilder.createBatchingCollectionInitializer(BatchingCollectionInitializerBuilder.java:71) ~[BatchingCollectionInitializerBuilder.class:4.3.1.Final]
    at org.hibernate.persister.collection.BasicCollectionPersister.createCollectionInitializer(BasicCollectionPersister.java:343) ~[BasicCollectionPersister.class:4.3.1.Final]
    at org.hibernate.persister.collection.AbstractCollectionPersister.postInstantiate(AbstractCollectionPersister.java:676) ~[AbstractCollectionPersister.class:4.3.1.Final]
    at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:484) ~[SessionFactoryImpl.class:4.3.1.Final]
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1857) ~[Configuration.class:4.3.1.Final]
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:850) ~[EntityManagerFactoryBuilderImpl$4.class:4.3.1.Final]
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:843) ~[EntityManagerFactoryBuilderImpl$4.class:4.3.1.Final]
    at org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl.withTccl(ClassLoaderServiceImpl.java:399) ~[ClassLoaderServiceImpl.class:4.3.1.Final]
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:842) ~[EntityManagerFactoryBuilderImpl.class:4.3.1.Final]
    at org.hibernate.jpa.HibernatePersistenceProvider.createContainerEntityManagerFactory(HibernatePersistenceProvider.java:150) ~[HibernatePersistenceProvider.class:4.3.1.Final]
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:336) ~[LocalContainerEntityManagerFactoryBean.class:4.0.1.RELEASE]
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:318) ~[AbstractEntityManagerFactoryBean.class:4.0.1.RELEASE]
    ...

What is wrong with my mapping? How can I prevent this exception?

It is interesting, that the BasicCollectionPersister seems to get initialized correctly. Debugging shows, that eg the sqlUpdateRowString is update MyEntity_map set val1=?, val2=? where MyEntity_id=? and key1_id=? and key2=? update MyEntity_map set val1=?, val2=? where MyEntity_id=? and key1_id=? and key2=? .

From the JPA 2.0 specification:

Relationship mappings defined within an embedded id class are not supported.

See also JPA - EmbeddedId with @ManytoOne .

EDIT:

Hibernate 4.3.1.Final implements JPA 2.1, so my first guess was wrong. Mea culpa.

The problem with the mapping is the use of @ElementCollection with an embeddable CompositeKey referencing to the entity ReferencedEntity . The exception in AbstractPropertyMapping is thrown when Hibernate looks for a reference from CompositeValue (sic!) to ReferencedEntity . However as no such relation exists, the exception is thrown. For me it is unclear if this is a Hibernate bug or according the JPA 2.1 specs.

I see following solutions.

Solution 1

Change CompositeKey to an Entity adding an id field. In this case the @OneToMany annotation could be used:

@Entity
public class CompositeKey {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @ManyToOne(targetEntity = ReferencedEntity.class)
    private ReferencedEntity key1;

    @Basic
    private Integer key2;

}

Solution 2

Add an additional entity CompositeSampler containing CompositeKey as the ID and CompositeValue as an additional attribute. MyEntity will contain a list of CompositeSampler .

Solution 3

Add a reference from CompositeValue to ReferencedEntity .

By the way, according the JPA 2.1 specs: "If an embeddable class is used as a map key, the embeddable class must implement the hashCode and equals methods consistently with the database columns to which the embeddable is mapped".

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM