javaspring-bootjpa

Jpa - Join table that contains 2 PK but only one of this two is used


I'm running a Spring Boot app.

I have these two tables in my database. I fill an example of data that contains these tables.

Table user Here column id is a PK and column state_id is a FK from table state with a reference of column id

id name state_id
33 John Doe 1

Table state Here, columns id and century are both primary keys of the table

id century label
1 21 1

I want to model these two tables as JPA entities, so I did it like that :

StatePK.java :

public class StatePK implements Serializable {
     private Long id;
     private Integer century;
}

State.java :

@Entity
@Table(name = "state")
@IdClass(StatePK.class)
public class State {

       @Id
       private Long id;

       @Id
       private Integer century;

       private String label;
}

User.java :

@Entity
@Table(name = "user")
public class User implements Serializable {

       @Id
       private Long id;

       private String name;

       @ManyToOne
       @JoinColumn(name = "state_id")
       private State state;
}

But when I compile with Maven, it's ok, but when I start my app, I have this error :

Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: An association that targets entity 'fr..infrastructure.provider.jpa.model.State' from entity 'fr.infrastructure.provider.jpa.model.User' has 1 '@JoinColumn's but the primary key has 2 columns"

Can someone help me with that issue, please?


Solution

  • If you want your state table to have a primary key composed of two fields (id and century), both columns must appear in the user table as foreign keys. According to your schema, only state_id seems to be present in user as a foreign key. You need to add also a state_century column within user.

    Furthermore, as @RobSpoor suggested in the comments, this is assuming that multiple states with the same id and different centuries (or the other way around) can be linked to the same user.

    However, if that is the case, once you've amended the user table, you should replace the annotation @JoinColumn in the User class with @JoinColumns to support foreign keys with multiple fields.

    In your snippet, under User.java there is a class named State. I assume that is just a typo and you meant User.

    @Entity
    @Table(name = "state")
    @IdClass(StatePK.class)
    public class State implements Serializable {
    
        @Id
        private Long id;
    
        @Id
        private Integer century;
    
        private String label;
    }
    
    @Entity
    @Table(name = "user")
    public class User implements Serializable {
    
        @Id
        private Long id;
    
        private String name;
    
        @ManyToOne
        @JoinColumns({
            @JoinColumn(name = "state_id", referencedColumnName = "id"),
            @JoinColumn(name = "state_century", referencedColumnName = "century")
        })
        private State state;
    }