【问题标题】:Flyway and PostgreSQL nullable definition of foreign key still generates a non-null constraintFlyway 和 PostgreSQL 可空定义的外键仍然生成非空约束
【发布时间】:2021-05-14 01:24:48
【问题描述】:

我在这个初始化脚本中使用 Spring Boot 和 Flyway:

CREATE TABLE ADDRESS(
    ID bigserial NOT NULL PRIMARY KEY
                    );

CREATE TABLE ROLE(
    ID bigserial NOT NULL PRIMARY KEY
);

CREATE TABLE PERSON(
    ID bigserial NOT NULL PRIMARY KEY,
    FIRST_NAME VARCHAR(255),
    LAST_NAME VARCHAR(255),
    ADDRESS bigserial NOT NULL REFERENCES ADDRESS (ID),
    ROLE bigserial REFERENCES ROLE (ID)                  -- notice here is no 'not null'
);

所有表之间的关系是:

  • 每个PERSON 都有0-1 ROLE。所以,每个ROLE 都属于0-n PERSON。因此,这种关系是可以为空的。
  • 每个PERSON 都有1 ADDRESS。所以,每个ADDRESS 都属于1-n PERSON。因此,这种关系不为空。

一旦我启动应用程序(我也尝试将查询直接发布到 PostgreSQL 数据库架构),PERSONROLE 表之间会以某种方式生成约束 not-null

使用 DataGrip,我选择 SQL Scripts -> Generate DDL to Query Console 并获取表的 DDL(见下文,为了缘故省略了新行和角色定义简洁)。

令我惊讶的是,NOT NULL 就在那里,尽管我没有定义这样的约束。除了改变表,如何摆脱它?

create table if not exists address
(
    id bigserial not null
        constraint address_pkey primary key
);

create table if not exists role
(
    id bigserial not nullconstraint role_pkey primary key
);

create table if not exists person
(
    id bigserial not null 
        constraint person_pkey primary key,
    first_name varchar(255),
    last_name varchar(255),
    address bigserial not null 
        constraint person_address_fkey references address,
    role bigserial not null                                   -- why is 'not null' here?
        constraint person_role_fkey references role
);

我使用的 PostgreSQL 版本(通过SELECT version())是:

PostgreSQL 10.13, compiled by Visual C++ build 1800, 64-bit

【问题讨论】:

标签: java spring postgresql spring-boot flyway


【解决方案1】:

"8.1.4. Serial Types":

数据类型smallserialserialbigserial不正确 类型,但仅仅是为了创建独特的符号方便 标识符列(类似于支持的 AUTO_INCREMENT 属性 由其他一些数据库)。在当前实现中,指定:

CREATE TABLE tablename (
    colname SERIAL
);

相当于指定:

CREATE SEQUENCE tablename_colname_seq AS integer;
CREATE TABLE tablename (
    colname integer NOT NULL DEFAULT nextval('tablename_colname_seq')
);
ALTER SEQUENCE tablename_colname_seq OWNED BY tablename.colname;

注意NOT NULL

不要将bigserial 用作foreign 键。这没有多大意义。只需使用bigint

CREATE TABLE IF NOT EXISTS person
                           (...
                            role bigint REFERENCES role);

【讨论】:

  • 这在使用(推荐的)identity 而不是serial 时更加明显
  • 我是否正确理解 bigserial 对主键定义有好处,只要它指定自动增量,bigint 适用于引用此类键?
  • @NikolasCharalambidis: bigint 是这里的底层实际数据类型。使用bigserial 也是bigint,它只是添加了一些巫术来使用序列自动为列创建唯一值,例如一些可以扩展的宏。对于方便的主键。但是自动为外键创建值没有多大意义。他们只是或多或少地随机引用一条记录(至少可能不是您想要的),或者偶尔会生成引用表中不存在的值,从而引发错误。这不太方便。
【解决方案2】:

可能的解决方案 1: 将 Biserial 更改为 Bigint 在 springboot 中运行 flyway 以写入 postgres 数据库时不会删除设置为外键列的空约束(至少对我而言)

  • postgres:11.3-alphine 3.4
  • 飞行路线:8.0.5

为了安全起见,需要添加脚本以将列更改为可为空

ALTER TABLE mytable ALTER COLUMN mycolumn DROP NOT NULL;

Change Postgres column to nullable

可能的解决方案 2: 当 Spring boot 将 JPA Hibernate ddl 配置设置为 createcreate-dropupdate 时,JPA 实体属性将更新 flyway DB 迁移脚本。 JPA 实体可以添加 NOT NULL 约束。

将 JPA Hibernate ddl 配置更改为 nonevalidate 将确保仅使用 flyway 脚本来创建模式。 JPA Hibernate ddl configuration

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-28
    • 1970-01-01
    • 1970-01-01
    • 2017-04-27
    • 1970-01-01
    • 2014-01-17
    相关资源
    最近更新 更多