【问题标题】:Problem using a "chained" Foreign key on a composite key在复合键上使用“链式”外键时出现问题
【发布时间】:2020-01-06 16:26:19
【问题描述】:

我有几个 OneToOne 和 ManyToOne 关系,我想为每个元素使用相同的主键,将键从父级“链接”到子级。

我的模型 (MWE) 具有三个实体:祖父母、父母和孩子。 Grandparent 和 Parent 是一对一相关的,而 Child 和 Parent 是多对一相关的,Child 具有一个以相关 Parent 作为成员的复合键。

实现如下:

祖父母:

@Entity
@Table(name = "GRANDPARENT")
public class Grandparent implements Serializable {
    @Id
    @Column(length = 20)
    String grandparentId;

    @OneToOne(mappedBy = "grandparent", cascade = CascadeType.ALL)
    Parent parent;
}

家长:

@Entity
@Table(name = "PARENT")
public class Parent implements Serializable {
    @Id
    @OneToOne
    @JoinColumn(name = "grandparent")
    Grandparent grandparent;

    @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
    List<Child> child;
}


孩子:

@Entity
@IdClass(ChildId.class)
@Table(name = "CHILD")
public class Child implements Serializable {
    @Id
    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "parent", referencedColumnName = "grandparent", columnDefinition = "varchar(20)")
    Parent parent;

    @Id
    @Column(length = 20)
    String childStringId;
}

ChildId:

public class ChildId implements Serializable {  
    Grandparent parent; // should have the type of the Id of "parent", according to the JPA spec
    String childStringId;

    // hashCode and equals methods
    ...
}

部署失败,但出现以下异常:

14:03:52,579 ERROR [org.jboss.msc.service.fail] (ServerService Thread Pool -- 85) MSC000001: Failed to start service jboss.persistenceunit."hibernateMWE.war#pu": org.jboss.msc.service.StartException in service jboss.persistenceunit."hibernateMWE.war#pu": java.util.NoSuchElementException
    at org.jboss.as.jpa@17.0.1.Final//org.jboss.as.jpa.service.PersistenceUnitServiceImpl$1$1.run(PersistenceUnitServiceImpl.java:198)
    at org.jboss.as.jpa@17.0.1.Final//org.jboss.as.jpa.service.PersistenceUnitServiceImpl$1$1.run(PersistenceUnitServiceImpl.java:128)
    at org.wildfly.security.elytron-private@1.9.1.Final//org.wildfly.security.manager.WildFlySecurityManager.doChecked(WildFlySecurityManager.java:649)
    at org.jboss.as.jpa@17.0.1.Final//org.jboss.as.jpa.service.PersistenceUnitServiceImpl$1.run(PersistenceUnitServiceImpl.java:212)
    at org.jboss.threads@2.3.3.Final//org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
    at org.jboss.threads@2.3.3.Final//org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1982)
    at org.jboss.threads@2.3.3.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
    at org.jboss.threads@2.3.3.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377)
    at java.base/java.lang.Thread.run(Thread.java:835)
    at org.jboss.threads@2.3.3.Final//org.jboss.threads.JBossThread.run(JBossThread.java:485)
Caused by: java.util.NoSuchElementException
    at java.base/java.util.ArrayList$Itr.next(ArrayList.java:999)
    at org.hibernate@5.3.10.Final//org.hibernate.internal.util.collections.JoinedIterator.next(JoinedIterator.java:47)
    at org.hibernate@5.3.10.Final//org.hibernate.cfg.annotations.TableBinder.linkJoinColumnWithValueOverridingNameIfImplicit(TableBinder.java:724)
    at org.hibernate@5.3.10.Final//org.hibernate.cfg.PkDrivenByDefaultMapsIdSecondPass.doSecondPass(PkDrivenByDefaultMapsIdSecondPass.java:37)
    at org.hibernate@5.3.10.Final//org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1696)
    at org.hibernate@5.3.10.Final//org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1653)
    at org.hibernate@5.3.10.Final//org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:287)
    at org.hibernate@5.3.10.Final//org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:904)
    at org.hibernate@5.3.10.Final//org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:935)
    at org.hibernate.jipijapa-hibernate5-3@17.0.1.Final//org.jboss.as.jpa.hibernate5.TwoPhaseBootstrapImpl.build(TwoPhaseBootstrapImpl.java:44)
    at org.jboss.as.jpa@17.0.1.Final//org.jboss.as.jpa.service.PersistenceUnitServiceImpl$1$1.run(PersistenceUnitServiceImpl.java:170)
    ... 9 more

14:03:52,580 ERROR [org.jboss.as.controller.management-operation] (DeploymentScanner-threads - 2) WFLYCTL0013: Operation ("full-replace-deployment") failed - address: ([]) - failure description: {"WFLYCTL0080: Failed services" => {"jboss.persistenceunit.\"hibernateMWE.war#pu\"" => "java.util.NoSuchElementException
    Caused by: java.util.NoSuchElementException"}}

如果我将 ChildId 中“parent”字段的类型从 Grandparent 更改为 Parent,则部署将失败并显示 MappingException: Unable to find column with logical name: grandparent in org.hibernate.mapping.Table(PARENT) and its related supertables and secondary tables。如果我改为将其更改为 String,它会成功部署,但持久化实体会引发 IllegalArgumentException: Can not set java.lang.String field entity.ChildId.parent to entity.Parent

我也尝试从 Child 中删除 JoinColumn 注释,但这会导致以下异常:

 Caused by: org.hibernate.MappingException: Foreign key (FKdjmtwrjnm98ag78day8q0okub:CHILD [])) must have same number of columns as the referenced primary key (PARENT [grandparent])"}

我在 wildfly 17.0.1.Final 上使用 Hibernate 5.3.10.Final,我的数据库是 MariaDB 10.0.38。我已经使用@MapsId 进行了此映射,但为了简单起见,我宁愿只使用@Ids。

【问题讨论】:

    标签: java hibernate jpa


    【解决方案1】:

    您可以尝试像这样映射ParentChild,让Grandparent 保持原样:

    @Entity
    @Table(name = "PARENT")
    public class Parent implements Serializable {
        @Id
        String id;
    
        @OneToOne
        @JoinColumn(name = "grandparent")
        @MapsId
        Grandparent grandparent;
    
        @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
        List<Child> child;
    }
    
    
    
    @Entity
    @IdClass(ChildId.class)
    @Table(name = "CHILD")
    public class Child implements Serializable {
        @Id
        @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
        @JoinColumn(name = "parent", referencedColumnName = "grandparent", columnDefinition = "varchar(20)")
        Parent parent;
    
        @Id
        @Column(length = 20)
        String childStringId;
    }
    
    public class ChildId implements Serializable {  
        String parent; // should have the type of the Id of "parent", according to the JPA spec
        String childStringId;
    
        // hashCode and equals methods
        ...
    }
    
    

    【讨论】:

    • 这个映射对我不起作用,我得到一个 MappingException 并且 Eclipse 抱怨我不应该使用具有通过 MapsId 获得的 id 的 Column。相反,我在祖父母字段上使用了 JoinColumn,它似乎正在工作。对吗?
    • @fsanches - 你是对的。我有点马虎。我已经为后代确定了我的答案。谢谢!
    猜你喜欢
    • 2020-07-05
    • 1970-01-01
    • 2021-10-03
    • 1970-01-01
    • 1970-01-01
    • 2016-06-25
    • 2011-12-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多