如何使用 Hibernate 和 JPA 定义外键映射的字段顺序?

How to define the order of fields on foreign key mapping using Hibernate and JPA?

我在 hibernate 部署我的应用程序时遇到了一些问题。

我在日志中看到一些错误,说 hibernate 无法更新 tables 因为发现外键违规,如下所示:

Caused by: org.postgresql.util.PSQLException: ERROR: insert or update on table "order" violates foreign key constraint "fk1w50d63tw42cwgrb7pjn11crs"
  Detail: Key (id_office_o, id_period)=(2, 1) is not present in table "office".

但在我的 table 定义中,我将 FK fk_order_office_o 作为 (id_period, id_office_o) 关系。

这是我的 table 定义。

CREATE TABLE period (
    id_period INTEGER PRIMARY KEY NOT NULL,
    initial_date TIMESTAMP,
    final_date TIMESTAMP
);

CREATE TABLE office (
    id_period INTEGER NOT NULL,
    id_office INTEGER NOT NULL,
    large_name VARCHAR(50) NOT NULL,
    code_name VARCHAR(2) NOT NULL,
    CONSTRAINT pk_office PRIMARY KEY (id_periodo, id_office),
    CONSTRAINT fk_office_period FOREIGN KEY (id_period) REFERENCES period (id_period)
);
CREATE UNIQUE INDEX uk_office_id_office ON office (id_office);

CREATE TABLE order (
    id_order INTEGER PRIMARY KEY NOT NULL,
    id_period INTEGER,
    id_office_o INTEGER,
    id_office_d INTEGER,
    status INTEGER,
    CONSTRAINT fk_order_office_o 
        FOREIGN KEY (id_period, id_office_o) 
        REFERENCES office (id_period, id_office) 
        MATCH SIMPLE ON UPDATE CASCADE ON DELETE RESTRICT,
    CONSTRAINT fk_order_office_d
        FOREIGN KEY (id_period, id_office_d) 
        REFERENCES office (id_period, id_office),
        MATCH SIMPLE ON UPDATE CASCADE ON DELETE RESTRICT,
    CONSTRAINT fk_order_period
        FOREIGN KEY (id_period)
        REFERENCES period (id_period)
        MATCH SIMPLE ON UPDATE CASCADE ON DELETE RESTRICT
);

这些是我 table 上的数据。

Period.
+------------+--------------+------------+
| id_period  | initial_date | final_date |
|------------|--------------|------------+
| 1          | 2016-01-01   | 2017-12-31 |
+------------+--------------+------------+

Office.
+------------+-----------+------------+-----------+
| id_period  | id_office | large_name | code_name |
|------------|-----------|------------|-----------|
| 1          | 1         | Sales      | SL        |
| 1          | 2         | Billing    | BL        |
| 1          | 3         | Shipments  | SH        |
| 1          | 4         | Returns    | RT        |
+------------+-----------+------------+-----------+

Order.
+------------+-----------+--------------+--------------+-----------+
| id_order   | id_period | id_office_o  | id_office_d  | status    |
+------------+-----------+--------------+--------------+-----------+
| 1          | 1         | 2            | 3            | 1         |
| 2          | 1         | 2            | 3            | 1         |
| 1          | 1         | 3            | 4            | 1         |
| 1          | 1         | 1            | 2            | 1         |
+------------+-----------+--------------+--------------+-----------+

如您所见,与订单 table 相关的 office table 的 FK 有一个名为 fk_order_office_o 的约束,使用 id_periodid_office,就是这样。

实体映射在这里:

@Embeddable
public class OfficePK implements Serializable {

    @Column(name = "id_period")
    private Integer idPeriod;

    @Column(name="id_office")
    private Integer idOffice;

    public OfficePK() {
    }

    public OfficePK(Integer idPeriod, Integer idOffice) {
        this.idPeriod = idPeriod;
        this.idOffice = idOffice;
    }

    ... 

}


@Entity
@Table(schema="enterprise", name="office")
public class Office {


    @EmbeddedId
    private OfficePK idOffice;

    private String largeName;
    private String codeName;

    public Office() {
    }

    public OfficePK getIdOffice() {
        return this.idOffice;
    }

    public void setIdOffice(OfficePK idOffice) {
        this.idOffice = idOffice;
    }

    ...

}


@Entity
@Table(schema="operation", name = "order")
public class Order {

    @Column(name="id_order")
    private Integer idOrder;

    @ManyToOne
    @JoinColumns(
        value = {
            @JoinColumn(name="id_period", 
                        referencedColumnName = "id_period", 
                        nullable = false, insertable = false, 
                        updatable = false),
            @JoinColumn(name="id_office_o",  
                        referencedColumnName = "id_office",
                        nullable = false, insertable = false, 
                        updatable = false)
        },
        foreignKey = @ForeignKey(name = "fk_order_office_o")
    )
    private Office officeO;

    @ManyToOne
    @JoinColumns(
        value = {
            @JoinColumn(name="id_period", 
                        referencedColumnName = "id_period", 
                        nullable = false, insertable = false, 
                        updatable = false),
            @JoinColumn(name="id_office_d",  
                        referencedColumnName = "id_office",
                        nullable = false, insertable = false, 
                        updatable = false)
        },
        foreignKey = @ForeignKey(name = "fk_order_office_d")
    )
    private Office officeD;

    public Order() {
    }

    ...

}

部署应用程序时,我看到了一些引起我注意的错误:

  1. 我没有按照日志文件中显示的顺序定义外键,日志文件正在查找 (id_office_o, id_period) 对,但外键在table 并在映射中。
  2. 我正在设置外键的名称,但是 hibernate 正在设置一个随机名称,例如 fk1w50d63tw42cwgrb7pjn11crs

如何定义实体外键映射字段的顺序?可能吗?..或者应该如何定义或映射外键字段?

我正在使用

  1. 休眠 5.2.6.Final
  2. Spring-JPA 1.10.6.RELEASE

谢谢大家

@ForeignKey 名称相关的部分可能与 HHH-11180 相关。

关于外键顺序的部分,你可以通过提供一个自定义的org.hibernate.boot.model.naming.ImplicitNamingStrategy实现来解决它,它从默认的org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl扩展并覆盖determineForeignKeyName方法,以便它按照您需要的顺序生成包含列的外键。

现在,您正在经历的所有这些努力都让人觉得您没有正确管理架构。你不应该在生产中使用自动 HBM2DDL 模式生成。 You should use Flyway instead。这是您环境中的根本问题。