简体   繁体   中英

foreign key constraint in One To One relationship in JPA hibernate

I have two entities A and B with one-to-one relationship. When I want to insert them into DB, I get the following error:

Cannot add or update a child row: a foreign key constraint fails ( mydb . a , CONSTRAINT FK_77pkrkrin5nqsx16b6nw6k9r7 FOREIGN KEY ( id ) REFERENCES b ( b_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;

}

The insert operation will work perfectly if I remove the optional=false . I checked the objects before inserting them into DB and I make sure that A contains B and B contains A .

The SQL scripts for A and B creation are:

Hibernate: create table b (b_id integer not 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 foreign key (detail_id) references a (id)

Hibernate: alter table a add constraint FK_77pkrkrin5nqsx16b6nw6k9r7 foreign key (id) references b (b_id)

When I see the following sentence:

I checked the objects before inserting them into DB and I make sure that A contains B and B contains A.

I guess that you want to create a bidirectional one-to-one relationship. If so, your current mapping does not work as expected. Let us see what the JPA 2.0 spec (download link) states about this to understand the matter:

Relationships may be bidirectional or unidirectional. A bidirectional relationship has both an owning side and an inverse (non-owning) side. A unidirectional relationship has only an owning side. The owning side of a relationship determines the updates to the relationship in the database, as described in section 3.2.4.

The following rules apply to bidirectional relationships:

• The inverse side of a bidirectional relationship must refer to its owning side by use of the mappedBy element of the OneToOne, OneToMany, or ManyToMany annotation. The mappedBy element designates the property or field in the entity that is the owner of the relationship.

•The many side of one-to-many / many-to-one bidirectional relationships must be the owning side, hence the mappedBy element cannot be specified on the ManyToOne annotation.

• For one-to-one bidirectional relationships, the owning side corresponds to the side that contains the corresponding foreign key.

• For many-to-many bidirectional relationships either side may be the owning side.

So, according to the specification in a bidirectional one-to-one relationship one of the entities must be made the owning side and the other one the inverse side. Let assume entity A is the owning side the following mapping should work:

@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;

}

In order to make the above mapping work either the physical tables must be generated automatically or if you want to create the tables yourself, the corresponding SQL should look the following:

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));

NOTE:

  1. I removed the JSON-specific annotation to make the code shorter (I don't have any knowledge about them)

  2. If you want to make entity B the owning side, you have to adjust the relationship mapping accordingly.

  3. The @JoinColumn annotation is always on the owning side.

  4. Due to lack of time I haven't tested the code. If you find any bug (especially the MySQL syntax) just leave me a comment.

From the hibernate docs:

(Optional) Whether the association is optional. If set to false then a non-null relationship must always exist.

Right at the time of creation of B, the b_id is not available, it is created when the flush happens to database.

Since A has a foreign key relation on B(b_id), B needs to be created along with its id before you can insert A or mark this foreign relation as optional.

Create B and flush it to database, then create A. B has A is not required as its A who defines a foreign key to B and not vice-versa, B's reference to A is only reflexive, two-way foreign key will create cyclic reference issue.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM