繁体   English   中英

使用jOOQ从值()中选择插入并使用正确的类型转换

[英]select into insert from values() with correct type casts using jOOQ

我正在使用jOOQ将很多行插入到一个多对多关系的表中。 代码工作,生成的SQL是预期的,我的问题是,我希望jOOQ代码可以更简单。

我有一个简化的结构(所有重命名,删除大多数字段,删除大多数约束,它只是一个愚蠢,但准确的结构示例):

CREATE TABLE person (
    person_id BIGSERIAL PRIMARY KEY,
    person_name VARCHAR(64) NOT NULL UNIQUE
);

CREATE TABLE company (
    company_id BIGSERIAL PRIMARY KEY,
    company_name VARCHAR(100) NOT NULL UNIQUE
);

CREATE TABLE employment_contract (
    company_id BIGINT NOT NULL REFERENCES company,
    person_id BIGINT NOT NULL REFERENCES person,
    PRIMARY KEY (company_id, person_id),

    salary INT NOT NULL,
    creation_date_time TIMESTAMP NOT NULL
);

我的插入代码:

Table<Record4<String, String, Integer, Timestamp>> insertValues = values(
    row(
        cast(null, COMPANY.COMPANY_NAME),
        cast(null, PERSON.PERSON_NAME),
        cast(null, EMPLOYMENT_CONTRACT.SALARY),
        cast(null, EMPLOYMENT_CONTRACT.CREATION_DATE_TIME)
    )
).as("insert_values",
        COMPANY.COMPANY_NAME.getName(),  -- these lines are bugging me
        PERSON.PERSON_NAME.getName(),
        EMPLOYMENT_CONTRACT.SALARY.getName(),
        EMPLOYMENT_CONTRACT.CREATION_DATE_TIME.getName()
);

Insert<AffectedSubscriberRecord> insert = insertInto(EMPLOYMENT_CONTRACT)
    .columns(EMPLOYMENT_CONTRACT.COMPANY_ID,
            EMPLOYMENT_CONTRACT.PERSON_ID,
            EMPLOYMENT_CONTRACT.SALARY,
            EMPLOYMENT_CONTRACT.CREATION_DATE_TIME
    )
    .select(
        select(
            COMPANY.COMPANY_ID,
            PERSON.PERSON_ID,
            insertValues.field(EMPLOYMENT_CONTRACT.SALARY),
            insertValues.field(EMPLOYMENT_CONTRACT.CREATION_DATE_TIME)
        )
        .from(insertValues)
            .join(COMPANY).using(COMPANY.COMPANY_NAME)
            .join(PERSON).using(PERSON.PERSON_NAME)
    );

然后我将所有行绑定到context.batch(insert)并执行该操作。 我知道肯定被引用键person以及company已经存在,原来的代码也解决了重复,我们并不需要在这里关心这些事情。

我错误的是insertValues表 - 我需要在容易出错的复制粘贴中指定列类型和名称两次,使用.getName()调用,这些调用会掩盖整个代码并且容易错误地交换。 我尝试了什么:

Table<Record4<String, String, Integer, Timestamp>> insertValues = values(
    row( (String)null, (String)null, (Integer)null, (Timestamp)null )
).as("insert_values",
        COMPANY.COMPANY_NAME.getName(),
        PERSON.PERSON_NAME.getName(),
        EMPLOYMENT_CONTRACT.SALARY.getName(),
        EMPLOYMENT_CONTRACT.CREATION_DATE_TIME.getName()
);

这显然不起作用,jOOQ和Postgres都不知道插入的类型,DB猜测varchar并且失败。 我们需要jOOQ至少为查询的第一行生成类型转换。 另一个尝试:

Table<Record4<String, String, Integer, Timestamp>> insertValues = values(
    row( COMPANY.COMPANY_NAME, PERSON.PERSON_NAME, EMPLOYMENT_CONTRACT.SALARY, EMPLOYMENT_CONTRACT.CREATION_DATE_TIME )
).as("insert_values");

这将是炸弹。 JOOQ知道这种方式正确的类型,并可以为我生成演员表,所有代码重复消失,事情是安全的。 但是,这也失败了。 JOOQ不明白我给它充满了空行。

有没有办法在没有不干净的.getName()调用的情况下实现相同(或等效)的结果查询,直接在某处传递字段?

所以jOOQ中的values()为你提供了一个表构造函数。 默认情况下, select()会按原样为您提供所有列,这意味着jOOQ将定义为Field<object>

我认为考虑这个问题的方法是,jOOQ在这一点上要认识到两件事。 您可以尝试在输出中定义字段,也可以接受对象并尝试在返回途中处理类型情况。 我的偏好是输出中的字段。

这里要考虑的一个重要方面是行必须具有相同类型的给定列,但有些情况下,SQL中的隐式转换可能与jOOQ中的那些混乱。 例如,如果在第一行中插入数值而在第二行中插入整数,则整数将隐式地转换为数字。 为选择定义字段可以避免此问题。 因此,就问题而言,我将单独定义字段,特别是在值选择部分中定义它们。 那应该可以解决这个问题。 使用VALUES() ,您已经独立,jOOQ无法安全地推断类型。

因此,您的代码应该变为:

Table<Record4<String, String, Integer, Timestamp>> insertValues = values(
    row(
        cast(null, COMPANY.COMPANY_NAME),
        cast(null, PERSON.PERSON_NAME),
        cast(null, EMPLOYMENT_CONTRACT.SALARY),
        cast(null, EMPLOYMENT_CONTRACT.CREATION_DATE_TIME)
    )
).as("insert_values", "company_name", "person_name", "salary", "creation_time");
Field<String> ivCompanyNmae = field("insert_values.company_name". Stirng.class);
Field<Integer> ivSalary = field("insert_values.salary", Integer.class);
...

Insert<AffectedSubscriberRecord> insert = insertInto(EMPLOYMENT_CONTRACT)
    .columns(EMPLOYMENT_CONTRACT.COMPANY_ID,
        EMPLOYMENT_CONTRACT.PERSON_ID,
        EMPLOYMENT_CONTRACT.SALARY,
        EMPLOYMENT_CONTRACT.CREATION_DATE_TIME
    )
    .select(
        select(
            COMPANY.COMPANY_ID,
            PERSON.PERSON_ID,
            ivSalary,
            ivCreatedTime
        )
        .from(insertValues)
            .join(COMPANY).using(COMPANY.COMPANY_NAME)
            .join(PERSON).using(PERSON.PERSON_NAME)
     );

这就是jOOQ生成演员表的地方。 未知数可以成为弓箭手,但会明确地正确施放。

暂无
暂无

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

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