1

I have many-to-many relationship between Employee and SkillSet table, with additional column numberOfYears for each relation

employeeId  skillSetId  numberOfYears 
10          101         2

I am new to JPA and unable to define the entities with relationship. Should I define a new entity class for Employee_SkillSet table? Or can I have many to many relationship defined in both Employee and SkillSet class? Where do I specify numberOfYears column?

Edit: Seems duplicate, but I had explicit requirement of using @IdClass, and one of the entities was @MappedSuperclass, so have to define both ID instance, and referred entity object.

sidgate
  • 14,650
  • 11
  • 68
  • 119

2 Answers2

1

Since you need an additional field for the tuple (Employee, SkillSet), you have to make another entity.

@Entity
public class Employee implements Serializable {
    private @Id Long id;

    @OneToMany(mappedBy="employee")
    private List<EmployeeSkillSet> skillSets;
}

@Entity
public class SkillSet implements Serializable {
    private @Id Long id;
}

@Entity
public class EmployeeSkillSet implements Serializable {
    private @Id Long id;
    private @ManyToOne Employee employee;
    private @ManyToOne SkillSet skillSet;
    private @Basic int numberOfYears;
}

Of course you can choose to use a @IdClass to make ("employee", "skillSet") the primary key of EmployeeSkillSet like so:

@Entity @IdClass(EmployeeSkillSet.Key.class)
public class EmployeeSkillSet implements Serializable {

    private @Id @ManyToOne Employee employee;
    private @Id @ManyToOne SkillSet skillSet;
    private @Basic int numberOfYears;

    public static class Key implements Serializable {
        private Long employee; // plus getter+setter
        private Long skillSet; // plus getter+setter
        // plus hashCode, equals
    }
}
jabu.10245
  • 1,884
  • 1
  • 11
  • 20
  • I don't have `id` column in EmployeeSkillSet, how would I define the primary key on the entity ? – sidgate Nov 30 '15 at 18:22
  • I've updated my answer to show the `@IdClass` option. – jabu.10245 Dec 01 '15 at 09:35
  • Thanks. My use case is little complex where one of the entities in the relation is `@MappedSuperClass`, so cannot have it defined in EmployeeSkillSet. Instead I am defining both ID column and @ManyToOne. Please refer to my another question http://stackoverflow.com/questions/34013489/jpa-mapping-manytomany-relationship-refers-to-incorrect-column-name/34013650#34013650 – sidgate Dec 01 '15 at 09:48
1

Although you can use @ManyToMany annotation is better for performance reasons define two many to one relations in order to model the Many To Many relationship.

You will need 4 artifacts, there are

  • Employee Entity
  • SkillSet Entity
  • EmployeeSkillSet Relation entity (here you can specify numberOfYears Column)
  • EmployeeSkillSetPK (Primary Key for EmployeeSkillSet Relation entity)

The code would be something like this

Employee

package <your_package>;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 *
 */
@Entity
@Table(name="EMPLOYEE")
public class Employee implements Serializable {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    @Column(name="EMPLOYEEID")
    private int id;

    // Rest of columns and getter and setters for all
}

SkillSet

package <your_package>;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 *
 */
@Entity
@Table(name="SKILLSET")
public class SkillSet implements Serializable {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    @Column(name="SKILLSETID")
    private int id;

    // Rest of columns and getter and setters for all
}

EmployeeSkillSetPK

/**
 * 
 */
package <your_package>;

import java.io.Serializable;

import javax.persistence.Embeddable;

/**
 *
 */
@Embeddable
public class EmployeeSkillSetPK implements Serializable {

    @ManyToOne
    private Employee emp;

    @ManyToOne
    private SkillSet sk;
    /**
     * @return the employee
     */
    public Employee getEmployee() {
        return emp;
    }
    /**
     * @param employee the employee to set
     */
    public void setEmployee(Employee employee) {
        this.employee = employee;
    }
    /**
     * @return the sk
     */
    public SkillSet getSkillSet() {
        return sk;
    }
    /**
     * @param sk the sk to set
     */
    public void setSkillSet(SkillSet sk) {
        this.sk = sk;
    }

    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }

        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        EmployeeSkillSetPK that = (EmployeeSkillSetPK) o;

        if (employee != null ? !employee.equals(that.employee) : that.employee != null) {
            return false;
        }
        if (sk != null ? !sk.equals(that.sk) : that.sk != null) {
            return false;
        }

        return true;
    }

    public int hashCode() {
        int result;
        result = (employee != null ? employee.hashCode() : 0);
        result = 31 * result + (sk != null ? sk.hashCode() : 0);
        return result;
    }
}

EmployeeSkillSet

/**
 * 
 */
package <your_package>;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

/**
 *
 */
@Entity
@Table(name="EMPLOYEESKILLSET")
    @AssociationOverrides({ @AssociationOverride(name = "pk.employee", joinColumns = @JoinColumn(name = "EMPLOYEEID")),
    @AssociationOverride(name = "pk.sk", joinColumns = @JoinColumn(name = "SKILLSETID")) })
public class EmployeeSkillSet implements Serializable {

    @EmbeddedId
    private EmployeeSkillSetPK pk = new EmployeeSkillSetPK();

    @Column 
    private Integer numberOfYears;

    @Transient
    public Employee getEmployee() {
        return pk.getEmployee();
    }

    public void setEmployee(Employee employee) {
        pk.setEmployee(employee);
    }

    @Transient
    public SkillSet getSkillSet() {
        return pk.getSkillSet();
    }

    public void setSkillSet(SkillSet sk) {
        pk.setSkillSet(sk);
    }

    public Integer getNumbersOfYears() {
        return numberOfYears;
    }

    public void setNumbersOfYears(Integer numberOfYears) {
        this.numberOfYears = numberOfYears;
    } 
}

This is for JPA 1.0, I cannot test the code right now, but it should work.

Note that I wrote the table and columns names on my own. Adapt it as you wish.

Francisco Hernandez
  • 2,378
  • 14
  • 18
  • Thanks for detailed code. Can we use `@IdClass` instead of `@EmbeddedId`? – sidgate Nov 30 '15 at 18:26
  • I think is possible, IdClass is available since JPA 1.0 (http://docs.oracle.com/javaee/5/api/javax/persistence/IdClass.html). I don´t have an example right now using IdClass, but I can test it later at home if you want – Francisco Hernandez Nov 30 '15 at 18:31