Laures
Laures

Reputation: 5489

Error when saving @IdClass using Entity

i created an entity with a composite id that looks like this:

@Entity
@IdClass(CompId.class)
public class CompEntity {

    @Id
    @ManyToOne(optional = false)
    private AccountEntity account;

    @Id
    @ManyToOne(optional = false)
    private ProductEntity product;
...
}

With the CompId looks like this:

public class CompId implements Serializable {

    private Long account;

    private Long product;
}

Both account and product use simple Long ids that are auto-generated.

In Unit tests, everything works. In a runing server when i try to save a new CompEntity, i get the following error:

org.springframework.dao.InvalidDataAccessApiUsageException: Can not set java.lang.Long field CompId.product to ProductEntity_$$_jvstd2f_36; nested exception is java.lang.IllegalArgumentException: Can not set java.lang.Long field CompId.product to ProductEntity_$$_jvstd2f_36

As as far as i understand jpa and online examples, this should work, so i have no idea what is going wrong.

I'm thankful for any advice.

EDIT1:

Here are the ProductEntity and AccountEntity

@Entity
public class AccountEntity implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
...
}

@Entity
public class ProductEntity implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
...
}

EDIT 2:

And the repository for CompEntity

public interface CompRepository extends JpaRepository<CompEntity, CompId> {
...
}

Upvotes: 2

Views: 2730

Answers (2)

Mason T.
Mason T.

Reputation: 1577

The @IdClass is used to create a composite key in hibernate entities.

Single Id Parent

If the parent class has a single key then use the Single Key's datatype in the @IdClass of the child.

Example:

//Parent
@Entity
@Table(name = "GROUP")
public class Group implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "GROUP_ID")
    private Long groupId;

    ...
}

//Child
@Entity
@Table(name = "RECORD")
@IdClass(RecordId.class)
public class Record implements Serializable {

    @Id
    @Column(name = "FIELD_ID")
    private Long fieldId;

    @Id
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="GROUP_ID") //Join Column is just for naming purposes
    private Group group;

    ...
}

//Composite Id for Record
public class RecordId implements Serializable {

    private Long fieldId;

    //Use the @Id datatype from Group
    private Long group;

    //Getters, Setters, and Constructor omitted for space
}

Composite Id Parent

If the parent class has a composite Id class then use the composite Id class as the datatype in the @IdClass of the child.

Example:

    //Parent
@Entity
@Table(name = "GROUP")
@IdClass(GroupId.class)
public class Group implements Serializable {

    @Id
    @Column(name = "GROUP_ID")
    private Long groupId;

    @Id
    @Column(name = "GROUP_NAME")
    private String groupName

    ...
}

//Composite Id for Group
public class GroupId implements Serializable {

    private Long groupId;

    //Use the @Id datatype from Group
    private String groupName;

    //Getters, Setters, and Constructor omitted for space
}



//Child
@Entity
@Table(name = "RECORD")
@IdClass(RecordId.class)
public class Record implements Serializable {

    @Id
    @Column(name = "FIELD_ID")
    private Long fieldId;


    /*
    * JoinColumns is just used for naming columns:
    *    -(name) indicates the column to create in this table Record
    *    -(referencedColumnName) indicates the corresponding column in the parent Group
    */
    @Id
    @ManyToOne(fetch = FetchType.LAZY)
     @JoinColumns({
        @JoinColumn(name="GROUP_ID", referencedColumnName="GROUP_ID"),
        @JoinColumn(name="GROUP_NAME", referencedColumnName="GROUP_NAME")
    })
    private Group group;

    ...
}

//Composite Id for Record
public class RecordId implements Serializable {

    private Long fieldId;

    //Use the @IdClass for the datatype because the parent has a composite key.
    private GroupId group;

    //Getters, Setters, and Constructor omitted for space
}

I put the bounty, and discovered I wasn't using the correct datatype in the child's @IdClass for the parent. I'm putting the rules for @IdClass creation in this answer to hopefully help others.

P.S.

The wrong @IdClass I originally created, and was the cause of my headache the other day.

    //Composite Id for Record
    public class RecordId implements Serializable {

        private Long fieldId;

        /*
         * WRONG WAY
         * 
         * The cause of my error was due to the wrong dataype being used in this child 
@IdClass.
         */
        private Group group;

        //Getters, Setters, and Constructor omitted for space
    }

Upvotes: 1

TrafLaw
TrafLaw

Reputation: 318

You need to have @JoinColumn on your relationships to specify which column to refer in that entity.

    @Entity
    @IdClass(CompId.class)
    public class CompEntity {

        @Id
        @ManyToOne(optional = false)
        @JoinColumn(name = "account_id")
        private AccountEntity account;

        @Id
        @ManyToOne(optional = false)
        @JoinColumn(name = "product_id")
        private ProductEntity product;
    }

Upvotes: 0

Related Questions