技术栈版本
| 技术栈 | 版本 |
|---|---|
| Spring AI | 1.0.0-RC1 |
| MySQL | 8.0 |
| Spring Boot | 3.5.4 |
| JDK | 17 |
前言
在开发AI商城系统时,我需要为AI对话添加持久化记忆功能。按照Spring AI官方文档,我引入了以下依赖:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-chat-memory-repository-jdbc</artifactId>
</dependency>
数据库表SPRING_AI_CHAT_MEMORY也严格按照文档创建完成,一切看起来都很正常。然而启动应用测试时,控制台报出以下错误:
Caused by: java.sql.SQLException: No value specified for parameter 2
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:130) ~[mysql-connector-j-8.0.33.jar:8.0.33]
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122) ~[mysql-connector-j-8.0.33.jar:8.0.33]
at com.mysql.cj.jdbc.ClientPreparedStatement.executeQuery(ClientPreparedStatement.java:989) ~[mysql-connector-j-8.0.33.jar:8.0.33]
... 155 common frames omitted

问题分析:为什么参数2会缺失?
错误信息很明确 - SQL语句需要两个参数但只提供了一个。问题在于,我根本没有手动编写任何SQL!这表明问题可能出现在Spring AI框架的源代码中。
深入排查:追踪Bug的起源
通过调试模式,定位到 JdbcChatMemoryRepository类中的findByConversationId方法:
// 问题代码:只传入了一个参数
@Override
public List<Message> findByConversationId(String conversationId) {
return this.jdbcTemplate.query(
dialect.getSelectMessagesSql(), // 需要2个参数的SQL
new MessageRowMapper(),
conversationId // 但只传了1个参数
);
}

查看MySQL方言实现,发现了问题根源:
public class MysqlChatMemoryRepositoryDialect {
@Override
public String getSelectMessagesSql() {
// 这条SQL需要2个参数!
return "SELECT content, type FROM SPRING_AI_CHAT_MEMORY
WHERE conversation_id = ? ORDER BY `timestamp` DESC LIMIT ?";
}
}

而顶层接口却只定义了一个参数:
public interface ChatMemoryRepository {
// 接口只定义了一个参数!
List<Message> findByConversationId(String conversationId);
}

这就导致了实现与接口契约不一致 - MySQL方言需要两个参数(conversationId和LIMIT值),但接口只提供了一个参数。
验证猜想
通过IDEA的"Evaluate Expression"功能执行SQL,添加LIMIT值后,可以正确执行:

这证实了我的猜想:问题在于参数数量不匹配。
解决方案:自定义MySQL方言
Spring AI文档提供了自定义数据库语法的能力:
ChatMemoryRepository chatMemoryRepository = JdbcChatMemoryRepository.builder()
.jdbcTemplate(jdbcTemplate)
.dialect(new PostgresChatMemoryDialect())
.build();
创建自定义方言类
public class IMysqlChatMemoryRepositoryDialect implements JdbcChatMemoryRepositoryDialect {
private static final int MAX_MESSAGES_LIMIT = 100;
@Override
public String getSelectMessagesSql() {
// 将LIMIT ?改为固定值,避免参数不匹配
return "SELECT content, type, conversation_id, `timestamp` " +
"FROM SPRING_AI_CHAT_MEMORY " +
"WHERE conversation_id = ? " +
"ORDER BY `timestamp` DESC LIMIT " + MAX_MESSAGES_LIMIT;
}
}

配置修复后的聊天记忆存储
@Configuration
public class ChatMemoryConfig {
@Bean(name = "mySqlChatMemory")
public ChatMemory mySqlChatMemory(JdbcTemplate jdbcTemplate) {
ChatMemoryRepository chatMemoryRepository = JdbcChatMemoryRepository.builder()
.jdbcTemplate(jdbcTemplate)
.dialect(new IMysqlChatMemoryRepositoryDialect())
.build();
return MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository)
.maxMessages(10)
.build();
}
}

在ChatClient中应用修复
@Bean
public ChatClient deepSeekChatClient(
DeepSeekChatModel model,
ChatMemory mySqlChatMemory,
GuideTool guideTool) {
return ChatClient.builder(model)
.defaultTools(guideTool) // 添加工具
.defaultAdvisors(
new SimpleLoggerAdvisor(), // 添加advisor增强环绕通知:日志记录器
MessageChatMemoryAdvisor.builder(mySqlChatMemory).build() // 添加聊天记录记忆的环绕增强器
)
.build();
}

修复效果验证
完成上述配置后,重新启动应用:
- ✅ 之前的
SQLException异常消失 - ✅ 应用正常启动且运行稳定
- ✅ 聊天消息正确持久化到数据库
- ✅ AI能够正确回忆对话历史



总结与最佳实践
问题本质
这个Bug的本质是实现层与接口层的契约不一致:
- 接口层只约定一个参数
- 实现层需要两个参数
- 框架没有做参数数量校验
核心解决方案
// 将动态LIMIT参数改为固定值
return "SELECT ... LIMIT " + FIXED_VALUE;
适用范围
此解决方案适用于:
- ✅ Spring AI 1.0.0-RC1 版本
技术提示:该方案是临时解决方案,建议在官方修复后及时更新。同时关注Spring AI项目的GitHub issue,了解官方修复进度。
欢迎交流!
如果你在实际项目中也遇到了Spring AI的相关问题,或者有更好的解决方案,欢迎在评论区分享你的经验!
关注我,获取更多Spring AI实战技巧和源码解析!


1万+

被折叠的 条评论
为什么被折叠?



