0

I'm having a problem setting up a bidirectional association with the one-to-many side as the owner. I will describe my issue using an example from the Hibernate documentation slightly modified.

@Entity
public class Troop {
    @OneToMany(cascade = CascadeType.ALL)
    @JoinColumn(name="troop_fk") //we need to duplicate the physical information
    public Set<Soldier> getSoldiers() {
    ...
}

@Entity
public class Soldier {
    @ManyToOne()
    @JoinColumn(name="troop_fk", insertable=false, updatable=false, nullable = false)
    public Troop getTroop() {
    ...
} 

I understand that in the world of databases the owner of the above relationship would be soldier since it has the column with the foreign key, however this does no make sense in real world logic since a soldier is part of a troop and a troop owns a soldier.

I would like to be able to use CascadeType.ALL to automatically save all Soldier when I persist their troop.

The hibernate documentation indicates that:

This solution is not optimized and will produce additional UPDATE statements.

eg. log

Insert Into Troop(id) Values(1)
Insert Into Soldier(id) Values(1)
Insert Into Soldier(id) Values(2)

Update Soldier Set troop_fk = 1 Where id = 1
Update Soldier Set troop_fk = 1 Where id = 2

The problem is that when we first try to insert soldier without the troop_fk an exception is thrown since that column is not nullable.

Why does Hibernate not just add the troop_fk to the insert instead of updating the record later with it? Is there a way for me to do what I described above?

Mike Khan
  • 595
  • 1
  • 7
  • 18
  • Excerpt from JPA 2.0 spec, chapter 2.9 Entity Relationships: The inverse side of a bidirectional relationship must refer to its owning side by use of the `mappedBy` element of the `OneToOne`, `OneToMany`, or `ManyToMany` annotation. The `mappedBy` element designates the property or field in the entity that is the owner of the relationship. (...) The many side of one-to-many / many-to-one bidirectional relationships must be the owning side, hence the `mappedBy` element cannot be specified on the `ManyToOne` annotation. – wypieprz May 01 '14 at 18:15
  • I understand that, but as you can see from the link I posted above of the Hibernate documentation they suggest that you remove the mappedBy element and add a @JoinColum on the One-To-Many side – Mike Khan May 01 '14 at 18:25
  • the way I see it: When you persist the troop, Cascacde ALL will result in JPA to persist the soldier first, i.e. a flush to the database. But at that time, your troop doesn't have an id yet. So there is no way to fill troop_fk at the time the insert happens. Some databases support a constraint mode where the constraint check is deferred until the commit. That might solve your problem. ..but not sure what other problems this might bring with it. never tried it. – EasterBunnyBugSmasher May 01 '14 at 19:30

1 Answers1

0

The problem is the insertable=false, updatable=false.

This is explicitly telling Hibernate to NOT update the troop_fk when the Soldier entity is inserted or updated.

But you still won't be able to insert a new Soldier entity without first associating it with a troop because the fk column is not nullable.

If you want to be able to create Soldiers without a Troop, then you need to make the association optional: @ManyToOne(optional=true).

And I would change the Troop's @OneToMany association to use the mappedBy. In the 300-odd entities in our system we've never used a @JoinColumn on the @OneToMany side.

DuncanKinnear
  • 4,563
  • 2
  • 34
  • 65