agauchy agauchy - 5 months ago 264
Java Question

JPA + Hibernate + Spring - Parent Child persistence with generating ID leads to foreign key error

I have edited my question after the answer of IntelliData.

DB



In database i have, and i can't do any changes :

Table A {
ID NUMBER(10, 0) NOT NULL
DATE_DEBUT DATE NOT NULL
DATE_FIN DATE
.....
}

Table B {
ID NUMBER(10, 0) NOT NULL
B_ID VARCHAR2(10 BYTE) NOT NULL
}


In table A ID is PK, and it's a generated value.

In table B (ID, B_ID) is PK.

B.ID is FK on A.ID.

Entities



A



@Entity
@Table(name = "A")
public class A implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "aIdGenerator")
@SequenceGenerator(name = "aIdGenerator", sequenceName = "A_SEQ", allocationSize = 1)
@Column(name = "ID")
private Integer id;

@Column(name = "DATE_DEBUT")
private LocalDate dateDebut;

@Column(name = "DATE_FIN")
private LocalDate dateFin;

@OneToMany(mappedBy = "a", fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST })
private List<B> bs;
.
.
.
}


B



@Entity
@Table(name = "B")
public class B implements Serializable {

@Id
@ManyToOne
@JoinColumn(name = "ID", referencedColumnName = "ID")
private A a;

@Id
@Column(name = "B_ID")
private String bID;

public B(final A a, final String bID) {
super();
this.a = a
this.bID= bID;
}
.
.
.
}


Repository



public interface aRepository extends JpaRepository<A, Integer> {
// nothing more
}


My java



A a= new A();
a.setDateDebut(LocalDate.now());
a.setDateFin(LocalDate.now());

B bs = new ArrayList<B>();
bs.add(new B(a, "aaa"));
bs.add(new B(a, "bbb"));
bs.add(new B(a, "ccc"));

a.setBs(bs);

myrepo.saveAndFlush(a);


Problem (finaly)



ORA-01400: cannot insert NULL into ("B"."ID") ---> RESOLVED by IntelliData


But now I get

ORA-02291: integrity constraint (FK_B_ID) violated - parent key not found


And I don't understand why because Hibernate insert in the good order AND WITH GOOD VALUES... I managed to get those logs :

DEBUG [main] org.hibernate.SQL(109) - insert into GOELAND_DEPLOI (DATE_DEBUT, DATE_FIN, ID) values (?, ?, ?)
Hibernate: insert into A (DATE_DEBUT, DATE_FIN, ID) values (?, ?, ?)
TRACE [main] o.h.t.d.sql.BasicBinder(81) - binding parameter [1] as [DATE] - [2016-06-22]
TRACE [main] o.h.t.d.sql.BasicBinder(81) - binding parameter [1] as [DATE] - [2016-06-22]
TRACE [main] o.h.t.d.sql.BasicBinder(81) - binding parameter [2] as [DATE] - [2016-06-22]
TRACE [main] o.h.t.d.sql.BasicBinder(81) - binding parameter [2] as [DATE] - [2016-06-22]
TRACE [main] o.h.t.d.sql.BasicBinder(81) - binding parameter [3] as [INTEGER] - [127]
DEBUG [main] org.hibernate.SQL(109) - insert into B (ID, B_ID) values (?, ?)
Hibernate: insert into B (ID, B_ID) values (?, ?)
TRACE [main] o.h.t.d.sql.BasicBinder(81) - binding parameter [1] as [INTEGER] - [127]
TRACE [main] o.h.t.d.sql.BasicBinder(81) - binding parameter [2] as [VARCHAR] - [TEST]
DEBUG [main] o.h.e.j.spi.SqlExceptionHelper(139) - could not execute statement [n/a]
java.sql.SQLIntegrityConstraintViolationException: ORA-02291: violation de contrainte d'intégrité (COMMON.FK_B_ID) - clé parent introuvable


If it's not enought I can put more logs that appear on myrepo.saveAndFlush(a);

Answer

It's seem that another one change your ID. Are you sure to not have a trigger on your A table which update the value

eg :

Hibernate : seq.nextVal => 52
Hibernate : insert into A values (52...)
Database  : trigger on insert A : call to seq.nextVal => 53
Databse   : Value really inserted into A : 53

Hibernate didn't know the value has changed.

Two solution :

  • Edit your trigger : if id is set, don't get a new one
  • Make two step : insert A, get A, insert Bs (not recommanded)