You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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} |
+----+---------------+-----------+

问题原因

  1. R2DBC MySQL驱动类型映射差异:JDBC驱动默认将bit(1)映射为Boolean,但io.asyncer实现的R2DBC MySQL驱动,默认把bit(n)(n≥1)映射为二进制类型(ByteBuffer/byte[]),而非布尔类型。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 21:14:55