JPA hibernate中One To One关系中的外键约束

时间:2016-07-14 07:34:23

标签: java mysql spring hibernate jpa

我有两个实体AB具有一对一的关系。当我想将它们插入DB时,我收到以下错误:

  

无法添加或更新子行:外键约束失败   (mydba,CONSTRAINT   FK_77pkrkrin5nqsx16b6nw6k9r7 FOREIGN KEY(id)参考   bb_id))

@JsonInclude(JsonInclude.Include.NON_NULL)     
@JsonIgnoreProperties(ignoreUnknown = true, value={"hibernateLazyInitializer", "handler"})
@Generated("org.jsonschema2pojo")
@Inheritance(strategy = InheritanceType.JOINED)
@Entity
public class A {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  @JsonIgnore
  private Integer id;

  @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false)
  @PrimaryKeyJoinColumn(referencedColumnName="AId")
  @JsonIgnore
  private B b;

}

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true, value = {"hibernateLazyInitializer", "handler"})
@Generated("org.jsonschema2pojo")
@Entity
public class B {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private int bId;

  @OneToOne()
  @JsonIgnore
  private A a;

}

如果删除optional=false,插入操作将完美运行。我在将对象插入数据库之前检查了对象,并确保A包含BB包含A

AB创建的SQL脚本是:

  

Hibernate:创建表b(b_id整数不是null auto_increment,   string_results longtext,a_id integer,primary key(b_id))

     

Hibernate:create table a(id integer not null auto_increment,primary   key(id))

     

Hibernate:alter table b add constraint FK_o3oen721etlltdc7ls82524nh   外键(detail_id)引用(id)

     

Hibernate:alter table a add constraint FK_77pkrkrin5nqsx16b6nw6k9r7   外键(id)引用b(b_id)

2 个答案:

答案 0 :(得分:3)

当我看到以下句子时:

  

我在将对象插入数据库之前检查了对象,并确保A包含B,B包含A.

我想你想创建一个双向的一对一关系。如果是这样,您当前的映射无法按预期工作。让我们看一下JPA 2.0 spec(下载链接)对此有何看法,以了解此事:

  

关系可以是双向的或单向的。双向关系具有拥有方和反向(非拥有方)。单向关系只有一个拥有方。关系的拥有方确定数据库中关系的更新,如3.2.4节所述。

     

以下规则适用于双向关系:

     

•双向关系的反面必须通过使用OneToOne,OneToMany或ManyToMany注释的mappedBy元素来引用其拥有方。 mappedBy元素指定作为关系所有者的实体中的属性或字段。

     

•一对多/多对一双向关系的多方必须是拥有方,因此无法在ManyToOne批注上指定mappedBy元素。

     

•对于一对一的双向关系,拥有方对应于包含相应外键的一侧。

     

•对于多对多双向关系,任何一方都可能是拥有方。

因此,根据规范,双向一对一关系中的一个实体必须成为拥有方,而另一个实体必须是反方。假设实体A是拥有方,以下映射应该起作用:

@Entity
public class A {

   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   private Integer id;

   @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false)
   @JoinColumn(name="b_id", referencedColumnName="ID")
   private B b;

}

@Entity
public class B {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    @OneToOne(mappedBy = "b")
    private A a;

}

为了使上述映射工作,必须自动生成物理表,或者如果您想自己创建表,相应的SQL应该如下所示:

create table a (id integer not null auto_increment,
                b_id integer not null,
                primary key (id),
                foreign key b_id references b (id));

create table b (id integer not null auto_increment, 
                string_results longtext, 
                primary key (id));

注:

  1. 我删除了特定于JSON的注释以缩短代码(我对它们没有任何了解)

  2. 如果您想让实体B成为拥有方,则必须相应地调整关系映射。

  3. @JoinColumn注释始终属于所有者。

  4. 由于时间不够,我没有测试过代码。如果您发现任何错误(尤其是MySQL语法),请给我留言。

答案 1 :(得分:0)

来自hibernate docs:

  

(可选)关联是否可选。如果设置为false,则必须始终存在非空关系。

在创建B时,b_id不可用,它是在数据库发生刷新时创建的。

由于A在B(b_id)上具有外键关系,因此需要先创建B及其ID,然后才能插入A或将此外键关系标记为可选。

创建B并将其刷新到数据库,然后创建A. B不需要A作为A定义B的外键而不是反之亦然,B对A的引用只是反身,2 -way外键会产生循环引用问题。