如何将jOOQ查询结果映射到Immutables不可变对象?
解决jOOQ映射Immutables可空Optional字段的问题
当用Immutables生成带Optional可空字段的对象时,jOOQ默认会把数据库NULL值直接传入of()工厂方法,但该方法要求传入Optional.empty()而非null,导致映射失败。以下是几种可行的解决办法:
方法一:配置Immutables生成支持null参数的工厂方法
通过Immutables的@Value.Style注解开启nullAccepted选项,让生成的of()方法允许接收null参数,并自动将其转换为Optional.empty()。
示例代码:
@Value.Immutable @Value.Style( factory = "of", // 指定工厂方法名保持为of nullAccepted = true // 允许参数为null,内部自动处理为Optional.empty() ) public abstract class User { public abstract String firstName(); @Nullable // 标记该字段可为null public abstract Optional<String> lastName(); }
这样jOOQ在映射时,遇到数据库中的NULL值直接传入of()方法即可,Immutables会自动把null转为Optional.empty(),无需额外处理。
方法二:给jOOQ注册全局Optional转换器
自定义一个Converter,让jOOQ在映射时自动将数据库的NULL值转换为Optional.empty(),非NULL值转为Optional.of(值),适配Immutables的of()方法要求。
示例代码:
// 通用Optional转换器 public class OptionalConverter<T> implements Converter<T, Optional<T>> { private final Class<T> type; public OptionalConverter(Class<T> type) { this.type = type; } @Override public Optional<T> from(T databaseValue) { return Optional.ofNullable(databaseValue); } @Override public T to(Optional<T> domainValue) { return domainValue.orElse(null); } @Override public Class<T> fromType() { return type; } @Override public Class<Optional<T>> toType() { return (Class<Optional<T>>) (Class<?>) Optional.class; } } // 注册到jOOQ配置 Configuration jooqConfig = new DefaultConfiguration() .set(SQLDialect.YOUR_DIALECT) // 替换为你的数据库方言,比如MYSQL、POSTGRES .set(new DefaultDataTypeProvider() { @Override public <T> DataType<T> defaultDataType(Class<T> type) { // 针对String类型的Optional注册转换器,可按需添加其他类型(如Integer、Long等) if (type.equals(Optional.class)) { return (DataType<T>) SQLDataType.VARCHAR.asConvertedDataType(new OptionalConverter<>(String.class)); } return super.defaultDataType(type); } });
注册后,jOOQ在映射到Optional类型字段时会自动转换null值,完美适配Immutables的of()方法。
方法三:自定义jOOQ RecordMapper
如果以上两种方法不适用,可以自定义RecordMapper,手动处理字段映射,将null值转为Optional.empty()后再调用Immutables的of()方法。
示例代码:
public class UserRecordMapper implements RecordMapper<Record, ImmutableUser> { @Override public ImmutableUser map(Record record) { String firstName = record.get("first_name", String.class); String lastName = record.get("last_name", String.class); // 手动将null转为Optional.empty() return ImmutableUser.of(firstName, Optional.ofNullable(lastName)); } } // 使用时指定Mapper Result<Record> result = dslContext.select().from("user").fetch(); List<ImmutableUser> users = result.map(new UserRecordMapper());
这种方法需要为每个Immutables对象编写单独的Mapper,适合字段较少的场景。
内容的提问来源于stack exchange,提问作者Loginus
相关产品推荐
相关产品推荐

