jOOQ+R2DBC+MySQL栈读取bit(1)列失败问题求助
R2DBC + jOOQ + MySQL 中 bit(1) 列读取为 null 的问题分析与解决
问题现象
技术栈:io.asyncer:r2dbc-mysql:1.1.3 + jOOQ:3.19.8 + MySQL:8.4.0,定义包含bit(1)类型的列后,JDBC查询可正常获取值,但R2DBC查询时该列返回null;直接执行jOOQ生成的SQL在MySQL客户端能得到正确结果。
数据库DDL
CREATE TABLE entity ( id INT NOT NULL AUTO_INCREMENT, deleted_tinyint tinyint(1) NOT NULL DEFAULT false, deleted_bit bit(1) NOT NULL DEFAULT 0, CONSTRAINT pk_entity PRIMARY KEY (id) ); INSERT INTO test_schema.entity (deleted_tinyint, deleted_bit) VALUES (DEFAULT, DEFAULT);
JDBC查询代码及结果
private DSLContext ctx; @Before public void setup() { Connection connection = DriverManager.getConnection( "jdbc:mysql://localhost:3310/test_schema", "test", "test" ); ctx = DSL.using(connection, SQLDialect.MYSQL); } @Test public void test() { var fetched = ctx .selectFrom(ENTITY) .fetchOne(); log.info("{}", fetched); }
结果:
+----+---------------+-----------+ | id | deleted_tinyint | deleted_bit | +----+---------------+-----------+ | 1 | 0 | false | +----+---------------+-----------+
R2DBC查询代码及结果
private DSLContext ctx; @Before public void setup() { ConnectionFactory connectionFactory = ConnectionFactories.get( ConnectionFactoryOptions .parse("r2dbc:mysql://localhost:3310/test_schema") .mutate() .option(ConnectionFactoryOptions.USER, "test") .option(ConnectionFactoryOptions.PASSWORD, "test") .build() ); ctx = DSL.using(connectionFactory); } @Test public void test() { var fetched = Flux.from(ctx.selectFrom(ENTITY)) .log() .blockFirst(); }
结果:
+----+---------------+-----------+ | id | deleted_tinyint | deleted_bit | +----+---------------+-----------+ | 1 | 0 | {null} | +----+---------------+-----------+
问题原因
- R2DBC MySQL驱动类型映射差异:JDBC驱动默认将
bit(1)映射为Boolean,但io.asyncer实现的R2DBC MySQL驱动,默认把bit(n)(n≥1)映射为二进制类型(ByteBuffer/byte[]),而非布尔类型。 - jOOQ类型匹配失败:jOOQ在R2DBC场景下,会根据驱动返回的类型匹配对应的Java类型,当驱动返回二进制类型而jOOQ期望布尔类型时,无法完成映射,最终返回
null。
解决方案
方案1:配置R2DBC驱动将bit(1)映射为布尔类型
在连接字符串中添加bitIsBoolean=true参数,让驱动自动把bit(1)转换为Boolean:
ConnectionFactory connectionFactory = ConnectionFactories.get( ConnectionFactoryOptions .parse("r2dbc:mysql://localhost:3310/test_schema?bitIsBoolean=true") .mutate() .option(ConnectionFactoryOptions.USER, "test") .option(ConnectionFactoryOptions.PASSWORD, "test") .build() );
也可通过ConnectionFactoryOptions显式配置:
ConnectionFactoryOptions options = ConnectionFactoryOptions.builder() .option(ConnectionFactoryOptions.DRIVER, "mysql") .option(ConnectionFactoryOptions.HOST, "localhost") .option(ConnectionFactoryOptions.PORT, 3310) .option(ConnectionFactoryOptions.DATABASE, "test_schema") .option(ConnectionFactoryOptions.USER, "test") .option(ConnectionFactoryOptions.PASSWORD, "test") .option(Option.valueOf("bitIsBoolean"), true) .build(); ConnectionFactory connectionFactory = ConnectionFactories.get(options);
方案2:通过jOOQ forcedTypes强制类型映射
在jOOQ代码生成配置中添加强制类型规则,将bit(1)绑定为布尔类型:
<generator> <database> <forcedTypes> <forcedType> <name>BOOLEAN</name> <types>BIT\(1\)</types> <binding>org.jooq.impl.BooleanBinding</binding> <r2dbcBinding>org.jooq.impl.R2DBCBooleanBinding</r2dbcBinding> </forcedType> </forcedTypes> </database> </generator>
重新生成实体类后,deleted_bit字段会被定义为Boolean,jOOQ在R2DBC查询时可正确解析。
方案3:替换列类型为tinyint(1)
将bit(1)列改为tinyint(1),这是MySQL中兼容JDBC和R2DBC的布尔类型存储方式,无需额外配置即可正常映射。
验证
应用上述任一方案后,重新执行R2DBC查询,deleted_bit列将正确返回false,与JDBC查询结果一致。
内容的提问来源于stack exchange,提问作者Valeriy Stavyanko
相关产品推荐
相关产品推荐

