21

I'm trying to create manytomany realation between Student and Teaching Course using Composite Primary key:

my classes:

@Entity
@Table(name="Student_mtm_cId")
public class Student {

    private String id;
    private Set<StudentTClass> teachingClasses = new HashSet<StudentTClass>();

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.student")
    public Set<StudentTClass> getTeachingClasses() {
        return teachingClasses;
    }
    public void setTeachingClasses(Set<StudentTClass> teachingClasses) {
        this.teachingClasses = teachingClasses;
    }

    public void addStudentToClass(TeachingClass teachingClass){
        StudentTClass studentTClass = new StudentTClass();
        studentTClass.setStudent(this);
        studentTClass.setTeachingClass(teachingClass);
        teachingClasses.add(studentTClass);
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    @Id @GeneratedValue(generator="system-uuid")
    @GenericGenerator(name="system-uuid", strategy = "uuid")
     @Column(name = "student_id", nullable = false)
    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    //all other setters and getters and isequal/hashCode omitted.
}

TeachingClass:

@Entity
@Table(name="TechingClass_MTM")
public class TeachingClass {

    private String id;
    private String name;
    private String description;
    private Set<StudentTClass> teachingClasses = new HashSet<StudentTClass>();

    public TeachingClass(){}

    public TeachingClass(String name, String description) {
        super();
        this.name = name;
        this.description = description;
    }

    public void addStudentToClass(Student student){
        StudentTClass studentTClass = new StudentTClass();
        studentTClass.setStudent(student);
        studentTClass.setTeachingClass(this);
        teachingClasses.add(studentTClass);
    }

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.teachingClass")
    public Set<StudentTClass> getTeachingClasses() {
        return teachingClasses;
    }

    public void setTeachingClasses(Set<StudentTClass> teachingClasses) {
        this.teachingClasses = teachingClasses;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    @Id @GeneratedValue(generator="system-uuid")
    @GenericGenerator(name="system-uuid", strategy = "uuid")    
     @Column(name = "teachingClass_id", nullable = false)
    public String getId() {
        return id;
    }

public void setId(String id) {
        this.id = id;
    }
}

Collection Objects:

@Entity
@Table(name = "student_TClass_MTM")
@AssociationOverrides({
@AssociationOverride(name = "pk.student", joinColumns = @JoinColumn(name = "student_id")),
@AssociationOverride(name = "pk.teachingClass", joinColumns = @JoinColumn(name = "teachingClass_id"))
        })
public class StudentTClass {

    @EmbeddedId
    private StudentTClassPK pk = new StudentTClassPK();

    public StudentTClassPK getPk() {
        return pk;
    }

    public void setPk(StudentTClassPK pk) {
        this.pk = pk;
    }

    public StudentTClass() {}

    @Transient
    public Student getStudent(){
     return this.pk.getStudent();
    }

    @Transient
    public TeachingClass getTeachingClass(){
     return this.pk.getTeachingClass();     
    }

    public void setStudent(Student student){
        this.pk.setStudent(student);
    }

    public void setTeachingClass(TeachingClass teachingClass){
        this.pk.setTeachingClass(teachingClass);    
    }

    }

Now The primary Key:

@Embeddable
public class StudentTClassPK implements Serializable{

    private static final long serialVersionUID = -7261887879839337877L;
    private Student student;
    private TeachingClass teachingClass;

    @ManyToOne
    public Student getStudent() {
        return student;
    }
    public void setStudent(Student student) {
        this.student = student;
    }

    @ManyToOne
    public TeachingClass getTeachingClass() {
        return teachingClass;
    }
    public void setTeachingClass(TeachingClass teachingClass) {
        this.teachingClass = teachingClass;
    }
    public StudentTClassPK(Student student, TeachingClass teachingClass) {
        this.student = student;
        this.teachingClass = teachingClass;
    }
    public StudentTClassPK() {}


}

When I'm trying to Persist Student I got the following error:

Caused by: org.hibernate.MappingException: Could not determine type for: com.vanilla.objects.Student, at table: student_TClass_MTM, for columns: [org.hibernate.mapping.Column(student)]
    at org.hibernate.mapping.SimpleValue.getType(SimpleValue.java:306)
    at org.hibernate.tuple.PropertyFactory.buildStandardProperty(PropertyFactory.java:143)
    at org.hibernate.tuple.component.ComponentMetamodel.<init>(ComponentMetamodel.java:68)
    at org.hibernate.mapping.Component.buildType(Component.java:184)
    at org.hibernate.mapping.Component.getType(Component.java:177)
    at org.hibernate.mapping.SimpleValue.isValid(SimpleValue.java:290)
    at org.hibernate.mapping.RootClass.validate(RootClass.java:236)
    at org.hibernate.cfg.Configuration.validate(Configuration.java:1362)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1865)
    at org.springframework.orm.hibernate3.LocalSessionFactoryBean.newSessionFactory(LocalSessionFactoryBean.java:855)
    at org.springframework.orm.hibernate3.LocalSessionFactoryBean.buildSessionFactory(LocalSessionFactoryBean.java:774)
    at org.springframework.orm.hibernate3.AbstractSessionFactoryBean.afterPropertiesSet(AbstractSessionFactoryBean.java:211)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1477)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1417)
    ... 51 more

What am I doing wrong?

Guido
  • 46,642
  • 28
  • 120
  • 174
danny.lesnik
  • 18,479
  • 29
  • 135
  • 200

2 Answers2

16

I solved this issue. I mapped Getter instead of field.

public class StudentTClass {

    //@EmbeddedId
    private StudentTClassPK pk = new StudentTClassPK();

    @EmbeddedId
    public StudentTClassPK getPk() {
        return pk;
    }
cнŝdk
  • 31,391
  • 7
  • 56
  • 78
danny.lesnik
  • 18,479
  • 29
  • 135
  • 200
  • This worked! I had to do this in both the class with the EmbeddedId tag, as well as in the class tagged as Embedabble. – Noru Feb 26 '17 at 03:03
4

If you can, I'd seriously suggest removing the composite keys. Worth with simple primary keys can both make a lot of problems go away and simplify your code. I have used composite keys in a database in the past because I had no ability to modify the db. Unfortunately I don't have the code. But I do remember it took some work to get it all working correctly. Sorry, can't help more.

drekka
  • 20,957
  • 14
  • 79
  • 135
  • 3
    I've solved this problem and my code works now perfect. Although, this is tricky to get Hibernate work with Composite Primary keys, I think it is useful in special cases where we need to guarantee that there will only be exactly one row per combination in a Many to Many relation. – danny.lesnik Jun 20 '11 at 17:25
  • 2
    I agree, there are cases where composite keys are legitimately useful. – aroth May 02 '13 at 02:12
  • 2
    @danny.lesnik Combination uniqueness can be guaranteed via a UNIQUE index. JPA 2.1 provides implementation-agnostic syntax. – Hollis Waite Sep 26 '13 at 22:38
  • Making ORM simpler just because we do not know how to do things with ORM framework is not a way imho. I like all possibilities which does DB provide and I like to preserve them in backend code also. Another thing is that it is a bit complex to set-up ORM which does work as we want to work. – Lubo Sep 06 '19 at 12:57