java.lang.IllegalArgumentException: expecting IdClass mapping

家住魔仙堡 提交于 2019-12-04 22:25:06

A composite key mapping can be either done with an IdClass or an Embeddable. If you want to use an IdClass you have to annotate your fields in Employee with @Id.

@IdClass(EmployeeId.class)
    class Person{
    @Id
     private Person person;
    @Id   
     private Branch branch;
    }

If you want to use an Embedded as a composite key please remove the @IdClass(EmployeeId.class) annotation from Person. You also don't need the person and branch field in your Person class because those are defined in your Embedded class.

Your situation corresponds to the chapter 2.4.1 Primary Keys Corresponding to Derived Identities of the JPA 2.1 Specification.

The identity of Employee is derived from identities of Person and Branch. You haven't shown the code of either of them, so I'll assume they have simple primary keys. In that relationship, Person and Branch are "parent entities" and Employee is a "dependant" entity.

The ID of Employee may be mapped using either IdClass or EmbeddedId, not both at the same time.

See chapter 2.4.1.1 Specification of Derived Identities.

If you want to use IdClass, then:

The names of the attributes of the id class and the Id attributes of the dependent entity class must correspond as follows:

  • The Id attribute in the entity class and the corresponding attribute in the id class must have the same name.

...

  • If an Id attribute in the entity is a many-to-one or one-to-one relationship to a parent entity, the corresponding attribute in the id class must be of (...) the type of the Id attribute of the parent entity.

So your classes would look like this (getters, setters, superfluous annotations etc. omitted)

@Entity
@IdClass(EmployeeId.class)
public class Employee {
   @Id
   @ManyToOne
   private Person person;
   @Id
   @ManyToOne
   private Branch branch;
}

public class EmployeeId {
    private Long person; // Corresponds to the type of Person ID, name matches the name of Employee.person
    private Long branch; // Corresponds to the type of Branch ID, name matches the name of Employee.branch
}

If you use EmbeddedId, then:

If the dependent entity uses an embedded id to represent its primary key, the attribute in the embedded id corresponding to the relationship attribute must be of the same type as the primary key of the parent entity and must be designated by the MapsId annotation applied to the relationship attribute. The value element of the MapsId annotation must be used to specify the name of the attribute within the embedded id to which the relationship attribute corresponds.

And the code would look like this:

@Entity
public class Employee {
   @EmbeddedId
   private EmployeeId id;
   @ManyToOne
   @MapsId("personId") // Corresponds to the name of EmployeeId.personId
   private Person person;
   @ManyToOne
   @MapsId("branchId") // Corresponds to the name of EmployeeId.branchId
   private Branch branch;
}

@Embeddable
public class EmployeeId {
    private Long personId; // Corresponds to the type of Person ID
    private Long branchId; // Corresponds to the type of Branch ID
}

Change to:

@Entity
@Table(name = "employee")
@Proxy(lazy = false)
@IdClass(EmployeeId.class)
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;

private EmployeeId id;
private Person person;
private Branch branch;
private boolean isActive;

public Employee() {

}

@EmbeddedId
@AttributeOverrides({@AttributeOverride(name = "person", column = @Column(name = "person_id") ),
    @AttributeOverride(name = "branch", column = @Column(name = "branch_id") )})

public EmployeeId getId() {
return id;
}

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

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "person_id")
public Person getPerson() {
return person;
}

public void setPerson(Person person) {
this.person = person;
}

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "branch_id")
public Branch getBranch() {
return branch;
}

public void setBranch(Branch branch) {
this.branch = branch;
}

@Column(name = "is_active")
public boolean getIsActive() {
return isActive;
}

public void setIsActive(boolean isActive) {
this.isActive = isActive;
}

}

The IdClass shouldnt be defined as Embeddable -

@Entity
@Table(name="employee")
@IdClass(EmployeeId.class)
public class Employee implements Serializable {
   private static final long serialVersionUID = 1L;

   @Id   
   @ManyToOne
   private Person person;
   @Id
   @ManyToOne
   private Branch branch;

   private boolean isActive;

   public Employee() { }
   //....
}

And -

public class EmployeeId implements Serializable {
    private static final long serialVersionUID = 1L;

    private Person person;
    private Branch branch;

    public EmployeeId() {}

    public EmployeeId(Person argPerson, Branch argbranch) {
        this.person = argPerson;
        this.branch = argbranch;
    }
}

Read your comment - Can I make a suggestion that you map Employee to person_id and branch_id, and not the JPA objects Person and Branch? This will let us test if your hbm config is correct. Id also suggest posting your hbm config as I think there is information missing from this problem

So the table will be similar to -

@Entity
@Table(name="employee")
@IdClass(EmployeeId.class)
public class Employee implements Serializable {
   private static final long serialVersionUID = 1L;

   @Id
   private Long personId;
   @Id
   private Long branchId;

   private boolean isActive;

   public Employee() { }
   //....
}

And -

And -

public class EmployeeId implements Serializable {
    private static final long serialVersionUID = 1L;

    private Long personId;
    private Long branchId;

    public EmployeeId() {}

    public EmployeeId(Person argPerson, Branch argbranch) {
        this.person = argPerson;
        this.branch = argbranch;
    }
}
SEY_91

This link could help you JPA - EmbeddedId with @ManytoOne

Relationship mappings defined within an embedded id class are not supported.Then you need to change the embeddedId class like this

@Embeddable
public class EmployeeId implements Serializable {
    private static final long serialVersionUID = 1L;

    private Long personId;
    private Long branchId;

    public EmployeeId() {

    }

    public EmployeeId(Long argPerson, Long argbranch) {
        this.personId = argPerson;
        this.branchId = argbranch;
    }


     @Column(name = "person_id")
    public Long getPersonId() {
        return personId;
    }
    public void setPersonId(Long personId) {
        this.personId = personId;
    }

    @Column(name = "branch_id")
    public Long getBranchId() {
        return branchId;
    }
    public void setBranchId(Long branchId) {
        this.branchId = branchId;
    }
}

JPA Composite Primary Key

Specifies a composite primary key class that is mapped to multiple fields or properties of the entity.

The names of the fields or properties in the primary key class and the primary key fields or properties of the entity must correspond and their types must be the same.

The answer is in here. read description for you. enter link description here

(Sample code)

@Entity
@Table(name = "EMP_PROJECT")
@IdClass(ProjectAssignmentId.class)
public class ProjectAssignment {
   @Id
   @Column(name = "EMP_ID", insertable = false, updatable = false)
   private int empId;

   @Id
   @Column(name = "PROJECT_ID", insertable = false, updatable = false)
   private int projectId;

   @ManyToOne
   @JoinColumn(name = "EMP_ID")
   Professor employee;

   @ManyToOne
   @JoinColumn(name = "PROJECT_ID")
   Project project;
   ....
}

public class ProjectAssignmentId implements Serializable {
   private int empId;
   private int projectId;
  ...
}
Akash Mishra

Mention @IdClass annotation with the class which holds the ID. Check the answer at this post

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!