如何使用JPA和休眠映射复合键?

2022-08-31 05:45:24

在此代码中,如何为复合键生成 Java 类(如何在休眠状态下生成复合键):

create table Time (
     levelStation int(15) not null,
     src varchar(100) not null,
     dst varchar(100) not null,
     distance int(15) not null,
     price int(15) not null,
     confPathID int(15) not null,
     constraint ConfPath_fk foreign key(confPathID) references ConfPath(confPathID),
     primary key (levelStation, confPathID)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

答案 1

要映射复合键,可以使用 注释 注释。我知道这个问题不是严格意义上的JPA,但规范定义的规则也适用。所以这里他们是:EmbeddedIdIdClass

2.1.4 主键和实体标识

...

复合主键必须对应于单个持久性字段或属性,或者对应于一组此类字段或属性,如下所述。必须定义主键类来表示复合主键。当从旧数据库映射时,当数据库键由多个列组成时,通常会出现复合主键。EmbeddedIdIdClass 批注用于表示复合主键。请参阅第 9.1.14 和 9.1.15 节。

...

以下规则适用于复合主键:

  • 主键类必须是公共的,并且必须具有公共的无参数构造函数。
  • 如果使用基于属性的访问,则主键类的属性必须是公共的或受保护的。
  • 主键类必须是可序列化的
  • 主键类必须定义等于哈希代码方法。这些方法的值相等的语义必须与键映射到的数据库类型的数据库相等性一致。
  • 复合主键必须表示并映射为可嵌入类(请参见第 9.1.14 节 “EmbeddedId Annotation”),或者必须表示并映射到实体类的多个字段或属性(请参见第 9.1.15 节 “IdClass Annotation”)。
  • 如果复合主键类映射到实体类的多个字段或属性,则主键类中的主键字段或属性的名称必须与实体类的名称相对应,并且其类型必须相同。

使用IdClass

复合主键的类可能如下所示(可以是静态内部类):

public class TimePK implements Serializable {
    protected Integer levelStation;
    protected Integer confPathID;

    public TimePK() {}

    public TimePK(Integer levelStation, Integer confPathID) {
        this.levelStation = levelStation;
        this.confPathID = confPathID;
    }
    // equals, hashCode
}

实体:

@Entity
@IdClass(TimePK.class)
class Time implements Serializable {
    @Id
    private Integer levelStation;
    @Id
    private Integer confPathID;

    private String src;
    private String dst;
    private Integer distance;
    private Integer price;

    // getters, setters
}

注释将多个字段映射到表 PK。IdClass

EmbeddedId

复合主键的类可能如下所示(可以是静态内部类):

@Embeddable
public class TimePK implements Serializable {
    protected Integer levelStation;
    protected Integer confPathID;

    public TimePK() {}

    public TimePK(Integer levelStation, Integer confPathID) {
        this.levelStation = levelStation;
        this.confPathID = confPathID;
    }
    // equals, hashCode
}

实体:

@Entity
class Time implements Serializable {
    @EmbeddedId
    private TimePK timePK;

    private String src;
    private String dst;
    private Integer distance;
    private Integer price;

    //...
}

注释将 PK 类映射到表 PK。@EmbeddedId

差异:

  • 从物理模型的角度来看,没有区别
  • @EmbeddedId以某种方式更清楚地传达了密钥是复合密钥,当组合的pk本身是一个有意义的实体或在代码中重用时,IMO是有意义的。
  • @IdClass 用于指定某些字段组合是唯一的,但这些字段没有特殊含义

它们还会影响您编写查询的方式(使它们或多或少冗长):

  • IdClass

    select t.levelStation from Time t
    
  • EmbeddedId

    select t.timePK.levelStation from Time t
    

引用

  • JPA 1.0 规范
    • 第 2.1.4 节 “主键和实体标识”
    • 第 9.1.14 节 “嵌入式注释”
    • 第 9.1.15 节 “IdClass Annotation”

答案 2

您需要使用@EmbeddedId

@Entity
class Time {
    @EmbeddedId
    TimeId id;

    String src;
    String dst;
    Integer distance;
    Integer price;
}

@Embeddable
class TimeId implements Serializable {
    Integer levelStation;
    Integer confPathID;
}

推荐