【问题标题】:How to map compound primary key in JPA, where part of Primary Key is a Foreign Key如何在 JPA 中映射复合主键,其中主键的一部分是外键
【发布时间】:2016-07-20 05:01:36
【问题描述】:

我试图弄清楚如何构建 JPA 实体 bean 以使数据适用于我的设备。数据库很旧而且一成不变,所以我无法更改架构。 Device Models 有一个复合主键,其中一列是 Device Type 的 FK。

我尝试了几种不同的方法。首先是 Device 有一个 DeviceModel 和一个 DeviceType,但这给了我一个错误,即有太多东西引用了 dev_type。于是我尝试让 DeviceModel 引用 DeviceType 但我遇到了同样的错误。

如果有帮助/重要,我将使用 Spring Data 4.2.x 和 Hibernate 4.3.8.Final 来支持一切。

我在网上找到的其他答案(例如How to create and handle composite primary key in JPA)对我没有帮助,因为它们只映射到基本数据类型。其实上面的答案是在我下面的代码中实现的……不过我还需要再上一层。

架构:

create table devices
(
  device_nbr serial(1),
  device_id nchar(20) not null unique,
  dev_type integer not null,
  model_nbr integer default 1,
  unit_addr nchar(32),
  primary key (device_nbr),
  foreign key (dev_type) references devtypes (dev_type),
  foreign key (dev_type, model_nbr) references devmodels (dev_type, model_nbr)
);

create table devmodels
(
  dev_type      integer  not null,
  model_nbr     integer  not null,
  model_desc    nchar(20),
  primary key (dev_type, model_nbr),
  foreign key (dev_type) references devtypes (dev_type)
);

create table devtypes
(
  dev_type integer not null,
  dev_desc nchar(16) not null unique,
  primary key (dev_type)
);

到目前为止我的 Beans(它没有将 DeviceType 与 Device 或 DeviceModel 联系起来,这就是我需要帮助的地方):

@Entity
@Table(name = "devices")
public class Device
{
    @Id
    @GeneratedValue
    @Column(name = "device_nbr")
    private Long                number;

    @Column(name = "device_id", length = 30)
    private String          id;

    @Column(name = "unit_addr", length = 30)
    private String          unitAddress;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumns({
        @JoinColumn(name = "dev_type"),
        @JoinColumn(name = "model_nbr")
    })
    private DeviceModel deviceModel;

...Getters and setters
}

public class DeviceModelPK implements Serializable
{
    private static final long   serialVersionUID    = -8173857210615808268L;
    protected Integer                   deviceTypeNumber;
    protected Integer                   modelNumber;

...Getters and setters
}

@Entity
@Table(name = "devmodels")
@IdClass(DeviceModelPK.class)
public class DeviceModel
{
    @Id
    @Column(name = "dev_type")
    private Integer         deviceTypeNumber;

    @Id
    @Column(name = "model_nbr")
    private Integer         modelNumber;

    @Column(name = "model_desc")
    private String          description;

...Getters and setters
}

@Entity
@Table(name = "devtypes")
public class DeviceType
{
    @Id
    @GeneratedValue
    @Column(name = "dev_type")
    private Integer number;

    @Column(name = "dev_desc", length = 30)
    private String  description;

...Getters and setters
}

【问题讨论】:

  • 为什么不试试@EmbeddedId 而不是@IdClassas they only map to basic data types你找到相关的参考了吗?
  • 虽然我还没有尝试过使用@EmbeddedId,但我看不出它将如何解决引用同一列的 2 个不同 java 字段的问题。
  • 我没有发现任何问题。你有一个设备类型,它有自己的 id。然后你有属于类型的模型,每个模型都有一个数字和类型 id,它是一个外键,两列都构成复合主键。即使将 DeviceModelPK 中的两个字段都设为 int,您也不应该有任何问题。

标签: java spring hibernate jpa


【解决方案1】:

嗯,您遇到的基本问题是考虑列而不是实体,尽管这可能是一个不公平的说法,因为这个问题有点棘手。基本问题是如何将实体包含为复合键的一部分,我在这里找到了答案:How to create a composite primary key which contains a @ManyToOne attribute as an @EmbeddedId in JPA?。 设备:

@Entity
@Table(name = "devices")
public class Device
{
    @Id
    @Column(name = "device_nbr")
    private Long number;

    @Column(name = "device_id", length = 20)
    private String deviceId;

    @ManyToOne(fetch=FetchType.EAGER, cascade=CascadeType.ALL)
    @JoinColumns({@JoinColumn(name="dev_type", referencedColumnName="dev_type"), @JoinColumn(name="model_nbr", referencedColumnName="model_nbr")})
    private DeviceModel deviceModel;

    // This creates a foreign key constraint, but otherwise doesn't function
    // deviceType must be accessed through deviceModel
    // note, it can be used for explicit selects, e.g., "select d.deviceType from Device d"
    @OneToOne(fetch=FetchType.EAGER, cascade=CascadeType.ALL)
    @JoinColumn(name="dev_type", referencedColumnName="dev_type", insertable=false, updatable=false)
    private DeviceType deviceType;

    @Column(name = "unit_addr", length = 32)
    private String unitAddress;

设备型号:

@Entity
@Table(name = "devmodels")
public class DeviceModel
{
    @EmbeddedId
    private DeviceModelId id;

    @ManyToOne(fetch=FetchType.EAGER, cascade=CascadeType.ALL)
    @JoinColumn(name="dev_type")
    @MapsId("deviceType")
    private DeviceType deviceType;

    @Column(name = "model_desc", length=20)
    private String  description;

设备型号ID:

@Embeddable
public class DeviceModelId implements Serializable
{
    private static final long   serialVersionUID    = -8173857210615808268L;
    private Integer deviceType;
    @Column(name="model_nbr")
    private Integer modelNumber;

请注意,我使用了@Embeddable@EmbeddedId。它只是更新,我已经阅读了 JPA 提供程序 cmets,它比 @IdClass 更受欢迎。我认为这也使列命名更容易一些,但我不记得了。

设备类型:

@Entity
@Table(name = "devtypes")
public class DeviceType
{
    @Id
    @GeneratedValue
    @Column(name = "dev_type")
    private Integer deviceType;

    @Column(name = "dev_desc", length = 16)
    private String  description;

诀窍是DeviceModel 中的@MapsId。这样就可以在 CompositeKey 中使用实体。该字段上的 @JoinColumn 启用了该字段的命名。使用它的唯一技巧是手动创建 DeviceTypeId:

DeviceModel model = new DeviceModel();
DeviceModelId modelId = new DeviceModelId();
modelId.setModelNumber(654321);
// have to have a DeviceType to create a DeviceModel
model.setDeviceType(type);
model.setId(modelId);

这将创建以下架构,这似乎与您的匹配。

create table devices (device_nbr bigint not null, device_id varchar(20), unit_addr varchar(32), dev_type integer, model_nbr integer, primary key (device_nbr))
create table devmodels (dev_type integer not null, model_nbr integer not null, model_desc varchar(20), primary key (dev_type, model_nbr))
create table devtypes (dev_type integer not null, dev_desc varchar(16), primary key (dev_type))
alter table devices add constraint FK8q0a886v04gg0qv261x1b2qrf foreign key (dev_type, model_nbr) references devmodels
alter table devices add constraint FKb72a7hq5phwjtbhaglobdkgji foreign key (dev_type) references devtypes
alter table devmodels add constraint FK4xlwyd2gwpbs4g4hdckyb11oj foreign key (dev_type) references devtypes

【讨论】:

  • 这看起来很有希望。我明天试试看!
  • 工作就像一个魅力。互联网太聪明了!谢谢。
猜你喜欢
  • 2019-04-22
  • 2017-08-06
  • 1970-01-01
  • 2015-02-03
  • 2017-01-30
  • 2020-10-04
  • 1970-01-01
  • 2016-09-18
  • 1970-01-01
相关资源
最近更新 更多