【问题标题】:How to give multiple columns as primary key in entity - JPA如何在实体中将多列作为主键 - JPA
【发布时间】:2021-01-05 10:17:37
【问题描述】:

我有 3 个实体 - 课程、模块、时间线 在时间线实体中 - 我想提供 2 个键作为主键,即复合键。我该怎么给。请告诉我在下面的代码中要进行的更改:

课程:

@Id
@Column(name = "id")
Integer courseId;
@Column(name = "course_name")
String course_name;

模块:

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "module_id")
Integer module_id;
@Column(name = "module_type")
String module_type;
@Column(name = "module_name")
String module_name;
@Column(name = "duration")
Integer duration;
@OneToOne( cascade = CascadeType.ALL)
private Course course;

时间线:

    @Id
    @Column(name = "timeline_id")
    Integer timeline_id;
    @ManyToOne( cascade=CascadeType.ALL )
    private Module module;
    @ManyToOne( cascade = CascadeType.ALL)
    private Course course;

现在在时间轴中,我希望将 course_idtimeline_id 作为主键。请帮忙。 提前谢谢你。

更新: 我尝试使用 Embeddable 和 EmbeddedId:

@Embeddable
public class TimelineId implements Serializable{
    private Integer course_id;
    private Integer timelineId;
    getters and setters
    hashcode and equals
}

模块:

@Entity
@Table (name = "timeline")
public class Timeline {
    @EmbeddedId
    private TimelineId timelinepk;
    @ManyToOne( cascade=CascadeType.ALL )
    private Module module;
    @ManyToOne( cascade = CascadeType.ALL)
    private Course course;
    
}

但这给出了一个错误:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: No identifier specified for entity: com.scb.axess.playbook.model.Timeline
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1762) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]

【问题讨论】:

  • 我会建议你不要使用复合主键,与简单的合成主键相比,它会让以后的事情变得更加困难。在某些情况下复合键是合理的,但在我看来,这种情况不在其中。 BR
  • 您的timeline 实体中是否需要timeline_id?所以:主键应该是(timeline_id, module, course) 还是只是(module, course)
  • @ApoorvaOjha 看看休眠状态documentation。您可以在此处找到大量示例。

标签: spring-boot hibernate jpa spring-data-jpa


【解决方案1】:

有多种方法可以解决您的问题:

可能性一:使用IdClass

定义IdClass 类型

这个类必须实现Serializable接口和equals(..)hashCode()方法。该类包含复合主键的各个部分。

public class TimelineId implements Serializable  {

    private Integer timelineId;
    private Integer courseId;

    // getters & setters

    @Override
    public int hashCode() {
        // your impl of hashCode
    }

    @Override
    public boolean equals(Object obj) {
        // your impl of equals
    }

}

修改你的 Timeline

这里将@IdClass注解添加到实体类中。此外,该类包含与IdClass 类型相同的字段(名称和类型应相同),但使用@Id 进行注释。

@Entity
@IdClass(TimelineId.class)
public class Timeline {

    @Id
    @Column(name = "timeline_id")
    private Integer timelineId;
    @Id
    @Column(name = "course_id")
    private Integer courseId;
    @ManyToOne
    @JoinColumn(name = "module_id")
    private Module module;

    // getters & setters

}

可能性2:使用EmbeddedId

定义EmbeddedId 类型

这个类也包含复合主键的部分。

@Embeddable
public class TimelineId {

    @Column(name = "timeline_id")
    private Integer timelineId;
    @Column(name = "course_id")
    private Integer courseId;

    // getters & setters

}

修改你的 Timeline

在这种情况下,可以省略复合主键的单个部分。仅定义了带有 @EmbeddedId 注释的嵌入键类型的字段。

@Entity
public class Timeline {

    @EmbeddedId
    private TimelineId timelineId;
    @ManyToOne
    @JoinColumn(name = "module_id")
    private Module module;

    // getters & setters

}

在这两种情况下,对应的存储库都应该这样定义(TimelineId 必须用于参数类型ID)(这里使用JpaRepository):

public interface TimelineRepository extends JpaRepository<Timeline, TimelineId> {}
**可能性3:不要使用复合PK,而是使列唯一**

修改你的Timeline

@Entity
@Table(uniqueConstraints = {
    @UniqueConstraint(columnNames = {
        "course_id", "module_id"
    })
})
public class Timeline {

    @Id
    @Column(name = "timeline_id")
    Integer timeline_id;
    @ManyToOne( cascade=CascadeType.ALL)
    @JoinColumn(name = "module_id)
    private Module module;
    @ManyToOne( cascade = CascadeType.ALL)
    @JoinColumn(name = "course_id)
    private Course course;

    // getters & setters

}

【讨论】:

  • 嘿,感谢您抽出宝贵的时间。我希望以timeline_id 和 course_id 作为主键。上述解决方案是否适用?
  • 对于第三种可能性,timeline_id 可以相同且重复,因此不能单独作为主键。 :(
  • 我改变了答案。我混淆了主键部分(我假设键由course_idmodule_id 组成,而不是timeline_idcourse_id
猜你喜欢
  • 2017-04-29
  • 1970-01-01
  • 2015-08-14
  • 2021-12-04
  • 2014-02-19
  • 2013-06-01
  • 1970-01-01
  • 2011-03-20
  • 2012-10-10
相关资源
最近更新 更多