【问题标题】:JOOQ fetch record from table and insert it into another tableJOOQ 从表中获取记录并将其插入到另一个表中
【发布时间】:2017-05-27 21:28:27
【问题描述】:

我想从一个表中选择所有字段,获取另一个表的记录,然后将记录插入到该表中。但是,我收到一条错误消息,指出这些值为空。这是相关的代码:

    // 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 对象已按预期完全填充。知道我在这里缺少什么吗?

【问题讨论】:

    标签: java sql jooq


    【解决方案1】:

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

    这与调用set(Map) 时将参数记录视为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.*;
    

    上述查询会将PERSON 中的所有列插入PERSON_DEACTIVATED,除了DEACTIVATED_ON 列将被当前时间戳替换。

    【讨论】:

    • 非常感谢,我选择了第一个解决方案(因为它对我来说更具可读性并且用户停用不会经常发生,所以我可以忽略第二个解决方案的优势)并且它有效!请注意,第二种解决方案存在一个小问题,因为 PERSON 没有 DEACTIVATED_ON 列(它仅存在于另一个表中)。
    • @Egemen:好的,感谢您的反馈。我修复了第二个解决方案。无论如何,这种方式可能会更好,因为它不依赖于列的顺序。
    • @LukasEder 对于解决方案 2,在 JOOQ 库中找不到内部选择方法,例如不编译。我检查了各种DSL.select 变体。
    • @JoseMartinez:1)我假设你有静态导入,我刚刚添加到答案中。 2)你得到什么错误?更具体地说,由于您可能没有编写完全相同的查询,您是否介意用您编写的实际查询提出一个新问题和/或建议对我的答案进行修复?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-25
    • 1970-01-01
    • 1970-01-01
    • 2014-07-20
    • 1970-01-01
    • 2017-08-01
    • 2014-03-24
    相关资源
    最近更新 更多