单向映射——父子实体的复合键

Posted

技术标签:

【中文标题】单向映射——父子实体的复合键【英文标题】:Unidirectional mapping - composite keys of parent child entities 【发布时间】:2021-06-01 12:56:11 【问题描述】:

这是我正在处理的代码结构。

@Data
public class ParentKey implements Serializable 
    
    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(generator= "uuid")
    @GenericGenerator(name="uuid", strategy="uuid2")
    private String Pa; 
    
    @Id
    private String Pb;
     // ignoring getters and setters and hashcode and equals()

@Entity
@IdClass(ParentKey.class)
@Table(name="parent_demo")
public class Parent_Demo 
    
    @Id
    private String Pa; 
    
    @Id
//  @Column(name="p_b")
    private String Pb; 
    
    @OneToMany(cascade=CascadeType.ALL,orphanRemoval=true )
    @JoinColumn(name="p_a_fk", referencedColumnName="Pa")
    @JoinColumn(name="p_b_fk", referencedColumnName="Pb")
    private List<Child_Demo> childs = new ArrayList<>(); 
    
    private String pc;
  // ignoring getter and setters 

@Data
public class ChildKey implements Serializable 
    
    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    @Id
    private String ca; 
    
    @Id
    private String cb;
    
    @Id
    private String Pa;

@Entity
@IdClass(ChildKey.class)
@Table(name="child_demo")
public class Child_Demo 

    @Id
    private String Pa;  // can i link this value to the value of Pa in ParentKey which is being autogenerated 
    
    @Id
    private String ca; 
    
    @Id
    private String cb; 
    
    private String cc; 
    
    private String cd;

我想创建一对多的单向映射,其中父键的一部分是自动生成的,另一部分是在 api 调用期间在请求正文中发送的。类似地,在 api 调用期间,ca 和 cb 将在 Request Body 中发送。我想确保 Pa 也成为孩子复合主键的一部分。 下面的代码生成 2 个表 parent_demo 以 (pa,pb) 作为主键和 child_demo 以 (pa,ca,cb) 作为主键。但是,在进行 post api 调用时,我得到了违反 not null 约束,因为 child_demo 中的 pa 为 null。我想将 parent_demo 中的 pa(自动生成)的值复制到 child_demo 中的 pa。

有人可以帮忙吗?

【问题讨论】:

我认为这在这个例子中是行不通的,因为你的子实体的 PK 只引用了父实体的复合 PK 的一部分。子表的 PK 必须引用父 PK 的所有部分。否则它是多对多关系,必须这样注释。 【参考方案1】:

您的父实体的主键映射存在问题,并且没有自动方法将父主键的一部分包含在子实体的主键中。

复合主键映射

让我们先修复父实体的主键映射。您不能在 IdClass 上使用任何映射注释。您要么需要将其更改为@Embeddable 和use it as an @EmbeddedId,要么将映射注释移动到实体类。我在以下映射中使用了第二种方法:

@Data
public class ParentKey implements Serializable 
    
    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    private String Pa; 
    
    private String Pb;
     // ignoring getters and setters and hashcode and equals()




@Entity
@IdClass(ParentKey.class)
@Table(name="parent_demo")
public class Parent_Demo 
    
    @Id
    @GeneratedValue(generator= "uuid")
    @GenericGenerator(name="uuid", strategy="uuid2")
    private String Pa; 
    
    @Id
    private String Pb; 
    
    @OneToMany(cascade=CascadeType.ALL,orphanRemoval=true )
    @JoinColumn(name="p_a_fk", referencedColumnName="Pa")
    @JoinColumn(name="p_b_fk", referencedColumnName="Pb")
    private List<Child_Demo> childs = new ArrayList<>(); 
    
    private String pc;
  // ignoring getter and setters 

您还应该从ChildKey 类中删除@Id 注释。

@Data
public class ChildKey implements Serializable 
    
    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    private String ca; 
    
    private String cb;
    
    private String Pa;

使用父主键值

如果父实体的整个主键值成为子实体主键的一部分,您可以在关联上使用@MapsId 注解。我在this article中详细解释了这一点。

但如果您只想共享其中一个主键属性,这将不起作用。然后您需要手动处理主键。要将新父实体与新子实体持久化,您首先需要实例化并持久化父实体。这会立即设置该实体的主键值,您可以使用它来实例化和初始化您的子实体。

Parent_Demo p = new Parent_Demo();
p.setPb("something");
// initialize more attributes ...
em.persist(p); // or parentRepo.save(p);

Child_Demo c = new Child_Demo();
c.setPa(p.getPa());
// initialize more attributes ...
em.persist(c);

【讨论】:

感谢您的解释。@Thorben super helpfpul :)。对于那些想要将 parent 的整个复合键映射到 child 的人,他们必须声明这个 MapsId ManyToOne private Parent_object parent_ref 使它们也成为子对象的主键。

以上是关于单向映射——父子实体的复合键的主要内容,如果未能解决你的问题,请参考以下文章

如果同时保存的两个实体父子实体映射为一对多关系,则抛出 id not found 父类异常

如何在 Fluent NHibernate 中将一对一关系映射为复合键的一部分

Nhibernate 中的父子映射

实体框架中的自引用/父子关系

工作单元模式 - 管理父子关系

NO17--vue父子组件间单向数据流的解决办法