java - 如何在 JPA 中映射复合主键,其中主键的一部分是外键

标签 java spring hibernate jpa

我正在尝试弄清楚如何构建 JPA 实体 bean 以使数据适用于我的设备。该数据库是旧的并且是石头,所以我无法更改架构。设备型号有一个复合主键,其中一列是设备类型的 FK。

我尝试了几种不同的方法。首先是设备有一个 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 绑定(bind)到 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
}

最佳答案

嗯,您遇到的基本问题是根据列而不是实体来思考,尽管这可能是一个不公平的说法,因为这个问题有点棘手。基本问题是如何将实体作为复合键的一部分包含在内,我在这里找到了答案: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 提供者的评论,认为它比 @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

关于java - 如何在 JPA 中映射复合主键,其中主键的一部分是外键,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36338527/

相关文章:

java - 是否可以使用 jpa 的 sql Developer 检索自动递增的字段

java - Envers 查询返回惰性结果而不是急切结果

Java Hibernate 标准平均值 : BigDecimal instead of Double

java - Jackson JsonDeserializer 总是得到空值

java - JMeter错误: jmeter.线程.JMeterThread : Test failed! java.lang.NoSuchFieldError:实例

java - 以异步方式实现长轮询

java - restcontroller 和 application/x-www-form-urlencoded;charset=UTF-8 媒体类型的问题

java - 当更新一个实体时, hibernate 尝试删除其他实体中的引用

java - 当我尝试使用 java 包 jdde 时 MATLAB 挂起,但只是在系统重新启动后第一次挂起

java - 如何关闭特定玩家的聊天?