Reputation: 45722
I have two entities: A and B. B use the same PK as A entity. A and B linked with one-2-one, optional = false assosiation.
What I do:
//in session scope
A a = new A();
B b = new B();
a.setB(b);
b.setA(a);
session.save(a);
session.flush(); //EXCEPTION!
Exception:
org.postgresql.util.PSQLException: ERROR: insert or update on table "B" violates foreign key constraint "fk_A_pk" Detail: Key (id)=(5600) is not present in table "A"
What a problem:
Due to PS3 I can't save A with null B-property, because relationship is obligatory. But I can't save A with not-null property, because A haven't aID yet, and B can't imagine own id.
Question:
How could I save A & B entities?
PS1 B.PK is simultaneously a FK to A.PK field.
PS2 From the generated sql, I see that Hibernate try to proceed 'insert into B ...' query, before it proceed 'insert into A'.
PS3
A class:
@Entity
@Table(name = "A")
@Inheritance(strategy= InheritanceType.JOINED)
public class A{
@Id
@SequenceGenerator(name = "a_sequence", sequenceName = "sq_a")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "a_sequence")
@Column(name = "id")
private long aID;
@JsonIgnore
@OneToOne(mappedBy="a", cascade = CascadeType.ALL, optional = false, fetch = FetchType.LAZY)
private B b;
}
B class:
@Entity
@Table(name="b")
public class B {
@Id
@Column(name="id", unique=true, nullable=false)
@GeneratedValue(generator="gen")
@GenericGenerator(name="gen", strategy="foreign", parameters=@Parameter(name="property", value="a"))
private long aID;
@JsonIgnore
@PrimaryKeyJoinColumn
@OneToOne(cascade = CascadeType.ALL)
private A a;
}
ADDITION (PostgreSQL DDL):
CREATE TABLE A
(
id bigint NOT NULL,
CONSTRAINT pk_file PRIMARY KEY (id )
)
CREATE TABLE B
(
id integer NOT NULL,
CONSTRAINT pk_file PRIMARY KEY (id ),
CONSTRAINT fk_a_pk FOREIGN KEY (id)
REFERENCES A (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
Upvotes: 2
Views: 1087
Reputation: 7844
tl;dr: optional = false
on A
tells Hibernate to insert into B
first.
Making the association unidirectional
The JPA JavaDoc provides an example of a one-to-one association that assumes both the source and target share the same primary key values, but it uses an unidirectional association:
Remove the association from A
:
@Entity
public class A {
@Id
@SequenceGenerator(...)
@GeneratedValue(...)
private long aId;
}
If you are using an Hibernate version which supports JPA 2.0 (recent versions do), specify the @MapsId
attribute on B
, removing the foreign generator:
@Entity
public class B {
@Id
private long aId;
@OneToOne
@MapsId
private A a;
}
Keeping the association bidirectional
You cannot put optional = false
on the @OneToOne
annotation in A
. Since the foreign key (B.id
) referencing A
is mandatory, you should insert into A
first anyway:
INSERT INTO A (id) VALUES (1);
-- there isn't a corresponding record in B,
-- until we perform a second insert into B:
INSERT INTO B (id) VALUES (1);
Knowing this, let's make the association bidirectional again:
@Entity
public class A {
@Id
@SequenceGenerator(...)
@GeneratedValue(...)
private long aId;
// should work without 'optional = false'
@OneToOne(mappedBy = "a", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private B b;
}
See also Primary Keys through OneToOne and ManyToOne Relationships
Upvotes: 1