JPA 继承:子类关系取决于超类字段

问题描述 投票:0回答:1

我有 3 个具有连接继承的 JPA 实体(1 个超类和 2 个子类),并且根据超类字段,子类之间存在一对一的关系。 声明关系有问题。

表格:

create table Parent (
  id int primary key,
  guid uuid
);

create table A (
  id int primary key references Parent(id),
  some_a_specific_field text
);

create table B (
  id int primary key references Parent(id),
  some_b_specific_field text,
  a_guid uuid
);

实体:

@Data
@NoArgsConstructor
@Entity
@Table(name = "Parent")
@Inheritance(strategy = InheritanceType.JOINED)
public class ParentEntity {
    @Id
    private Integer id;
    private UUID guid;
}


@Data
@NoArgsConstructor
@Entity
@Table(name = "A")
public class AEntity {
    private String someASpecificField;
    @OneToOne
    private BEntity b;
}

@Data
@NoArgsConstructor
@Entity
@Table(name = "B")
public class BEntity {
    private String someBSpecificField;
}

我尝试了几种声明关系的方法,但每种方法都出错了。

第一: 具有单向和@JoinColumn。

声明:

@Data
@NoArgsConstructor
@Entity
@Table(name = "A")
public class AEntity {
    private String someASpecificField;
    @OneToOne
    @JoinColumn(name = "guid", table = "Parent", referencedColumnName = "a_guid", insertable = false, updatable = false)
    private BEntity b;
}

错误:

Caused by: org.hibernate.AnnotationException: Cannot find the expected secondary table: no Parent available for AEntity

第二: 使用单向,@JoinColumn 和 @SecondaryTable。

实体:

@Data
@NoArgsConstructor
@Entity
@Table(name = "A")
@SecondaryTable(name = "Parent")
public class AEntity {
    private String someASpecificField;
    @OneToOne
    @JoinColumn(name = "guid", table = "Parent", referencedColumnName = "a_guid", insertable = false, updatable = false)
    private BEntity b;
}

错误:

Caused by: org.hibernate.MappingException: Could not instantiate persister org.hibernate.persister.entity.JoinedSubclassEntityPersister
    at org.hibernate.persister.internal.PersisterFactoryImpl.createEntityPersister(PersisterFactoryImpl.java:112)
    at org.hibernate.persister.internal.PersisterFactoryImpl.createEntityPersister(PersisterFactoryImpl.java:77)
    at org.hibernate.metamodel.internal.MetamodelImpl.initialize(MetamodelImpl.java:181)
    at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:319)
    at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:471)
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1498)
    at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:58)
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409)
    ... 90 more
Caused by: java.lang.IllegalStateException: Encountered 'subclass table index' [-1] was outside expected range ( [0] < i < [5] )
    at org.hibernate.persister.entity.JoinedSubclassEntityPersister.associateSubclassNamesToSubclassTableIndex(JoinedSubclassEntityPersister.java:697)
    at org.hibernate.persister.entity.JoinedSubclassEntityPersister.associateSubclassNamesToSubclassTableIndexes(JoinedSubclassEntityPersister.java:681)
    at org.hibernate.persister.entity.JoinedSubclassEntityPersister.processPersistentClassHierarchy(JoinedSubclassEntityPersister.java:657)
    at org.hibernate.persister.entity.JoinedSubclassEntityPersister.processPersistentClassHierarchy(JoinedSubclassEntityPersister.java:639)
    at org.hibernate.persister.entity.JoinedSubclassEntityPersister.buildSubclassNamesBySubclassTableMapping(JoinedSubclassEntityPersister.java:621)
    at org.hibernate.persister.entity.JoinedSubclassEntityPersister.<init>(JoinedSubclassEntityPersister.java:561)
    at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:67)
    at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
    at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:483)
    at org.hibernate.persister.internal.PersisterFactoryImpl.createEntityPersister(PersisterFactoryImpl.java:96)
    ... 98 more

第三: 具有双向

实体:

@Data
@NoArgsConstructor
@Entity
@Table(name = "A")
public class AEntity {
    private String someASpecificField;
    @OneToOne(mappedBy = "a")
    private BEntity b;
}

@Data
@NoArgsConstructor
@Entity
@Table(name = "B")
public class BEntity {
    private String someBSpecificField;;
    @OneToOne
    @JoinColumn(name = "a_guid", referencedColumnName = "guid")
    private AEntity a;
}

错误:

Caused by: org.hibernate.property.access.spi.PropertyAccessException: Error accessing field [private java.util.UUID Parent.guid] by reflection for persistent property [Parent#guid] : 1
    at org.hibernate.property.access.spi.GetterFieldImpl.get(GetterFieldImpl.java:75) ~[hibernate-core-5.6.8.Final.jar:5.6.8.Final]
    at org.hibernate.tuple.component.AbstractComponentTuplizer.getPropertyValue(AbstractComponentTuplizer.java:59) ~[hibernate-core-5.6.8.Final.jar:5.6.8.Final]
    at org.hibernate.type.ComponentType.getPropertyValue(ComponentType.java:419) ~[hibernate-core-5.6.8.Final.jar:5.6.8.Final]
    at org.hibernate.type.ComponentType.getHashCode(ComponentType.java:246) ~[hibernate-core-5.6.8.Final.jar:5.6.8.Final]
    at org.hibernate.engine.spi.EntityUniqueKey.generateHashCode(EntityUniqueKey.java:67) ~[hibernate-core-5.6.8.Final.jar:5.6.8.Final]
    at org.hibernate.engine.spi.EntityUniqueKey.<init>(EntityUniqueKey.java:48) ~[hibernate-core-5.6.8.Final.jar:5.6.8.Final]
    at org.hibernate.type.EntityType.loadByUniqueKey(EntityType.java:757) ~[hibernate-core-5.6.8.Final.jar:5.6.8.Final]
    at org.hibernate.type.EntityType.resolve(EntityType.java:468) ~[hibernate-core-5.6.8.Final.jar:5.6.8.Final]
    at org.hibernate.engine.internal.TwoPhaseLoad$EntityResolver.lambda$static$0(TwoPhaseLoad.java:576) ~[hibernate-core-5.6.8.Final.jar:5.6.8.Final]
    at org.hibernate.engine.internal.TwoPhaseLoad.initializeEntityEntryLoadedState(TwoPhaseLoad.java:221) ~[hibernate-core-5.6.8.Final.jar:5.6.8.Final]
    at org.hibernate.engine.internal.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:155) ~[hibernate-core-5.6.8.Final.jar:5.6.8.Final]
    at org.hibernate.engine.internal.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:126) ~[hibernate-core-5.6.8.Final.jar:5.6.8.Final]
    at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:1201) ~[hibernate-core-5.6.8.Final.jar:5.6.8.Final]
    at org.hibernate.loader.Loader.processResultSet(Loader.java:1009) ~[hibernate-core-5.6.8.Final.jar:5.6.8.Final]
    at org.hibernate.loader.Loader.doQuery(Loader.java:967) ~[hibernate-core-5.6.8.Final.jar:5.6.8.Final]
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:357) ~[hibernate-core-5.6.8.Final.jar:5.6.8.Final]
    at org.hibernate.loader.Loader.doList(Loader.java:2868) ~[hibernate-core-5.6.8.Final.jar:5.6.8.Final]
    at org.hibernate.loader.Loader.doList(Loader.java:2850) ~[hibernate-core-5.6.8.Final.jar:5.6.8.Final]
    at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2682) ~[hibernate-core-5.6.8.Final.jar:5.6.8.Final]
    at org.hibernate.loader.Loader.list(Loader.java:2677) ~[hibernate-core-5.6.8.Final.jar:5.6.8.Final]
    at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:540) ~[hibernate-core-5.6.8.Final.jar:5.6.8.Final]
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:400) ~[hibernate-core-5.6.8.Final.jar:5.6.8.Final]
    at org.hibernate.engine.query.spi.HQLQueryPlan.performList(HQLQueryPlan.java:219) ~[hibernate-core-5.6.8.Final.jar:5.6.8.Final]
    at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1459) ~[hibernate-core-5.6.8.Final.jar:5.6.8.Final]
    at org.hibernate.query.internal.AbstractProducedQuery.doList(AbstractProducedQuery.java:1649) ~[hibernate-core-5.6.8.Final.jar:5.6.8.Final]
    at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1617) ~[hibernate-core-5.6.8.Final.jar:5.6.8.Final]
    at org.hibernate.query.Query.getResultList(Query.java:165) ~[hibernate-core-5.6.8.Final.jar:5.6.8.Final]
    at org.hibernate.query.criteria.internal.compile.CriteriaQueryTypeQueryAdapter.getResultList(CriteriaQueryTypeQueryAdapter.java:76) ~[hibernate-core-5.6.8.Final.jar:5.6.8.Final]
    at org.springframework.data.jpa.repository.query.JpaQueryExecution$CollectionExecution.doExecute(JpaQueryExecution.java:128) ~[spring-data-jpa-2.6.4.jar:2.6.4]
    at org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:90) ~[spring-data-jpa-2.6.4.jar:2.6.4]
    at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:155) ~[spring-data-jpa-2.6.4.jar:2.6.4]
    at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:143) ~[spring-data-jpa-2.6.4.jar:2.6.4]
    at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:137) ~[spring-data-commons-2.6.4.jar:2.6.4]
    at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:121) ~[spring-data-commons-2.6.4.jar:2.6.4]
    at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:159) ~[spring-data-commons-2.6.4.jar:2.6.4]
    at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:138) ~[spring-data-commons-2.6.4.jar:2.6.4]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.19.jar:5.3.19]
    at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:80) ~[spring-data-commons-2.6.4.jar:2.6.4]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.19.jar:5.3.19]
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123) ~[spring-tx-5.3.19.jar:5.3.19]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388) ~[spring-tx-5.3.19.jar:5.3.19]
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-5.3.19.jar:5.3.19]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.19.jar:5.3.19]
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137) ~[spring-tx-5.3.19.jar:5.3.19]
    ... 66 common frames omitted
Caused by: java.lang.IllegalArgumentException: Can not get java.util.UUID field Parent.guid on java.lang.Long
    at java.base/jdk.internal.reflect.MethodHandleFieldAccessorImpl.newGetIllegalArgumentException(MethodHandleFieldAccessorImpl.java:86) ~[na:na]
    at java.base/jdk.internal.reflect.MethodHandleObjectFieldAccessorImpl.get(MethodHandleObjectFieldAccessorImpl.java:61) ~[na:na]
    at java.base/java.lang.reflect.Field.get(Field.java:428) ~[na:na]
    at org.hibernate.property.access.spi.GetterFieldImpl.get(GetterFieldImpl.java:71) ~[hibernate-core-5.6.8.Final.jar:5.6.8.Final]
    ... 109 common frames omitted

P.S.第三种方法有奇怪的错误,hibernate尝试将id字段值转换为UUID

java spring hibernate jpa spring-data-jpa
1个回答
0
投票

您在第三种方法中遇到的错误表明实体类中的字段类型与底层数据库架构之间不匹配。

  • 错误Can not get java.util.UUID field Parent.guid on java.lang.Long表明Hibernate正在尝试从实际上是Long的字段读取UUID。

  • 您已将 ParentEntity 中的 id 字段声明为整数,但它应该是 UUID 类型以匹配数据库架构。

纠正此问题:

  • 将 ParentEntity 中的 id 字段更改为 UUID 类型。 确保正确定义实体之间的关系,尤其是 @JoinColumn 映射。

这是更新的片段:

@Data
@NoArgsConstructor
@Entity
@Table(name = "Parent")
@Inheritance(strategy = InheritanceType.JOINED)
public class ParentEntity {
@Id
private UUID id;  // Change type to UUID
private UUID guid;
}

@Data
@NoArgsConstructor
@Entity
@Table(name = "A")
public class AEntity {
private String someASpecificField;

@OneToOne
@JoinColumn(name = "id")  // Ensure correct column name
private ParentEntity parent;

@OneToOne(mappedBy = "b")
private BEntity b;
}

@Data
@NoArgsConstructor
@Entity
@Table(name = "B")
public class BEntity {
private String someBSpecificField;

@OneToOne
@JoinColumn(name = "id")  // Ensure correct column name
private ParentEntity parent;

@OneToOne(mappedBy = "b")
private AEntity a;
}

确保@JoinColumn中的列名与数据库表中的实际列名正确对应。此外,字段类型和关系应与您的数据库架构保持一致,以避免数据检索期间出现不匹配和异常。

© www.soinside.com 2019 - 2024. All rights reserved.