Github project to reproduce: https://raw.githubusercontent.com/tmonney/hibernate-mapsid
Suppose a pair of entities defined with the same composite primary key: {code|title=Parent.java}
@Entity public class Parent { @EmbeddedId public Key id; @OneToOne(cascade = CascadeType.ALL, mappedBy = "parent") public Child child; @Basic public String value; }
{code|title=Child.java}
@Entity
public class Child {
@EmbeddedId
private Key id;
@OneToOne
@MapsId
@JoinColumns({
@JoinColumn(name = "id1", referencedColumnName = "id1"),
@JoinColumn(name = "id2", referencedColumnName = "id2")
})
public Parent parent;
@Basic
public String value;
}
{code|title=Key.java}
@Embeddable public class Key implements Serializable {
private static final long serialVersionUID = 1L;
@Column(name = "id1") public Long id1;
@Column(name = "id2") public Long id2;
public Key() { }
public Key(Long id1, Long id2) { this.id1 = id1; this.id2 = id2; }
@Override public int hashCode() { return Objects.hash(id1, id2); }
@Override public boolean equals(Object obj) { if (!(obj instanceof Key)) { return false; }
Key that = (Key) obj; return Objects.equals(this.id1, that.id1) && Objects.equals(this.id2, that.id2); } }
The bug manifests itself when loading a {{Parent}} entity, getting its {{child}}, modifying some of its fields and finally flushing the entity manager:
Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
This is because Hibernates executes the following query:
{code:sql}
update FailingChild set value=? where id1=? and id2=?
And passes the same value for id1 and id2, which is obviously wrong.
Interestingly enough, everything works OK if the mapping of the {Child class is changed as follows (notice the second join column of the parent is renamed): {code|title=Child.java}
@Entity public class Child { @EmbeddedId public Key id;
@OneToOne @MapsId @JoinColumns( { @JoinColumn(name = "id1", referencedColumnName = "id1"), @JoinColumn(name = "renamed_id2", referencedColumnName = "id2") }
) public Parent parent;
@Basic public String value; }
|