jOOQ 3.19:如何按列名格式强制指定Record列类型为Long?
解决jOOQ 3.19读取SQLite大Integer字段转long的问题
针对你遇到的SQLite INTEGER字段存储超过32位数值,jOOQ默认转成int导致溢出的问题,结合jOOQ 3.19版本和无代码生成的场景,推荐两种可行方案:
方案一:全局配置自定义Binding(推荐)
通过配置ForcedType和自定义Binding,让jOOQ自动将匹配指定列名格式的INTEGER字段映射为Java Long类型,无需修改每一处SQL。
1. 实现自定义Binding
这个Binding负责处理SQLite INTEGER与Java Long的双向转换,兼容SQLite可能返回的Integer/Long类型:
import org.jooq.*; import java.sql.*; import java.util.Objects; public class LongBinding implements Binding<Object, Long> { @Override public Converter<Object, Long> converter() { return new Converter<>() { @Override public Long from(Object t) { if (t == null) return null; // 统一将SQLite返回的数值转成Long return ((Number) t).longValue(); } @Override public Object to(Long u) { return u; } @Override public Class<Object> fromType() { return Object.class; } @Override public Class<Long> toType() { return Long.class; } }; } @Override public void sql(BindingSQLContext<Long> ctx) throws SQLException { ctx.render().visit(DSL.val(ctx.value(), SQLDataType.BIGINT)).sql(""); } @Override public void register(BindingRegisterContext<Long> ctx) throws SQLException { ctx.statement().registerOutParameter(ctx.index(), Types.BIGINT); } @Override public void set(BindingSetStatementContext<Long> ctx) throws SQLException { ctx.statement().setLong(ctx.index(), Objects.requireNonNull(ctx.value())); } @Override public void set(BindingSetSQLOutputContext<Long> ctx) throws SQLException { ctx.output().writeLong(Objects.requireNonNull(ctx.value())); } @Override public void get(BindingGetResultSetContext<Long> ctx) throws SQLException { ctx.convert(converter()).value(ctx.resultSet().getLong(ctx.index())); } @Override public void get(BindingGetStatementContext<Long> ctx) throws SQLException { ctx.convert(converter()).value(ctx.statement().getLong(ctx.index())); } @Override public void get(BindingGetSQLInputContext<Long> ctx) throws SQLException { ctx.convert(converter()).value(ctx.input().readLong()); } }
2. 配置DSLContext时启用强制类型转换
在创建DSLContext时,通过Settings指定ForcedType,匹配你需要转换的列名格式(比如以_id结尾、包含large前缀等):
import org.jooq.*; import org.jooq.conf.*; import org.jooq.impl.DSL; import org.sqlite.SQLiteDataSource; import javax.sql.DataSource; public class JooqSetup { public static DSLContext getDslContext() { // 初始化SQLite数据源 SQLiteDataSource dataSource = new SQLiteDataSource(); dataSource.setUrl("jdbc:sqlite:your-database.db"); // 配置强制类型转换 Settings settings = new Settings() .withForcedTypes(new ForcedType() .withName("BIGINT") // 映射为SQL BIGINT类型 .withBinding(LongBinding.class) // 绑定自定义转换逻辑 .withIncludeExpression(".*_id|large_\\w+") // 正则匹配目标列名,按需调整 .withIncludeTypes("INTEGER") // 只针对SQLite的INTEGER类型生效 ); return DSL.using(dataSource, SQLDialect.SQLITE, settings); } }
配置完成后,执行原生SQL时,匹配列名的INTEGER字段会自动被读取为Java Long类型,避免溢出问题。
方案二:手动指定字段类型(临时场景)
如果不想全局配置,也可以在fetch时手动指定字段的目标类型,直接从Record中读取Long:
DSLContext dsl = JooqSetup.getDslContext(); Result<Record> result = dsl.fetch("SELECT id, large_num FROM your_table"); for (Record record : result) { // 直接指定类型读取 long id = record.get("id", Long.class); long largeNum = record.get("large_num", Long.class); }
为什么之前的RecordMapper/Converter没生效?
- RecordMapper:它是将Record映射到POJO的工具,但如果Record本身的字段已经被jOOQ转成int,映射时依然会溢出,必须先让jOOQ把字段读成Long再映射。
- Converter:无代码生成场景下,Converter需要绑定到具体的
Field对象,而原生SQL的Field是动态生成的,无法提前关联Converter,因此ForcedType+Binding是更适配的方案。
内容的提问来源于stack exchange,提问作者kmxp
相关产品推荐
相关产品推荐

