agauchy
agauchy

Reputation: 117

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

Upvotes: 1

Views: 5180

Answers (2)

OcterA
OcterA

Reputation: 121

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)

Upvotes: 1

IntelliData
IntelliData

Reputation: 432

In order for b.ID to have a value, you must add a to b, as in :

b.setA(a);

The constructor for B sets the field b.bId, but not the field b.a ...


UPDATE:

Try changing this:

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

to this:

@OneToMany(fetch=FetchType.LAZY, cascade={CascadeType.PERSIST,CascadeType.MERGE, CascadeType.REMOVE})
@JoinColumn(name="ID")
@NotNull
private List<B> b;

Similar to the answer on this post. Please let me know if it worked.

Upvotes: 1

Related Questions