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

Spring Boot 3 + R2DBC操作MySQL 8.0参数绑定编码异常求助

Spring Boot 3 + R2DBC MySQL 插入数据参数绑定异常解决

问题描述

使用Spring Boot 3和R2DBC开发服务,向MySQL 8.0数据库上传数据时遭遇参数绑定异常:

reactor.core.Exceptions$ErrorCallbackNotImplemented: java.lang.IllegalArgumentException: Cannot encode value of type 'class io.r2dbc.spi.Parameters$InParameter'

Maven依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.koishi.web.discord</groupId>
    <artifactId>DiscordBotProject</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>DiscordBotProject</name>
    <description>DiscordBotProject</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.12.5</version> <!-- 使用当前版本 -->
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
            <version>2.12.5</version> <!-- 使用与你的jackson版本相同的版本 -->
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.discord4j</groupId>
            <artifactId>discord4j-core</artifactId>
            <version>3.2.5</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-r2dbc</artifactId>
            <version>3.1.3</version>
        </dependency>

        <dependency>
            <groupId>dev.miku</groupId>
            <artifactId>r2dbc-mysql</artifactId>
            <version>0.8.2.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

异常栈信息

reactor.core.Exceptions$ErrorCallbackNotImplemented: java.lang.IllegalArgumentException: Cannot encode value of type 'class io.r2dbc.spi.Parameters$InParameter'
Caused by: java.lang.IllegalArgumentException: Cannot encode value of type 'class io.r2dbc.spi.Parameters$InParameter'
    at dev.miku.r2dbc.mysql.codec.DefaultCodecs.encode(DefaultCodecs.java:178) ~[r2dbc-mysql-0.8.2.RELEASE.jar:0.8.2.RELEASE]
    at dev.miku.r2dbc.mysql.ParametrizedStatementSupport.bind(ParametrizedStatementSupport.java:72) ~[r2dbc-mysql-0.8.2.RELEASE.jar:0.8.2.RELEASE]
    at dev.miku.r2dbc.mysql.ParametrizedStatementSupport.bind(ParametrizedStatementSupport.java:39) ~[r2dbc-mysql-0.8.2.RELEASE.jar:0.8.2.RELEASE]
    at org.springframework.r2dbc.core.DefaultDatabaseClient$StatementWrapper.bind(DefaultDatabaseClient.java:556) ~[spring-r2dbc-6.0.11.jar:6.0.11]
    at org.springframework.r2dbc.core.binding.IndexedBindMarkers$IndexedBindMarker.bind(IndexedBindMarkers.java:86) ~[spring-r2dbc-6.0.11.jar:6.0.11]
    at org.springframework.r2dbc.core.NamedParameterUtils$ExpandedQuery.bind(NamedParameterUtils.java:540) ~[spring-r2dbc-6.0.11.jar:6.0.11]
    at org.springframework.r2dbc.core.NamedParameterUtils$ExpandedQuery.bindTo(NamedParameterUtils.java:589) ~[spring-r2dbc-6.0.11.jar:6.0.11]
    at org.springframework.r2dbc.core.DefaultDatabaseClient$DefaultGenericExecuteSpec.lambda$getResultFunction$5(DefaultDatabaseClient.java:378) ~[spring-r2dbc-6.0.11.jar:6.0.11]
    at org.springframework.r2dbc.core.ResultFunction.apply(ResultFunction.java:64) ~[spring-r2dbc-6.0.11.jar:6.0.11]
    at org.springframework.r2dbc.core.ResultFunction.apply(ResultFunction.java:42) ~[spring-r2dbc-6.0.11.jar:6.0.11]
    at org.springframework.r2dbc.core.DefaultDatabaseClient.sumRowsUpdated(DefaultDatabaseClient.java:195) ~[spring-r2dbc-6.0.11.jar:6.0.11]
    at org.springframework.r2dbc.core.DefaultDatabaseClient$DefaultGenericExecuteSpec.lambda$execute$6(DefaultDatabaseClient.java:403) ~[spring-r2dbc-6.0.11.jar:6.0.11]
    at org.springframework.r2dbc.core.DelegateConnectionFunction.apply(DelegateConnectionFunction.java:48) ~[spring-r2dbc-6.0.11.jar:6.0.11]
    at org.springframework.r2dbc.core.DelegateConnectionFunction.apply(DelegateConnectionFunction.java:33) ~[spring-r2dbc-6.0.11.jar:6.0.11]
        at org.springframework.r2dbc.core.DefaultDatabaseClient.lambda$inConnection$2(DefaultDatabaseClient.java:120) ~[spring-r2dbc-6.0.11.jar:6.0.11]
        at io.r2dbc.pool.MonoDiscardOnCancel$MonoDiscardOnCancelSubscriber.onNext(MonoDiscardOnCancel.java:92) ~[r2dbc-pool-1.0.1.RELEASE.jar:1.0.1.RELEASE] 
        at io.r2dbc.pool.MonoDiscardOnCancel.subscribe(MonoDiscardOnCancel.java:50) ~[r2dbc-pool-1.0.1.RELEASE.jar:1.0.1.RELEASE]
    at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165) ~[reactor-core-3.5.9.jar:3.5.9]
    at reactor.pool.AbstractPool$Borrower.deliver(AbstractPool.java:467) ~[reactor-pool-1.0.1.jar:1.0.1]
        at reactor.pool.SimpleDequePool.lambda$drainLoop$9(SimpleDequePool.java:425) ~[reactor-pool-1.0.1.jar:1.0.1]
        at dev.miku.r2dbc.mysql.util.DiscardOnCancelSubscriber.onComplete(DiscardOnCancelSubscriber.java:104) ~[r2dbc-mysql-0.8.2.RELEASE.jar:0.8.2.RELEASE]
        at dev.miku.r2dbc.mysql.client.ReactorNettyClient$ResponseSink.next(ReactorNettyClient.java:340) ~[r2dbc-mysql-0.8.2.RELEASE.jar:0.8.2.RELEASE]
        at dev.miku.r2dbc.mysql.client.ReactorNettyClient.lambda$new$0(ReactorNettyClient.java:103) ~[r2dbc-mysql-0.8.2.RELEASE.jar:0.8.2.RELEASE]
        at dev.miku.r2dbc.mysql.client.MessageDuplexCodec.handleDecoded(MessageDuplexCodec.java:187) ~[r2dbc-mysql-0.8.2.RELEASE.jar:0.8.2.RELEASE]
        at dev.miku.r2dbc.mysql.client.MessageDuplexCodec.channelRead(MessageDuplexCodec.java:95) ~[r2dbc-mysql-0.8.2.RELEASE.jar:0.8.2.RELEASE]

相关方法

public Mono<MessageKeywords> createKeyword(String keyword) {
        return databaseClient.sql("INSERT INTO message_keywords (Keyword) VALUES (:keyword)")
            .bind("keyword", keyword)
            .fetch()
            .rowsUpdated()
            .map(rowsUpdated -> new MessageKeywords(keyword)); 
    }

数据类

@Table("message_keywords")
public class MessageKeywords {
    @Id
    private Long keywordId;
    private String keyword;

    @Override
    public String toString() {
        return "MessageKeywords{" +
            "keywordId=" + keywordId +
            ", keyword='" + keyword + '\'' +
            '}';
    }
}
//getters and setters are ignored

数据库表结构

CREATE TABLE message_keywords (
    keywordId BIGINT PRIMARY KEY AUTO_INCREMENT,
    keyword JSON
);

解决思路

1. 修复版本兼容性

当前使用的r2dbc-mysql:0.8.2.RELEASE适配R2DBC 0.9.x版本,而Spring Boot 3.1.3依赖R2DBC 1.0.x,版本不匹配导致参数编码逻辑冲突。

将r2dbc-mysql升级到适配R2DBC 1.0的版本,比如1.0.2.RELEASE,修改Maven依赖:

<dependency>
    <groupId>dev.miku</groupId>
    <artifactId>r2dbc-mysql</artifactId>
    <version>1.0.2.RELEASE</version>
</dependency>

2. 显式处理JSON类型参数

数据库中keyword字段为JSON类型,但代码直接绑定String值,低版本驱动无法自动转换,可通过以下方式处理:

  • 将字符串包装为JSON格式后绑定:
    .bind("keyword", "\"" + keyword + "\"")
    
  • 使用Parameters.in()指定参数类型为JSON:
    import io.r2dbc.spi.Parameters;
    import io.r2dbc.spi.R2dbcType;
    
    // ...
    .bind("keyword", Parameters.in(R2dbcType.JSON, keyword))
    
  • 在实体类字段上标注JSON类型:
    import org.springframework.data.relational.core.mapping.Column;
    
    @Column(columnDefinition = "JSON")
    private String keyword;
    

3. 更换官方R2DBC驱动

若dev.miku驱动问题无法解决,可改用MySQL官方提供的R2DBC驱动,兼容性更好:

<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-r2dbc</artifactId>
    <version>8.0.33</version>
</dependency>

同时移除原有的dev.miku:r2dbc-mysql依赖,配置文件中的URL需改为官方驱动格式:r2dbc:mysql://localhost:3306/db_name


内容的提问来源于stack exchange,提问作者Koishi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 01:04:52