Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

@OneToMany and composite primary keys?

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, one component of which is a foreign key back to the id of the parent object.

The structure looks something like this:

+=============+                 +================+ | ParentObj   |                 | ObjectChild    | +-------------+ 1          0..* +----------------+ | id (pk)     |-----------------| parentId       | | ...         |                 | name           | +=============+                 | pos            |                                 | ...            |                                 +================+ 

I've tried a variety of combinations of annotations, none of which seem to work. This is the closest I've been able to come up with:

@Entity public class ParentObject {     @Column(nullable=false, updatable=false)     @Id @GeneratedValue(generator="...")     private String id;      @OneToMany(mappedBy="parent", fetch=FetchType.EAGER, cascade={CascadeType.ALL})     @IndexColumn(name = "pos", base=0)     private List<ObjectChild> attrs;      ... }  @Entity public class ChildObject {     @Embeddable     public static class Pk implements Serializable {         @Column(nullable=false, updatable=false)         private String parentId;          @Column(nullable=false, updatable=false)         private String name;          @Column(nullable=false, updatable=false)         private int pos;          @Override         public String toString() {             return new Formatter().format("%s.%s[%d]", parentId, name, pos).toString();         }          ...     }      @EmbeddedId     private Pk pk;      @ManyToOne     @JoinColumn(name="parentId")     private ParentObject parent;      ... } 

I arrived at this after a long bout of experimentation in which most of my other attempts yielded entities which hibernate couldn't even load for various reasons.

UPDATE: Thanks all for the comments; I have made some progress. I've made a few tweaks and I think it's closer (I've updated the code above). Now, however, the issue is on insert. The parent object seems to save fine, but the child objects are not saving, and what I've been able to determine is that hibernate is not filling out the parentId part of the (composite) primary key of the child objects, so I'm getting a not-unique error:

org.hibernate.NonUniqueObjectException:    a different object with the same identifier value was already associated     with the session: [org.kpruden.ObjectChild#null.attr1[0]] 

I'm populating the name and pos attributes in my own code, but of course I don't know the parent ID, because it hasn't been saved yet. Any ideas on how to convince hibernate to fill this out?

Thanks!

like image 376
Kris Pruden Avatar asked Apr 09 '10 23:04

Kris Pruden


People also ask

What is difference between composite and primary key?

While a primary key and a composite key might do the same things, the primary key will consist of one column, where the composite key will consist of two or more columns.

What is composite primary key?

A composite key can be defined as the primary key. This is done using SQL statements at the time of table creation. It means that data in the entire table is defined and indexed on the set of columns defined as the primary key.

What is composite primary key with example?

In a table representing students our primary key would now be firstName + lastName. Because students can have the same firstNames or the same lastNames these attributes are not simple keys. The primary key firstName + lastName for students is a composite key.

What is composite primary key in JPA?

A composite primary key, also called a composite key, is a combination of two or more columns to form a primary key for a table. In JPA, we have two options to define the composite keys: the @IdClass and @EmbeddedId annotations.


2 Answers

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; } } 
like image 120
RTBarnard Avatar answered Sep 22 '22 19:09

RTBarnard


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.

like image 24
Tomáš Záluský Avatar answered Sep 19 '22 19:09

Tomáš Záluský