在一个表中形成复合键的列实际上在另一个表中作为外键写入实体类

Posted

技术标签:

【中文标题】在一个表中形成复合键的列实际上在另一个表中作为外键写入实体类【英文标题】:Columns forming composite key in one table are actually in other table as foreign key-write entity class 【发布时间】:2018-11-19 18:35:54 【问题描述】:

行业代码表

Industry_Code(Primary key) | Industry_name
1| Reliance
2| TaTa

技术代码表

Technology_code(Primary Key) | technology name
81| java
81|cpp

carrier_codes 表

Industry_Code(Primary key)(Foreign Key to Industry_Codes table) | technology_code(pk)(Foreign key to Technology_Code table) | other fields

1 | 81 |
2| 81|
1| 82

注册

Mobile Number(Pk)|Industry_code(Fk to carrier_codes) |Technology_Code(Fk to carrier_codes)

12345|1|83
78913|1|88

对于给定的表结构,实体类写成-

@Entity
@Table(name = "Industry_Codes")
public class IndustryCodes implements Serializable 
    private static final long serialVersionUID = 1L;

    @Id
    @Column(name="TELECOM_INDUSTRY_CODE")
    private String telecomIndustryCode;

    @Column(name="INdustry_Name")
    private String Industry_Name;


表 2

@Entity
@Table(name="Technology_Codes")
public class TechnologyCodes 

    private static final long serialVersionUID = 1L;

    @Id
    @Column(name = "Technology_code")
    private int targetTechnologyCode;

    @Column(name="Technology_Name")
    private String TechnologyName;

@Entity
@Table(name = "CARRIER_CODES")
public class CarrierCodes 
    private static final long serialVersionUID = 1L;

    @Id
    @ManyToOne
    @JoinColumn(name ="CARRIER_CODE",referencedColumnName="INDUSTRY_CODE")
    private IndustryCodes carrierCode;

    @Id
    @ManyToOne
    @JoinColumn(name ="TECHNOLOGY_CODE",referencedColumnName="TECHNOLOGY_CODE")
    private TechnologyCodes TechnologyCode;


现在我完全不知道如何为 Register 表编写实体,该表具有像 carrier_code 和 technology_code 这样的列,这两者都是 carrier_codes 表的外键,它们一起形成了 carrier_codes 表的复合键,而且两者都出现了很多两个表中的字段,因此两个表的两个列之间存在多对多关联。

感谢您对编写寄存器实体的任何帮助。我对 JPA 中的协会完全陌生。

【问题讨论】:

【参考方案1】:

首先,确保您了解这些关系。 UML 和图像是你的朋友。

请注意,IndustryCodeTechCode 是多对多关系。在 DDL 中,多对多关系是通过连接表和复合键完成的。在 JPA 中,这通常只是使用 ManyToMany 注释映射,但由于您还想将其用作 Registry 表的外键,因此您必须自己定义实体。这是通过CarrierCodes 完成的。在 JPA 中,EmbeddableId 通常是为将以这种方式使用的实体创建复合键的最简单方法。所以 JPA 可以这样完成:

@Entity
public class IndustryCode 
    @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;
    private String industryName;

    @OneToMany(mappedBy="industryCode")
    private Set<CarrierCodes> industryCodes;

@Entity
public class TechCode 
    @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;
    private String techName;

    @OneToMany(mappedBy="techCode")
    private Set<CarrierCodes> techCodes;

@Entity
public class CarrierCodes 
    @EmbeddedId
    private CarrierCodesId id = new CarrierCodesId(); 

    @ManyToOne
    @MapsId("techCodeId")
    private TechCode techCode;

    @ManyToOne
    @MapsId("industryCodeId")
    private IndustryCode industryCode;

@SuppressWarnings("serial")
@Embeddable
public class CarrierCodesId implements Serializable 
    private Long industryCodeId;
    private Long techCodeId;

@Entity
public class Register 
    @Id
    private Long mobileNumber;
    @ManyToOne
    // optional but nice to have consistent names
    @JoinColumns(
        @JoinColumn(name="industryCode_id", referencedColumnName="industryCode_id"),
        @JoinColumn(name="techCode_id", referencedColumnName="techCode_id")
        )
    private CarrierCodes carrierCodes;

并且使用它类似于您的示例是这样的。

tx.begin();
IndustryCode ic1 = new IndustryCode("Reliance");
IndustryCode ic2 = new IndustryCode("TaTa");
TechCode tc1 = new TechCode("java");
TechCode tc2 = new TechCode("cpp");
CarrierCodes cc1 = new CarrierCodes(tc1, ic1);
CarrierCodes cc2 = new CarrierCodes(tc1, ic2);
CarrierCodes cc3 = new CarrierCodes(tc2, ic1);
Register r1 = new Register(12345L, cc1);
Register r2 = new Register(78913L, cc2);

em.persist(ic1);
em.persist(ic2);
em.persist(tc1);
em.persist(tc2);
em.persist(cc1);
em.persist(cc2);
em.persist(cc3);
em.persist(r1);
em.persist(r2);
tx.commit();

em.clear();
List<Register> rs = em.createQuery("select r from Register r left outer join fetch r.carrierCodes cc where cc.techCode.techName = 'java'", Register.class).getResultList();
rs.stream().forEach(r->System.out.println(r.getMobileNumber() + " " + r.getCarrierCodes().getTechCode().getTechName()));

List<Register> rs2 = em.createQuery("select r from Register r left outer join fetch r.carrierCodes cc where cc.industryCode.industryName = 'TaTa'", Register.class).getResultList();
rs2.stream().forEach(r->System.out.println(r.getMobileNumber() + " " + r.getCarrierCodes().getIndustryCode().getIndustryName()));

这给了我以下输出。

Hibernate: create table CarrierCodes (industryCode_id bigint not null, techCode_id bigint not null, primary key (industryCode_id, techCode_id))
Hibernate: create table IndustryCode (id bigint generated by default as identity (start with 1), industryName varchar(255), primary key (id))
Hibernate: create table Register (mobileNumber bigint not null, industryCode_id bigint, techCode_id bigint, primary key (mobileNumber))
Hibernate: create table TechCode (id bigint generated by default as identity (start with 1), techName varchar(255), primary key (id))
Hibernate: alter table CarrierCodes add constraint FKfq42ix66txvd15crq2pey3dcp foreign key (industryCode_id) references IndustryCode
Hibernate: alter table CarrierCodes add constraint FK9os97pd53ijerp2mibllknovn foreign key (techCode_id) references TechCode
Hibernate: alter table Register add constraint FK2k626ouo1ajsccqlpb5y3xa8u foreign key (industryCode_id, techCode_id) references CarrierCodes
Hibernate: insert into IndustryCode (id, industryName) values (default, ?)
Hibernate: insert into IndustryCode (id, industryName) values (default, ?)
Hibernate: insert into TechCode (id, techName) values (default, ?)
Hibernate: insert into TechCode (id, techName) values (default, ?)
Hibernate: insert into CarrierCodes (industryCode_id, techCode_id) values (?, ?)
Hibernate: insert into CarrierCodes (industryCode_id, techCode_id) values (?, ?)
Hibernate: insert into CarrierCodes (industryCode_id, techCode_id) values (?, ?)
Hibernate: insert into Register (industryCode_id, techCode_id, mobileNumber) values (?, ?, ?)
Hibernate: insert into Register (industryCode_id, techCode_id, mobileNumber) values (?, ?, ?)
Hibernate: select register0_.mobileNumber as mobileNu1_2_0_, carriercod1_.industryCode_id as industry1_0_1_, carriercod1_.techCode_id as techCode2_0_1_, register0_.industryCode_id as industry2_2_0_, register0_.techCode_id as techCode3_2_0_ from Register register0_ left outer join CarrierCodes carriercod1_ on register0_.industryCode_id=carriercod1_.industryCode_id and register0_.techCode_id=carriercod1_.techCode_id cross join TechCode techcode2_ where carriercod1_.techCode_id=techcode2_.id and techcode2_.techName='java'
Hibernate: select industryco0_.id as id1_1_0_, industryco0_.industryName as industry2_1_0_ from IndustryCode industryco0_ where industryco0_.id=?
Hibernate: select techcode0_.id as id1_3_0_, techcode0_.techName as techName2_3_0_ from TechCode techcode0_ where techcode0_.id=?
Hibernate: select industryco0_.id as id1_1_0_, industryco0_.industryName as industry2_1_0_ from IndustryCode industryco0_ where industryco0_.id=?
12345 java
78913 java
Hibernate: select register0_.mobileNumber as mobileNu1_2_0_, carriercod1_.industryCode_id as industry1_0_1_, carriercod1_.techCode_id as techCode2_0_1_, register0_.industryCode_id as industry2_2_0_, register0_.techCode_id as techCode3_2_0_ from Register register0_ left outer join CarrierCodes carriercod1_ on register0_.industryCode_id=carriercod1_.industryCode_id and register0_.techCode_id=carriercod1_.techCode_id cross join IndustryCode industryco2_ where carriercod1_.industryCode_id=industryco2_.id and industryco2_.industryName='TaTa'
78913 TaTa

【讨论】:

以上是关于在一个表中形成复合键的列实际上在另一个表中作为外键写入实体类的主要内容,如果未能解决你的问题,请参考以下文章

替换其他表中作为外键的主键

mysql复合外键引用超过2个属性

将包含外键的列添加到 Laravel 生产中的现有表中

一个表中的主键是不是也可以作为主键出现在另一个表中?

mysql外键仅引用复合主键的一部分

Laravel 与复合外键的关系