3

My question is pretty similar to the following:

JPA: How do I add new Items to a List with a OneToMany annotation

Why merging is not cascaded on a one to many relationship

JPA with JTA: Persist entity and merge cascaded child entities

I also had a look in Wikibooks but still not able to fix my problem

So, my problem is that I have a Parent class with childs, and when I add a new child and use entityManager.merge(parent) to update the new children are not inserted and I get an error of null primary key.

I'm able to create a Parent with whatever children I want, and update theses children, but not to add a new child.

Example:

@Entity
public class Parent {
    private String name;
    @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
    private List<Child> children;
}
@Entity
public class Child {
    private String name;
    @ManyToOne
    private Parent parent;
}

If I create a Parent with some children it works fine. If I update the children's attributes it works fine, and if I add a new child to parent it does not work.

public void foo(){

    Parent parent = new Parent();
    List<Child> children = new ArrayList<Child>();
    children.add(new Child());
    children.add(new Child());
    children.add(new Child());
    parent.setChildren(children);
    //it works
    entityManager.persist(parent);

    //and lets say that I have updated some attributes
    changeAttributesValues(parent);
    changeAttributesValues(children);
    //it still working and the values are updated properly
    entityManager.merge(parent);

    //but if I add some child
    List<Child> children = parent.getChildren();
    children.add(moreOneChild);
    parent.setChildren(children);
    entityManager.merge(parent);
    //here I got an error saying that jpa cannot insert my attribute because my PK is null
}

Note: My PK is a composite Key (lets say in this example that it is the idFromParent + idCompany)

Thanks in advance.

EDIT

I solved the problem removing the composite key and adding an auto generated ID. I'm posting my real code and what I have done to make it work. With this code,

@Entity
public class Serie implements Serializable{ 

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SERIE_ID_SEQ")
    @SequenceGenerator(name = "SERIE_ID_SEQ", sequenceName = "real.serie_id_seq")
    @Column(name = "idserie")
    private Long id;

    @Basic
    @Column(name = "nmserie")
    private String nmSerie;

    @OneToMany(mappedBy = "serie", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
    private List<SerieExercise> serieExercises;

    @ManyToOne
    @JoinColumn(name = "idtrainning")
    private Trainning Trainning;
}

@Entity
public class SerieExercise implements Serializable{

    @Basic
    @Column(name = "nrrepetition")
    private int nrRepetition;

    @Column(name = "tminterval")
    @Temporal(TemporalType.TIMESTAMP)
    private Date tmInterval;

    @Id
    @Basic
    @Column(name = "nrorder")
    private Integer nrOrder;

    @Id
    @ManyToOne
    @JoinColumn(name = "idexercise")
    private Exercise exercise;

    @Id
    @ManyToOne
    @JoinColumn(name = "idserie")
    private Serie serie;
}

With this code I get this error when I try to insert one more SerieExercise in my List:

Caused by: org.postgresql.util.PSQLException: ERRO: ERROR: null value in column "nrorder" violates not-null constraint
  Detail: Failing row contains(null, 10, null, null, null).
    at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2270)
    at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1998)
    at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:255)
    at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:570)
    at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:420)
    at org.postgresql.jdbc2.AbstractJdbc2Statement.executeUpdate(AbstractJdbc2Statement.java:366)
    at org.jboss.jca.adapters.jdbc.WrappedPreparedStatement.executeUpdate(WrappedPreparedStatement.java:493)
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:208) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
    ... 132 more

I have debugging and the values before I call entityManager.merge() are not null. I'm using hibernate provider. I'm able to insert a Serie with whatever SerieExercise I want in my list, and able to update the values, but not able to add a new SerieExercise in my List.

To fix I made these changes:

@Entity
public class SerieExercise implements Serializable{

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SER_EXER_ID_SEQ")
    @SequenceGenerator(name = "SER_EXER_ID_SEQ", sequenceName  = "realvida.ser_exer_id_seq")
    @Column(name = "idserieexercicio")
    private Long id;

    @Basic
    @Column(name = "nrrepetition")
    private int nrRepetition;

    @Column(name = "tminterval")
    @Temporal(TemporalType.TIMESTAMP)
    private Date tmInterval;

    //@Id
    @Basic
    @Column(name = "nrorder")
    private Integer nrOrder;

    //@Id
    @ManyToOne
    @JoinColumn(name = "idexercise")
    private Exercise exercise;

    //@Id
    @ManyToOne
    @JoinColumn(name = "idserie")
    private Serie serie;
}

Does anyone know why it happens? Can I make this work using a composite key?

Let me know if you need more info.

Community
  • 1
  • 1
Klinsman Maia
  • 45
  • 1
  • 1
  • 5
  • Did you set the parent in moreOneChild? Can you make a simpler example? – K.Nicholas Mar 03 '16 at 20:22
  • Sorry, code edited now. Looks that the problem is related to have a composite primary key. I'm still researching. – Klinsman Maia Mar 03 '16 at 20:48
  • I can get your save sequence to work, so there is nothing wrong with that. You need to make a reproducible question. If you have a composite primary key and your getting a key violation, that needs to be the basis of your question – K.Nicholas Mar 03 '16 at 22:01
  • Got it, thanks for the tip, that is my seconf question here, next time I'll try improve my question. – Klinsman Maia Mar 04 '16 at 13:15
  • Where's the code for when you try to insert one more SerieExercise into the List? – mwarren Mar 04 '16 at 13:36
  • You should add a new question instead of adding on to an existing one. You have accepted an answer that is not an answer. Even though it is `nice` to reward the effort, other readers may be mislead by thinking it actually is an answer. – K.Nicholas Mar 04 '16 at 14:50

2 Answers2

0

This line of code looks wrong to me:

parent.addChildren(moreOneChild);

I would have thought it should be something like this:

List<Child> children = parent.getChildren();
children.add(moreOneChild);
entityManager.merge(parent);
mwarren
  • 2,409
  • 1
  • 22
  • 28
0

I think your mapping at child side looks wrong to me. You must maintain this @ManyToOne relationship like this:

@ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.ALL)
@JoinColumn(name="idParent", nullable=false)
private Parent parent;

I mean, usually at the owner side of a @ManyToOne relationship you must use @JoinColumn. Maybe irrevelent but worth mentioning.

Also, we want to see full code and full stacktrace, especially the child ID mapping(a composite id may be or nothe reason, but full code please). When it comes to ID regeneration, sometimes underlying database and JPA implementation library can matter. Are you using EclipseLink, Toplink or something else?

WesternGun
  • 11,303
  • 6
  • 88
  • 157
  • Yeah, the problem was related to have a composite key. I have changed the composite key to an Auto Generated Id, it solves the problem. Thank you very much. – Klinsman Maia Mar 04 '16 at 12:32
  • I edited the post with my real code and the error, do you know what can I do to make it work with composite key? Is there some thing wrong with my mapping? I can persist properly and edit the persited values, but not add a new "SerieExercise". – Klinsman Maia Mar 04 '16 at 13:18
  • I think you should post your CompositeID code, which field you want to map, etc., if you still want to add a CompositeID. But if you are satisfied with Simple ID, that's fine... Actually I don't usually use composite id and I don't see many cases where it is really necessary. – WesternGun Mar 06 '16 at 13:30