当前位置: 首页 > 知识库问答 >
问题:

Hibernate JPA ManyToOne外键映射(通过IdClass使用复合键)找不到列

马亮
2023-03-14

我正在尝试使用hibernate解决应用程序中的数据库映射问题。我们使用JPA注释这些类,到目前为止我们是成功的。但是,我们现在想引入一个组合键,它包含一个标识客户的唯一字符串和一个标识数据库条目的字符串。为了设置复合键,我们使用一个名为MandtId.java的IdClass。

然而,我们的映射似乎不起作用,因为它在另一侧找不到要连接的列。

以下是一些代码片段来显示我们的注释:

MandtId.java

@SuppressWarnings("serial")
@Embeddable
public class MandtId implements Serializable {
private String mandt;

private String id;

public MandtId() {
}

public MandtId(String mandt, String id) {
    this.mandt = mandt;
    this.id    = id;
}

public String getId() {
    return id;
}

public String getMandt() {
    return mandt;
}

public void setId(String x) {
    id = x;
}

public void setMandt(String x) {
    mandt = x;
}

/* (non-Javadoc)
 * @see java.lang.Object#hashCode()
 */
@Override
public int hashCode() {
    int hashCode = 0;
    if (mandt != null) hashCode |= mandt.hashCode();
    if (id != null) hashCode |= id.hashCode();
    return hashCode;
}

/* (non-Javadoc)
 * @see java.lang.Object#equals(java.lang.Object)
 */
@Override
public boolean equals(Object other) {
    if (other == null) return false;
    if (other == this) return true;
    if (!(other instanceof MandtId))return false;
    MandtId otherMandtId = (MandtId)other;
    return Objects.equals(mandt, otherMandtId.mandt) && Objects.equals(id, otherMandtId.id); 
}

/* (non-Javadoc)
 * @see java.lang.Object#toString()
 */
@Override
public String toString() {
    return (mandt != null ? mandt : "[null]") + " / " + (id != null ? id : "[null]");
}

}

枚举值.java

@Entity
@IdClass(MandtId.class)
public class EnumValue extends ReadWriteRecord {

@Id
@Column(name="mandt", insertable = false, updatable = false)
private String mandt;

@Id
@Column(name="enumValueId", insertable = false, updatable = false)
private String id;

@ManyToOne(fetch=FetchType.EAGER, optional=false)
@JoinColumns( {
    @JoinColumn(name="mandt", referencedColumnName="mandt", insertable = false, updatable = false),
    @JoinColumn(name="enumTypeId", referencedColumnName="enumTypeId", insertable = false, updatable = false)
} )
private EnumType enumType;

...more but irrelevant code...

枚举类型.java

@Entity
@IdClass(MandtId.class)
public class EnumType extends ReadWriteRecord {

@Id
@Column(name="mandt", insertable = false, updatable = false)
private String mandt;

@Id
@Column(name="enumTypeId", insertable = false, updatable = false)
private String id;

@OneToMany(mappedBy="enumType", fetch=FetchType.EAGER)
@OrderBy("sortIndex")
private List<EnumValue> values;

...more but irrelevant code...

类不包含任何与 id 相关的注释,只包含其他列。

读写记录.java

@MappedSuperclass
public abstract class ReadWriteRecord extends PersistentRecord {

@Column
@Convert(converter = LocalDateTimeConverter.class)
private LocalDateTime modifiedAt;

@Column
private String modifiedBy;

@Column
private boolean deleted;

public boolean getDeleted() {
    return deleted;
}

public void setDeleted(boolean x) {
    deleted = x;
}

public LocalDateTime getModifiedAt() {
    return modifiedAt;
}

public String getModifiedBy() {
    return modifiedBy;
}

public final void touch(UserContext context) {
    final LocalDateTime timestamp = LocalDateTime.now(ZoneId.of("Z"));
    onTouch(context, timestamp);
}

protected void onTouch(UserContext context, LocalDateTime timestamp) {
    modifiedBy = context != null ? context.getUserId() : null;
    modifiedAt = timestamp;
}
}

持久记录.java

public abstract class PersistentRecord {

/**
 * Indicates what to do when this record is passed to DatabaseSession.store().
 * <ul>
 * <li> if false, EntitiyManager.merge() is called.
 * <li> if true, EntitiyManager.persist() is called.
 * </ul>
 * <p>This field is managed by the DatabaseSession and therefore not public.
 *    Users must call markNew() after the creation of new records.
 */
boolean mustInsert = false;

@Override
public String toString() {
    return String.format("%s(%s - %s)", getClass().getSimpleName(), getMandt(), getId());
}

/**
 * Returns the ID of the record, used by toString() and intended for generic logging.
 * @return the primary key of the record, any format is allowed.
 */
public abstract String getId();

/**
 * Returns the Mandant of the record, used by toString() and intended for generic logging.
 * @return the primary key of the record, any format is allowed.
 */
public abstract String getMandt();

/**
 * Sets the ID of the record.
 * @param id primary key of the record.
 */
public abstract void setId(MandtId id);

/**
 * Must be called after creating new entities so that EntitiyManager.persist() is called instead of EntitiyManager.merge().
 */
protected void markNew() {
    mustInsert = true;
}

/**
 * Can be called to force lazy loading.
 */
protected void fetch() { }

}

在映射和调试此问题时,我们发现引发了一个异常,其中包含以下消息:

无法在org.hibernate.mapping.表(ENumType)及其相关的超级表和辅助表中找到具有逻辑名称: mandt的列

由于 mandt 列在两个类中都存在,因此应该找到它。关于如何解决这个问题的任何建议?我更喜欢映射建议,因为我最好保持数据库方案不变。

堆栈跟踪:

 javax.persistence.PersistenceException: [PersistenceUnit: default] Unable    to build Hibernate SessionFactory
at   org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.persistenceExcep     tion(EntityManagerFactoryBuilderImpl.java:1249)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.access$600(EntityManagerFactoryBuilderImpl.java:120)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:860)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:850)
at org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl.withTccl(ClassLoaderServiceImpl.java:425)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:849)
at org.hibernate.jpa.HibernatePersistenceProvider.createEntityManagerFactory(HibernatePersistenceProvider.java:75)
at org.hibernate.ejb.HibernatePersistence.createEntityManagerFactory(HibernatePersistence.java:54)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:55)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:39)
at platform.data.DatabaseSession.<init>(DatabaseSession.java:94)
at platform.data.MandtDaoImpl.loadAllMandt(MandtDaoImpl.java:16)
at platform.data.Mandt.getMandt(Mandt.java:18)
at platform.data.Mandt.lambda$0(Mandt.java:10)
at platform.utils.Lazy.value(Lazy.java:24)
at platform.data.Mandt.deflt(Mandt.java:13)
at pm.business.InjectedLinkProject.getResourceTypes(InjectedLinkProject.java:66)
at pm.business.InjectedLinkProject.<init>(InjectedLinkProject.java:57)
at pm.business.PmLogic.<clinit>(PmLogic.java:17)
at main.business.MainApplication.<clinit>(MainApplication.java:22)
at main.application.MainController.<clinit>(MainController.java:25)
at Routes$$anonfun$routes$1$$anonfun$applyOrElse$158$$anonfun$apply$158.apply(routes_routing.scala:2477)
at Routes$$anonfun$routes$1$$anonfun$applyOrElse$158$$anonfun$apply$158.apply(routes_routing.scala:2477)
at play.core.Router$HandlerInvokerFactory$$anon$5.resultCall(Router.scala:267)
at play.core.Router$HandlerInvokerFactory$JavaActionInvokerFactory$$anon$15$$anon$1.invocation(Router.scala:255)
at play.core.j.JavaAction$$anon$1.call(JavaAction.scala:55)
at play.core.j.JavaAction$$anonfun$11.apply(JavaAction.scala:82)
at play.core.j.JavaAction$$anonfun$11.apply(JavaAction.scala:82)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24)
at play.core.j.HttpExecutionContext$$anon$2.run(HttpExecutionContext.scala:40)
at play.api.libs.iteratee.Execution$trampoline$.execute(Execution.scala:46)
at play.core.j.HttpExecutionContext.execute(HttpExecutionContext.scala:32)
at scala.concurrent.impl.Future$.apply(Future.scala:31)
at scala.concurrent.Future$.apply(Future.scala:492)
at play.core.j.JavaAction$class.apply(JavaAction.scala:82)
at play.core.Router$HandlerInvokerFactory$JavaActionInvokerFactory$$anon$15$$anon$1.apply(Router.scala:252)
at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4$$anonfun$apply$5.apply(Action.scala:130)
at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4$$anonfun$apply$5.apply(Action.scala:130)
at play.utils.Threads$.withContextClassLoader(Threads.scala:21)
at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4.apply(Action.scala:129)
at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4.apply(Action.scala:128)
at scala.Option.map(Option.scala:146)
at play.api.mvc.Action$$anonfun$apply$1.apply(Action.scala:128)
at play.api.mvc.Action$$anonfun$apply$1.apply(Action.scala:121)
at play.api.libs.iteratee.Iteratee$$anonfun$mapM$1.apply(Iteratee.scala:483)
at play.api.libs.iteratee.Iteratee$$anonfun$mapM$1.apply(Iteratee.scala:483)
at play.api.libs.iteratee.Iteratee$$anonfun$flatMapM$1.apply(Iteratee.scala:519)
at play.api.libs.iteratee.Iteratee$$anonfun$flatMapM$1.apply(Iteratee.scala:519)
at play.api.libs.iteratee.Iteratee$$anonfun$flatMap$1$$anonfun$apply$14.apply(Iteratee.scala:496)
at play.api.libs.iteratee.Iteratee$$anonfun$flatMap$1$$anonfun$apply$14.apply(Iteratee.scala:496)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24)
at akka.dispatch.TaskInvocation.run(AbstractDispatcher.scala:41)
at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:393)
at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
 Caused by: org.hibernate.MappingException: Unable to find column with logical name: mandt in org.hibernate.mapping.Table(EnumType) and its related supertables and secondary tables
at org.hibernate.cfg.Ejb3JoinColumn.checkReferencedColumnsType(Ejb3JoinColumn.java:582)
at org.hibernate.cfg.BinderHelper.createSyntheticPropertyReference(BinderHelper.java:258)
at org.hibernate.cfg.ToOneFkSecondPass.doSecondPass(ToOneFkSecondPass.java:116)
at org.hibernate.cfg.Configuration.processEndOfQueue(Configuration.java:1598)
at org.hibernate.cfg.Configuration.processFkSecondPassInOrder(Configuration.java:1521)
at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1422)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1846)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:857)
... 56 more
  org.hibernate.MappingException: Unable to find column with logical name: mandt in org.hibernate.mapping.Table(EnumType) and its related supertables and secondary tables
at org.hibernate.cfg.Ejb3JoinColumn.checkReferencedColumnsType(Ejb3JoinColumn.java:582)
at org.hibernate.cfg.BinderHelper.createSyntheticPropertyReference(BinderHelper.java:258)
at org.hibernate.cfg.ToOneFkSecondPass.doSecondPass(ToOneFkSecondPass.java:116)
at org.hibernate.cfg.Configuration.processEndOfQueue(Configuration.java:1598)
at org.hibernate.cfg.Configuration.processFkSecondPassInOrder(Configuration.java:1521)
at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1422)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1846)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:857)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:850)
at org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl.withTccl(ClassLoaderServiceImpl.java:425)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:849)
at org.hibernate.jpa.HibernatePersistenceProvider.createEntityManagerFactory(HibernatePersistenceProvider.java:75)
at org.hibernate.ejb.HibernatePersistence.createEntityManagerFactory(HibernatePersistence.java:54)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:55)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:39)
at platform.data.DatabaseSession.<init>(DatabaseSession.java:94)
at platform.data.MandtDaoImpl.loadAllMandt(MandtDaoImpl.java:16)
at platform.data.Mandt.getMandt(Mandt.java:18)
at platform.data.Mandt.lambda$0(Mandt.java:10)
at platform.utils.Lazy.value(Lazy.java:24)
at platform.data.Mandt.deflt(Mandt.java:13)
at pm.business.InjectedLinkProject.getResourceTypes(InjectedLinkProject.java:66)
at pm.business.InjectedLinkProject.<init>(InjectedLinkProject.java:57)
at pm.business.PmLogic.<clinit>(PmLogic.java:17)
at main.business.MainApplication.<clinit>(MainApplication.java:22)
at main.application.MainController.<clinit>(MainController.java:25)
at Routes$$anonfun$routes$1$$anonfun$applyOrElse$158$$anonfun$apply$158.apply(routes_routing.scala:2477)
at Routes$$anonfun$routes$1$$anonfun$applyOrElse$158$$anonfun$apply$158.apply(routes_routing.scala:2477)
at play.core.Router$HandlerInvokerFactory$$anon$5.resultCall(Router.scala:267)
at play.core.Router$HandlerInvokerFactory$JavaActionInvokerFactory$$anon$15$$anon$1.invocation(Router.scala:255)
at play.core.j.JavaAction$$anon$1.call(JavaAction.scala:55)
at play.core.j.JavaAction$$anonfun$11.apply(JavaAction.scala:82)
at play.core.j.JavaAction$$anonfun$11.apply(JavaAction.scala:82)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24)
at play.core.j.HttpExecutionContext$$anon$2.run(HttpExecutionContext.scala:40)
at play.api.libs.iteratee.Execution$trampoline$.execute(Execution.scala:46)
at play.core.j.HttpExecutionContext.execute(HttpExecutionContext.scala:32)
at scala.concurrent.impl.Future$.apply(Future.scala:31)
at scala.concurrent.Future$.apply(Future.scala:492)
at play.core.j.JavaAction$class.apply(JavaAction.scala:82)
at play.core.Router$HandlerInvokerFactory$JavaActionInvokerFactory$$anon$15$$anon$1.apply(Router.scala:252)
at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4$$anonfun$apply$5.apply(Action.scala:130)
at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4$$anonfun$apply$5.apply(Action.scala:130)
at play.utils.Threads$.withContextClassLoader(Threads.scala:21)
at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4.apply(Action.scala:129)
at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4.apply(Action.scala:128)
at scala.Option.map(Option.scala:146)
at play.api.mvc.Action$$anonfun$apply$1.apply(Action.scala:128)
at play.api.mvc.Action$$anonfun$apply$1.apply(Action.scala:121)
at play.api.libs.iteratee.Iteratee$$anonfun$mapM$1.apply(Iteratee.scala:483)
at play.api.libs.iteratee.Iteratee$$anonfun$mapM$1.apply(Iteratee.scala:483)
at play.api.libs.iteratee.Iteratee$$anonfun$flatMapM$1.apply(Iteratee.scala:519)
at play.api.libs.iteratee.Iteratee$$anonfun$flatMapM$1.apply(Iteratee.scala:519)
at play.api.libs.iteratee.Iteratee$$anonfun$flatMap$1$$anonfun$apply$14.apply(Iteratee.scala:496)
at play.api.libs.iteratee.Iteratee$$anonfun$flatMap$1$$anonfun$apply$14.apply(Iteratee.scala:496)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24)
at akka.dispatch.TaskInvocation.run(AbstractDispatcher.scala:41)
at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:393)
at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)

谢谢

共有1个答案

傅高逸
2023-03-14

我可以用stackoverflow上其他地方提出的解决方法自己解决这个问题(https://stackoverflow.com/a/13147366/4563947).

以下是一些代码片段,以显示我们有效的注释:

MandtId.java

@SuppressWarnings("serial")
public class MandtId implements Serializable {
private String mandt;

private String id;

public MandtId() {
}

public MandtId(String mandt, String id) {
    this.mandt = mandt;
    this.id    = id;
}

public String getId() {
    return id;
}

public String getMandt() {
    return mandt;
}

public void setId(String x) {
    id = x;
}

public void setMandt(String x) {
    mandt = x;
}

/* (non-Javadoc)
 * @see java.lang.Object#hashCode()
 */
@Override
public int hashCode() {
    int hashCode = 0;
    if (mandt != null) hashCode |= mandt.hashCode();
    if (id != null) hashCode |= id.hashCode();
    return hashCode;
}

/* (non-Javadoc)
 * @see java.lang.Object#equals(java.lang.Object)
 */
@Override
public boolean equals(Object other) {
    if (other == null) return false;
    if (other == this) return true;
    if (!(other instanceof MandtId))return false;
    MandtId otherMandtId = (MandtId)other;
    return Objects.equals(mandt, otherMandtId.mandt) && Objects.equals(id, otherMandtId.id); 
}

/* (non-Javadoc)
 * @see java.lang.Object#toString()
 */
@Override
public String toString() {
    return (mandt != null ? mandt : "[null]") + " / " + (id != null ? id : "[null]");
}

}

枚举值.java

@Entity
@IdClass(MandtId.class)
public class EnumValue extends ReadWriteRecord {

@Id
@Column(name="Mandt")
private String mandt;

@Id
@Column(name="EnumValueId")
private String id;

@ManyToOne(fetch=FetchType.EAGER, optional=false)
    @JoinColumnsOrFormulas( {
        @JoinColumnOrFormula(column= @JoinColumn(name="Mandt", referencedColumnName="Mandt", insertable=false, updatable=false)),
        @JoinColumnOrFormula(column= @JoinColumn(name="EnumTypeId", referencedColumnName="EnumTypeId", insertable=false, updatable=false))
} )
private EnumType enumType;

...more but irrelevant code...

枚举类型.java

@Entity
@IdClass(MandtId.class)
public class EnumType extends ReadWriteRecord {

@Id
@Column(name="Mandt")
private String mandt;

@Id
@Column(name="EnumTypeId")
private String id;

@OneToMany(mappedBy="enumType", fetch=FetchType.EAGER)
@OrderBy("sortIndex")
private List<EnumValue> values;

...more but irrelevant code...

JoinColumnOrFormula注释修复了未找到逻辑名称、混合了可更新和不可更新约束等问题。重要的是,即使我们使用ColumnOrFormula进行注释,我们也从未指定公式。公式导致公式到列的转换出现问题。

 类似资料:
  • 我在jpa/Hibernate中映射复合键时遇到了问题。父实体和子实体都具有复合主键。 在运行时保存它时会出现以下异常: 我认为这是虚假的,因为有getter和setter。如果在priceRequestLegModel上使用mappedby=“leg”,在allocationModel上使用@mapsid,也会出现同样的错误。有人能指出我在这里做错了什么吗?

  • 我有两个表:A和B,都有一个复合主键。表B的PK也是表a主键的外键。 当我试图获取映射表B的类的实例时,我得到了以下异常: org.hibernate.TypeMismatchException 这两个类都位于package:com.cairone.ejemple01.entities 完整的日志输出为: 2016-09-21 12:28:24.505错误8568--[main] O.S.Boot

  • 问题内容: 我在为某些实体设置jpa映射时遇到麻烦。我有一个如下定义的父实体。 然后,我有一个带有复合键的子实体,以及此表的主键的外键,如下所示: 我知道子实体不正确,但是我不确定如何将其设置为具有复合PK。我知道我需要设置一个PK类,但是当一个字段是父类的外键时,我不确定该怎么做。一旦设置好,父级将如何引用子级实体? 任何帮助表示赞赏。 问题答案: 这受JPA 2规范的 第2.4.1节“与派生身

  • 问题内容: 我在这里搜索,但未找到任何类似的主题,因此我发布了一个新问题。 我正在使用现有数据库上的Hibernate。我们不允许更改表的结构和数据。该应用程序正在从数据库读取数据,并根据某种逻辑迁移到另一个数据存储。 现在的问题是关于复合PK映射。例如 表A具有复合PK。 表B也有一个复合PK,此复合PK的一部分是A的PK,此处也用作FK。 我尝试了几种方法,但都无济于事。谁能告诉一个有效的Hi

  • 我正在尝试我们非主键作为外键在我的应用程序。场景如下:我有EMPLOYEE和EMPLOYEE_PROPERTIES表。Employee和Employee属性之间存在一对多的关系。下面是我的架构: 下面是我的hibernate映射XML:------------------------------- -------------员工属性------------------- 是否可以引用非主键作为外键

  • 我有2个表,即user和user_session。