简体   繁体   中英

JPA One-to-One relation creates a non unique foreign key

I have the follwoing entity:

@Entity
public class A {

  // id, etc..

  @OneToOne
  private B b;
}

The table a already exists and when i add the new field b to it, hibernate executes the following:

alter table a add column b_id int8
alter table a add constraint FKg76mxqt8whi8t8p4i7el95910 foreign key (b_id) references b

As you see, the foreign key column b_id is not unique. Why is that the case? Doesn´t the One-to-One relation imply that the foreign key has to be unique? That´s also what i found in the JPA specification for unidirectional One-to-One relatoins:

[...] The foreign key column has the same type as the primary key of table B and there is a unique key constraint on it.

To make it work i have to explicitally add the @JoinColumn(unique=true) annotation to the field. Why do i have to do that explicitally?

Bi-directional @OneToOne

In order to create a unique constraint you have to create a complete bi-directional OneToOne relation.

This means you have to add @OneToOne annotation on the Parent ( Owning ) entity, and add @OneToOne(mappedBy="...") on the Child entity.

This will create a unique constraint on your id column.

Otherwise, you're modeling two different relations instead of one bi-directional relation; because of that nothing is stopping the current model from having two Childs pointing to the same Parent.

The official JavaDoc for @OneToOne annotation has more information on additional parameters and advice on bi-directional relation.

UPD: Link to hibernate specification on how it is handling of @OneToOne relation:

  1. When using a bidirectional @OneToOne association, Hibernate enforces the unique constraint upon fetching the child-side.
  2. A unidirectional association follows the relational database foreign key semantics, the client-side owning the relationship.

In your case

This means that on your B entity model you should add a field with your A entity and annotate it with @OneToOne(mappedBy="b") to make your relation bi-direactional and complete, restricting access to single Parent and creating a unique constraint.

I came across this issue recently. My assumptions were @OneToOne must add a unique constraint on the foreign key. But hibernate doesn't do that.

See this section in the hibernate docs. Neither unidirectional nor bidirectional mappings add unique constraint (evident from the queries in the docs). That means hibernate won't throw an exception when adding rows with same foreign keys.

Although note that in case of bidirectional mapping, hibernate does a uniqueness check when fetching the parent entity (but not child). If there are multiple child ( owning ) entities refering to the same parent entity which is being fetched, hibernate throws a org.hibernate.exception.ConstraintViolationException .

The solution is to add @JoinColumn(unique = true, ...) on your child. This will also throws an error when inserting a row with a non-unique foreign key in the table.

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