2

I have an application with Parent-Children relationships. Every parent has a set of children(one-to-many) and every child has a parent (many-to-one). In the application I have a service-layer and in there I pretty much do the following:

  public void addChild() {
       parent = getParentFromDB();
       Child child = new Child();
       child.setParent(parent);
       saveChild(child);
       Set<Child> children = new HashSet<Child>();
       children.addAll(parent.getChildren());
       children.add(child);

       parent.setChildren(children); // notice this line here.

       saveParent(parent);
       doStuff(parent);
    }

The service layer is called from a facade, which is a regular Spring bean annotated with @Transactional. When I try to call the addChild() method I get the following result:

2013-03-05 17:09:50,195 [qtp511931089-83] WARN  org.hibernate.engine.jdbc.spi.SqlExceptionHelper - SQL Error: 1062, SQLState: 23000
2013-03-05 17:09:50,196 [qtp511931089-83] ERROR org.hibernate.engine.jdbc.spi.SqlExceptionHelper - Duplicate entry '25-9' for key 'PRIMARY'
2013-03-05 17:09:50,198 [qtp511931089-83] ERROR org.hibernate.engine.jdbc.batch.internal.BatchingBatch - HHH000315: Exception executing batch [Duplicate entry '25-9' for key 'PRIMARY']
2013-03-05 17:09:50.204:WARN:oejs.ServletHandler:/child/add
javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: Duplicate entry '25-9' for key 'PRIMARY'
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1377)
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1300)
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1306)

I did turn the SQL log in hibernate and it seems that hibernate is calling save twice!!! If I remove the "//notice this line here" line it all works fine - the entities are saved in the db and I can see the 25-9 relation in the DB. The problem is that in the end of the method I have the doStuff(parent); method which does some stuff with the parent and its children, so I can't remove the "// notice this line here" line

EDIT: This is how the parent is declared in the child:

@ManyToOne(fetch = FetchType.EAGER, targetEntity = ParentModel.class, cascade = { CascadeType.REMOVE })
@JoinTable(name = "PARENT_CHILDREN", joinColumns = @JoinColumn(name = "CHILD_ID"), inverseJoinColumns = @JoinColumn(name = "PARENT_ID"))
private ParentModel parent;

this is how the children are declared in the parent:

@OneToMany(fetch = FetchType.LAZY, targetEntity = ChildModel.class, cascade = { CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REMOVE })
@JoinTable(name = "PARENT_CHILDREN", joinColumns = @JoinColumn(name = "PARENT_ID"), inverseJoinColumns = @JoinColumn(name = "CHILD_ID"))
private Set<ChildModel> children;
Petar Tahchiev
  • 4,336
  • 4
  • 35
  • 48
  • please post the relationship definition for both parent and child – ben75 Mar 05 '13 at 15:36
  • You have a configuration error in the way you're persisting dependent entities. Can you post the mapping of `parent` and `child`? – Augusto Mar 05 '13 at 15:37

1 Answers1

2

It seems you are simply missing the mappedBy parameter in the relationship:

@OneToMany(mappedBy="parent", fetch = FetchType.LAZY, targetEntity = ChildModel.class, cascade = { CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REMOVE })
@JoinTable(name = "PARENT_CHILDREN", joinColumns = @JoinColumn(name = "PARENT_ID"), inverseJoinColumns = @JoinColumn(name = "CHILD_ID"))
private Set<ChildModel> children;
ben75
  • 29,217
  • 10
  • 88
  • 134
  • If I add the mappedBy parameter I get this exception during startup: Caused by: org.hibernate.AnnotationException: Associations marked as mappedBy must not define database mappings like \@JoinTable or @JoinColumn: xxxx.ParentModel.children – Petar Tahchiev Mar 06 '13 at 09:29
  • and what about removing the JoinTable ? – ben75 Mar 06 '13 at 09:37
  • 1
    I will accept your answer. It achieves what I'm after, although it does in a different way. The final configuration I ended up was: @ManyToOne(fetch = FetchType.EAGER, targetEntity = ParentModel.class, cascade = { CascadeType.REMOVE }) @JoinColumn(name = "PARENT_ID", nullable = true, referencedColumnName = "PK") private ParentModel parent; and @OneToMany(mappedBy = "parent", fetch = FetchType.LAZY, targetEntity = ChildrenModel.class, cascade = { CascadeType.REMOVE }) private Set children; – Petar Tahchiev Mar 06 '13 at 10:17
  • @ben75 I have a similar question. Are you willing to help me with it? Here is the link: http://stackoverflow.com/questions/25316761/duplicate-entry-string1-string2-for-key-primary – CodeMed Aug 14 '14 at 20:32