简体   繁体   English

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

[英]How to map compound primary key in JPA, where part of Primary Key is a Foreign Key

I'm trying to figure out how to build JPA Entity beans to get the data working for my devices. 我试图弄清楚如何构建JPA实体bean,以使数据适用于我的设备。 The database is old and in stone, so I can't change schema. 该数据库是旧的,一成不变的,所以我不能更改架构。 Device Models has a compound primary key, where one of the columns is a FK to Device Type. 设备模型具有复合主键,其中的一列是设备类型的FK。

I've tried a couple different things. 我尝试了几种不同的方法。 First was the Device had a DeviceModel and a DeviceType, but that gave me the error that too many things were referencing dev_type. 首先是设备具有DeviceModel和DeviceType,但是这给了我一个错误,那就是太多的东西在引用dev_type。 So then I tried to have DeviceModel have a reference to DeviceType but I ran into the same error. 因此,然后我尝试让DeviceModel引用DeviceType,但遇到了相同的错误。

If it helps/matters, I'm using Spring Data 4.2.x and Hibernate 4.3.8.Final to back everything. 如果有帮助/问题,我正在使用Spring Data 4.2.x和Hibernate 4.3.8.Final来备份所有内容。

Other answers I've found online (such as How to create and handle composite primary key in JPA ) don't help me as they only map to basic data types. 我在网上找到的其他答案(例如, 如何在JPA中创建和处理复合主键 )对我没有帮助,因为它们仅映射到基本数据类型。 In fact, the answer above is implemented in my code below...but I need to go 1 level further. 实际上,上面的答案是在下面的代码中实现的...但是我需要再上一级。

The Schema: 架构:

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)
);

My Beans so far (which don't tie DeviceType to either Device or DeviceModel, that's what I need help with): 到目前为止,我的Bean(它们不会将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
}

Well, the basic problem you're having is thinking in terms of columns instead of Entities, though that might be an unfair statement since the issue was kind of tricky. 嗯,您遇到的基本问题是根据列而不是实体进行思考,尽管由于问题有点棘手,所以这可能是不公平的陈述。 The basic question is how to do include an Entity as part of a Composite Key and I found the answer here: How to create a composite primary key which contains a @ManyToOne attribute as an @EmbeddedId in JPA? 基本问题是如何将实体作为复合键的一部分包含在内,我在这里找到了答案: 如何在JPA中创建包含@ManyToOne属性作为@EmbeddedId的复合主键? . Device: 设备:

@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;

DeviceModel: DeviceModel:

@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;

DeviceModelId: DeviceModelId:

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

Notice that I used @Embeddable and @EmbeddedId . 注意,我使用了@Embeddable@EmbeddedId It's just newer and I've read JPA provider comments that it is to be preferred over @IdClass . 它只是更新的,我已经阅读了JPA提供程序的评论,该评论优于@IdClass I think it also made the column naming a little easier, but I don't remember. 我认为这也使列命名更容易一些,但我不记得了。

DeviceType: 设备类型:

@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;

The trick was the @MapsId in the DeviceModel . 诀窍是DeviceModel@MapsId That enabled the usage of an Entity in the CompositeKey. 这样就可以在CompositeKey中使用实体。 The @JoinColumn on that field enabled the naming of that field. 该字段上的@JoinColumn启用了该字段的命名。 The only trick to using it is to create the DeviceTypeId manually: 使用它的唯一技巧是手动创建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);

This creates the following schema, which seems to match yours. 这将创建以下模式,该模式似乎与您的模式匹配。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM