java - Hibernate JPA ManyToOne 外键映射通过 IdClass 与复合键找不到列

标签 java hibernate jpa hibernate-mapping composite-primary-key

我正在尝试使用 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]");
}

}

EnumValue.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...

EnumType.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...

ReadWriteRecord 类不包含任何 id 相关注释,仅包含附加列。

ReadWriteRecord.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;
}
}

PersistendRecord.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() { }

}

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

Unable to find column with logical name: mandt in org.hibernate.mapping.Table(EnumType) and its related supertables and secondary tables

由于 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)

谢谢

最佳答案

我可以使用 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]");
}

}

EnumValue.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...

EnumType.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 进行注释,我们也根本不会指定公式。公式导致公式到列转换出现问题。

关于java - Hibernate JPA ManyToOne 外键映射通过 IdClass 与复合键找不到列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38221702/

相关文章:

java - 如何在java正则表达式中转义括号?

java - Rally REST API 找不到现有工作区

rest - Play 是否可以在没有 JPA 的情况下使用,并交换到您自己的 CRUD 实现中?

java - 使用 Transformer 时的 Hibernate 异常 PropertyNotFoundException

java - JPA 与 Google Cloud SQL "NoClassDefFoundError: java.util.prefs.Preferences is a restricted class"

java - spring默认@Transactional和默认丢失更新

java - Thymeleaf 的 3 片段插入不起作用

java - 如何实现贪心搜索算法

java - Hibernate 双向映射

java - Jersey + Hibernate 将 SessionFactory 注入(inject) DAO