admin

JOOQ从表中获取记录并将其插入到另一个表中

sql

我想从一个表中选择所有字段,提取到另一个表的记录中,然后将记录插入该表中。但是,我得到一个错误,说值是空的。这是相关的代码段:

    // Fetch
    PersonDeactivatedRecord person = getContext().selectFrom(PERSON)
            .where(PERSON.ID.eq(id))
            .fetchOneInto(PersonDeactivatedRecord.class);

    // Insert
    person.setDeactivatedOn(new Timestamp(System.currentTimeMillis()));
    getContext().insertInto(PERSON_DEACTIVATED)
            .set(person)
            .execute();

person_deactivated表是表的副本,person外加一列(deactivated_on)。我得到的错误是这样的:

org.jooq.exception.DataAccessException: SQL [insert into "xxx"."person_deactivated" ("deactivated_on") values (cast(? as timestamp))]; ERROR: null value in column "id" violates not-null constraint
  Detail: Failing row contains (null, null, null, null, null, null, null, null, null, ...)

请注意,当我调试此代码时,我看到该person对象已按预期完全填充。知道我在这里缺少什么吗?


阅读 195

收藏
2021-07-01

共1个答案

admin

InsertSetStep.set(Record)方法不会将整个person记录插入到目标表中,而是仅考虑值被“更改”的那些字段。参见Javadoc:

set(Map)与将参数记录视为a的调用相同Map<Field<?>, Object>,不同之处在于Record.changed()考虑这些标志以便仅更新更改的值。

因此,您可以通过多种方式解决此问题(当然,这里列出的内容比我列出的还要多):

1.将所有更改的标志设置为true

这将是最简单的解决方法:

// Insert
person.setDeactivatedOn(new Timestamp(System.currentTimeMillis()));
person.changed(true); // Sets all flags to true
getContext().insertInto(PERSON_DEACTIVATED)
        .set(person)
        .execute();

2.编写一个查询

该解决方案的优点是您只有一个查询,这意味着:

  1. 单个服务器往返(更少的延迟)
  2. 更好的锁定语义(数据库知道您在做什么,因此出现竞争状况的机会更少)

因此,您可以编写:

getContext()
    .insertInto(PERSON_DEACTIVATED)
    .columns(PERSON_DEACTIVATED.fields())
    .select(
        select(Arrays
           .stream(PERSON_DEACTIVATED.fields())
           .map(f -> f.equals(PERSON_DEACTIVATED.DEACTIVATED_ON)
                   ? currentTimestamp()
                   : PERSON.field(f))
           .collect(Collectors.toList()))
       .from(PERSON)
       .where(PERSON.ID.eq(id))
    )
    .execute();

与往常一样,此答案假定您的代码中包含以下静态导入:

import static org.jooq.impl.DSL.*;

上述查询将插入所有列从PERSONPERSON_DEACTIVATED,除了DEACTIVATED_ON列将通过当前的时间戳来代替。

2021-07-01