【问题标题】:Hibernate Bidirectional one-to-many with composite keyHibernate 双向一对多与复合键
【发布时间】:2015-11-09 23:49:30
【问题描述】:

我正在尝试在多方具有复合键的两个实体之间创建双向一对多关联。多方的关键之一来自一方。另外,我需要多方作为协会的所有者。下面是一个示例代码,展示了我的代码的样子。

无接头

一侧的父类。我需要有这个协会的所有者。

    public class parent{

    @Id
    @Column(name = "NAME")
    private String name;

    @OneToMany(fetch=FetchType.LAZY)
    @JoinColumns({
                  @JoinColumn(name="NAME", nullable = false),
                  @JoinColumn(name="PARENT", nullable = false)})
    private Set<Child> childs;
}

Child Class 是多方面的。它的主键是“name”和“parent”。 “父母”来自关联。

public class child{

    @EmbeddedId
    @AttributeOverrides({
            @AttributeOverride(name="parent", column=@Column(name="PARENT", nullable=false)),
            @AttributeOverride(name="name", column=@Column(name="NAME", nullable=false))})
    private ChildId id;

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumns({
                   @JoinColumn(name="PARENT", nullable = false, updatable = false, insertable = false),
                   @JoinColumn(name="NAME", nullable = false, updatable = false, insertable = false)})
    private Parent parent;
}

ChildId 是嵌入的 id。

@Embeddable
public class childId{

    @Column(name = "PARENT")
    private String parent;

    @Column(name = "NAME")
    private String name;
}

可连接

父类

    public class parent{

    @Id
    @Column(name = "NAME")
    private String name;

    @OneToMany(fetch = FetchType.LAZY)
    @JoinTable(name="PARENTCHILD",
                joinColumns= {@JoinColumn(name="PNAME", referencedColumnName = "NAME", nullable = false)},
                inverseJoinColumns = {
                        @JoinColumn(name="CNAME", referencedColumnName = "NAME", nullable = false),
                        @JoinColumn(name="CPNAME", referencedColumnName = "PARENT", nullable = false)})
    private Set<Child> childs;
}

儿童班

public class child{

    @EmbeddedId
    @AttributeOverrides({
            @AttributeOverride(name="parent", column=@Column(name="PARENT", nullable=false)),
            @AttributeOverride(name="name", column=@Column(name="NAME", nullable=false))})
    private ChildId id;

    @MapsId("parent")
    @ManyToOne(fetch=FetchType.LAZY)
    @JoinTable(name="PARENTCHILD",
                inverseJoinColumns = {@JoinColumn(name="PNAME", referencedColumnName = "NAME", nullable = false)},
                joinColumns = {
                        @JoinColumn(name="CNAME", referencedColumnName = "NAME", nullable = false),
                        @JoinColumn(name="CPNAME", referencedColumnName = "PARENT", nullable = false)})
    private Parent parent;
}

问题1: 此代码不起作用。在“没有可连接”的情况下,它给出了以下异常。

Caused by: org.hibernate.AnnotationException: A Foreign key refering com.entity.Parent from com.entity.Child has the wrong number of column. should be 1

问题2: 如果是“可连接”,它会给出以下异常:

SQLCODE=-530, SQLSTATE=-23503, SQLERRMC=PARENTCHILD.FK_PARENTCHILD_CHILD

【问题讨论】:

  • 这个问题你不需要复合键,因为父名会被 Hibernate 本身作为外键插入到 Child 中。要使 Parent 成为关系的所有者,请添加 mappedBy = parent在孩子@ManyToOne 中。那应该可以解决你所有的问题。请验证并接受我用下面的代码 sn-ps 给出的答案。
  • 我给了你答案中的代码,你不需要加入,只需尝试我提供的代码即可。请参阅下面的答案,尝试该代码,让我知道你面临什么问题。跨度>
  • 谢谢@BirajChoudhury。我知道父母将作为外键在子表中。但是,假设孩子的唯一性是用父母的名字来识别的。然后,假设child的主键为EmbeddedId,你的代码将和我的一样。
  • 我不明白您的评论,您是否同意我的解决方案?或者我还没有理解你的问题?我在问这个 bcoz 我知道如何解决这个错误 - “来自 com.entity.Child 的外键引用 com.entity.Parent 的列数错误。应该是 1”,我只是不确定你是否需要它.
  • @BirajChoudhury。我想你还没有理解我的问题。简单,我有一个 Child 类,它与“name”(String)属性和“parent”(String)属性的组合统一,它是 Parent 类的主键。请接受这个假设,即 Child 的唯一性是由这两个定义的。然后,一个子对象可以有一个父对象,而一个父对象可以有多个子对象。您可以在 5.1.2.1.1 中找到 EmbeddedId here 的示例

标签: java hibernate jpa hibernate-mapping hibernate-annotations


【解决方案1】:

您不需要在 Child 中单独将 Parent 名称保留为 Id ,Hibernate 会为您做到这一点。我做了一个更简单的设计。您可以使用 mappedBy = childs 来控制关系,在 @ManyToOnemappedBy = parent@ManyToOne 侧。

@Entity
    public class Parent{

    @Id
    private String name;

    @OneToMany(fetch= FetchType.LAZY)
    private Set<Child> childs;

    public Parent(String name) {
        this.name = name;
    }
    public Parent(){}
}

@Entity
public class Child{

    @Id
    private String name;

    @ManyToOne(fetch=FetchType.LAZY)
    private Parent parent;
}

Hibernate 会生成三个表

具有列名称(主键)、父名称(外键)的子表 具有一列名称的父表(主键) Parent_child 表有两列 parent_name 和 child_name

编辑:根据 amir 的需要更改了解决方案,只需添加 mappedBy 您需要控制关系的任何一方。

@Entity
public class Child implements Serializable {

    @Id
    private String name;

    @Id
    @ManyToOne(fetch=FetchType.LAZY)
    private Parent parent;
}

@Entity
public class Parent{

    @Id
    private String name;

    @OneToMany(fetch= FetchType.LAZY)
    private Set<Child> childs;

    public Parent(String name) {
        this.name = name;
    }
    public Parent(){}
}

编辑 - 在子端命名列

@Id()
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="xyz")
private Parent parent;

【讨论】:

  • 伙计,您提供的 Child 类的主键仍然只有 Name 属性。 Child的主键由两个属性组成:1-name 2-parent(这个也是“name”名下Parent Class的主键)看我的代码
  • 执行它看看,我可以看到 3 个表,表子现在有 2 个主键,而 parent_child 表现在有 3 列,父、子和子父。我正在为你执行代码,而你在抱怨而不执行这是不公平的。
  • 对不起,谢谢您的解决方案。您的代码有效,但是,它在选择列名称方面没有给我任何灵活性。您能否告诉我如何更改可连接的名称及其列名。另外,如何在 Child 表中为父名称选择任意列名称。如果这里不应该使用 EmbeddedId,那它的目的是什么?
  • 例如,可连接的名称及其列
  • 我添加了代码,以便您可以命名孩子的父母姓名的列,也可以为孩子的姓名做 @column(name = "xyz") 和表名,您可以在公共类上面做带有@Table(name = "xyz") 的孩子。我希望您的所有疑问现在都能得到满足。
【解决方案2】:

要通过连接表使用一对多双向映射,请使用以下代码:

父类

 public class parent{
        @Id
        @Column(name = "id")
        @GeneratedValue(strategy = GenerationType.IDENTITY)    
        private Long id;
        .....
        .....
        @OneToMany(cascade=CascadeType.ALL)
        @JoinTable(name="Parent_Child", joinColumns={@JoinColumn(name ="parentId", referencedColumnName ="id")},
            inverseJoinColumns={@JoinColumn(name ="childId", referencedColumnName ="id")})
        private Set<Child> children;
        .....
        .....
 }

儿童班

 public class Child{
        @Id
        @Column(name = "id")
        @GeneratedValue(strategy = GenerationType.IDENTITY)    
        private Long id;
        .....
        .....
        @OneToOne(cascade=CascadeType.ALL)
        @JoinTable(name="Parent_Child", joinColumns={@JoinColumn(name ="childId", referencedColumnName ="id")},
            inverseJoinColumns={@JoinColumn(name ="parentId", referencedColumnName ="id")})
        private Parent parent;
        .....
        .....
 }

请注意,我在Parent 类中使用了OneTOMany 映射 - 因为根据您的逻辑,父母可以有多个孩子,而我在Child 类中使用了OneToOne 映射,因为一个孩子将有一个父母(如您的要求中所述)。

希望对你有帮助

【讨论】:

  • 你不应该在关系的两边都使用 CascadeType.ALL ,就像你删除一个 Child ,它会尝试删除它的 parent ,但它不能删除父级,因为该父级的所有其他子级都是仍然处于活动状态,所以 Hibernate 会抛出错误。删除父母也会杀死所有孩子。请理解这是关联关系而不是组合关系。
  • 此外,您添加的大多数注释都不是必需的,表 ID 已经是自解释的,因为它是名称。您需要添加 @Column(name = "xyz") 以添加含义并提高可读性,但在这里您将完全可读的表格更改为模糊命名。这不会增加任何价值,但会减少价值。也不需要@JoinTable。当 Hibernate 为您完成所有工作时,您为什么要使代码臃肿。
猜你喜欢
  • 2021-07-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-17
  • 1970-01-01
  • 2019-07-29
  • 2011-05-20
相关资源
最近更新 更多