2

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> {
...
}
8
  • is your product entity id a Long? oh and afaik the pkClass need getters and setters too, not too sure about that cause i always prefer the @embeddedId annotation Commented Dec 1, 2017 at 16:03
  • can you share you repository class, service class, account entity and Product entity? Commented Dec 2, 2017 at 0:48
  • @PrasadMarne i added the entities Commented Dec 4, 2017 at 9:34
  • @Laures, did u try my solution? Commented Dec 4, 2017 at 9:41
  • Since this has a bounty I guess someone has the same problem? Could you post the stack trace? Commented Apr 17, 2020 at 7:47

2 Answers 2

1

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
    }
Sign up to request clarification or add additional context in comments.

Comments

0

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;
    }

8 Comments

are you sure about the joinColumn annotation? if i use name my tests fail because there are multiple id columns on my CompEntity. if i use referencedColumnName the tests work (like before). I'm testing this on the server now.
Do you have @oneToMany on your product and account entity. can you share that relationship and also service class if possible?
While an Account and a product can both have many CompEntities the relationship is one sided (only in the CompEntity, no field in Product/Account). All entities are accessed through runtime-generated spring-data repositories, so there is no code to share
if you name your entity with @Table("account") then you can use account_id in @joinColumn same for product entity. so if you have table name use it and change name in join column accordingly. So you can get rid of the the first error
Best option is if you have a @Column() on id. then use that column name in @joinColumn
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.