我正在使用 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)
);
然后我将所有行绑定(bind)到 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()
调用,直接将字段传递到某处?
最佳答案
所以 values()
在 jOOQ 中为您提供了一个表构造函数。默认 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 生成转换的地方。未知数可以成为弓箭手,但随后会被明确地正确施放。
关于java - 使用 jOOQ 使用正确的类型转换从 values() 中选择插入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46137979/