使用 JOOQ 在 java.sql.Timestamp 和 java.time.Instant 之间转换时遇到问题

Posted

技术标签:

【中文标题】使用 JOOQ 在 java.sql.Timestamp 和 java.time.Instant 之间转换时遇到问题【英文标题】:Trouble converting between java.sql.Timestamp & java.time.Instant with JOOQ 【发布时间】:2022-01-09 18:10:55 【问题描述】:

我在使用 JOOQ 转换器在 java.sql.Timestamp 和 java.time.Instant 之间转换时遇到问题。

这是我正在使用的代码的简化版本。

public class User 

    private static final Converter<Timestamp, Instant> MY_CONVERTER= Converter.of(
        Timestamp.class,
        Instant.class,
        t -> t == null ? null : t.toInstant(),
        i -> i == null ? null : Timestamp.from(i)
    )

    public static Table<?> table = DSL.table("user");

    public static Field<String> name = DSL.field(DSL.name(table.getName(), "name"), String.class);
    public static Field<Instant> name = DSL.field(DSL.name(table.getCreated(), "created"), SQLDataType.TIMESTAMP.asConvertedDataType(Converter.of(MY_CONVERTER)));    


private class UserDto 

    private String name;
    private Instant created;

    // getters, setters, etc.


public class UserWriter 

    // constructor with injected DefaultDSLContext etc..

    public void create(UserDto user) 

        dslContext.insertInto(User.table, User.firstName, User.lastName)
            .values(user.getName(), user.getCreated())
            .execute();
    


public class UserReader 

    // constructor with injected DefaultDSLContext etc..

    public Result<Record> getAll() 
        return dslContext.select().from(User.table).fetch();
    


public class UserService 

    // constructor with injected UserReader etc..

    public Collection<UserDto> getAll() 
        return userReader
                .getAll()
                .stream()
                .map(Users::from)
                .collect(Collectors.toList());
    


public class Users 

    public static UserDto from(Record record) 
        UserDto user = new UserDto();
        user.setName(record.get(User.name));
        user.setCreated(record.get(User.created);
        return user;
    

当我创建一个新用户时,转换器被调用并且插入工作正常。但是,当我选择用户时,不会调用转换器,并且 Users::from 方法中的 record.get(User.created) 调用会返回一个时间戳(因此会失败,因为 UserDto.setCreated 需要一个 Instant)。

有什么想法吗?

谢谢!

【问题讨论】:

更新:似乎问题在于(当我指定要选择的字段时,会调用转换器)。所以我的问题是,如果我想检索所有字段并执行转换,我是否需要在 select(à 中指定所有字段或者是否有另一种不那么冗长的方式? 能否请您发布SELECT 声明? 【参考方案1】:

为什么没有应用转换器

从您提出问题的方式来看(您没有发布您尝试过的确切 SELECT 语句),我假设您没有明确传递所有列表达式。但是,jOOQ 怎么能找出你的表有哪些列呢?您在 some 类中声明了 some 列表达式,但该类不遵循 jOOQ 已知的任何结构。让 jOOQ 获取所有 已知 列的唯一方法是让 jOOQ 知道它们,使用代码生成(见下文)。

当然,您可以让User 扩展内部org.jooq.impl.TableImpl 类并使用内部API 来注册Field 值。但是,如果您可以生成此代码,为什么还要手动执行呢?

代码生成

我将重复上一个问题的要点,即:请使用code generator。 I've now written an entire article on why you should do this。一旦 jOOQ 通过代码生成知道了您的所有元数据,您就可以像这样自动选择所有列:

UserRecord user = ctx
    .selectFrom(USER)
    .where(USER.ID.eq(...))
    .fetchOne();

不仅如此,您还可以使用&lt;forcedType&gt; 将数据类型配置为INSTANT,这样您就不必每次都担心数据类型转换。

对此我怎么强调都不为过,而且我经常感到惊讶的是,有多少项目试图在没有代码生成的情况下使用 jOOQ,这消除了 jOOQ 的大部分功能。 使用代码生成的主要原因是如果您的架构是动态的,但由于您有 User 类,它显然不是动态的。

【讨论】:

是的,我自己得出了这个结论——尽管非常感谢您花时间解释!我对 JOOQ 不太熟悉(尽管我一直打算使用它一段时间),这是一个现有项目,我目前无法完成重新布线(由于时间限制)但是我会当我有时间的时候一定会研究一下。 @AnthonyWebster 您不必一次性重写所有内容。您可以通过查询迁移查询,因为无论如何您都必须触摸它们。设置代码生成一开始会产生一些额外的开销,但随后,您的工作会得到很大改进,并且您会更有效率。 @AnthonyWebster:因为这个代码生成问题是一个常见的话题,堆栈溢出问题是关于代码生成不会发生的问题,see e.g. this one,我现在写了a more complete article about the many benefits of using the code generator

以上是关于使用 JOOQ 在 java.sql.Timestamp 和 java.time.Instant 之间转换时遇到问题的主要内容,如果未能解决你的问题,请参考以下文章

使用 jOOQ 在 PostgreSQL 中进行 UPSERT

如何使用 jOOQ 执行特定查询

使用 Jooq 进行集成测试

jooq 记录在获取数据时是不是使用列索引?

无法使用 gradle-jooq-plugin-3.0.1、jooq-3.11.2 找到或加载主类 org.jooq.codegen.GenerationTool

使用 JOOQ 在通用插入中返回 id