I\'m using Hibernate with annotations (in spring), and I have an object which has an ordered, many-to-one relationship which a child object which has a composite primary key
I was badly looking for an answer but couldn't find a working solution. Though I had the OneToMany in parent correctly and ManyToOne in child correctly, during parent's save, child's key was not getting assigned, the auto-generated value from parent.
My problem was fixed upon adding an annotation javax.persistence.MapsId above the @ManyToOne mapping in the child entity (Java class)
@MapsId("java_field_name_of_child's_composite_key_that_needs_the_value_from_parent")
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "PARENT_ID", nullable = false, insertable = false, updatable = false)
private Parent parent;
This is on top of what was answered by @Pascal Thivent (answered on Apr 10 '10 at 1:40)
Please refer to the example code snippet in his post, earlier in this thread.
Thanks, PJR.
After much experimentation and frustration, I eventually determined that I cannot do exactly what I want.
Ultimately, I went ahead and gave the child object its own synthetic key and let Hibernate manage it. It's a not ideal, since the key is almost as big as the rest of the data, but it works.
After spending three days on this, I think I have found a solution, but to be honest, I don't like it and it can definitely be improved. However, it works and solves our problem.
Here is your entity constructor, but you could also do it in the setter method. Also, I used a Collection object but it should be same or similar with List:
...
public ParentObject(Collection<ObjectChild> children) {
Collection<ObjectChild> occ = new ArrayList<ObjectChild>();
for(ObjectChild obj:children){
obj.setParent(this);
occ.add(obj);
}
this.attrs = occ;
}
...
Basically, as someone else suggested, we must first manually set all the children's parent id before saving the parent (along with all children)
You should incorporate the ParentObject
reference just into ChildObject.Pk
rather than map parent and parentId separately:
(getters, setters, Hibernate attributes not related to problem and member access keywords omitted)
class ChildObject {
@Embeddable
static class Pk {
@ManyToOne...
@JoinColumn(name="parentId")
ParentObject parent;
@Column...
String name...
...
}
@EmbeddedId
Pk id;
}
In ParentObject
you then just put @OneToMany(mappedBy="id.parent")
and it works.
The Manning book Java Persistence with Hibernate has an example outlining how to do this in Section 7.2. Fortunately, even if you don't own the book, you can see a source code example of this by downloading the JPA version of the Caveat Emptor sample project (direct link here) and examining the classes Category
and CategorizedItem
in the auction.model
package.
I'll also summarize the key annotations below. Do let me know if it's still a no-go.
ParentObject:
@Entity
public class ParentObject {
@Id @GeneratedValue
@Column(name = "parentId", nullable=false, updatable=false)
private Long id;
@OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
@IndexColumn(name = "pos", base=0)
private List<ChildObject> attrs;
public Long getId () { return id; }
public List<ChildObject> getAttrs () { return attrs; }
}
ChildObject:
@Entity
public class ChildObject {
@Embeddable
public static class Pk implements Serializable {
@Column(name = "parentId", nullable=false, updatable=false)
private Long objectId;
@Column(nullable=false, updatable=false)
private String name;
@Column(nullable=false, updatable=false)
private int pos;
...
}
@EmbeddedId
private Pk id;
@ManyToOne
@JoinColumn(name="parentId", insertable = false, updatable = false)
@org.hibernate.annotations.ForeignKey(name = "FK_CHILD_OBJECT_PARENTID")
private ParentObject parent;
public Pk getId () { return id; }
public ParentObject getParent () { return parent; }
}
It seems that you got pretty close, and I am trying to do the same thing in my current system. I started with the surrogate key but would like to remove it in favor of a composite primary key consisting of the parent's PK and the index in the list.
I was able to get a one-to-one relationship that shares the PK from the master table by using a "foreign" generator:
@Entity
@GenericGenerator(
name = "Parent",
strategy = "foreign",
parameters = { @Parameter(name = "property", value = "parent") }
)
public class ChildObject implements Serializable {
@Id
@GeneratedValue(generator = "Parent")
@Column(name = "parent_id")
private int parentId;
@OneToOne(mappedBy = "childObject")
private ParentObject parentObject;
...
}
I wonder if you could add the @GenericGenerator and @GeneratedValue to solve the problem of Hibernate not assigning the parent's newly acquired PK during insertion.