StackTraces
Spring AI已验证

Spring AI JDBC 记忆报 Table doesn't exist

@debug_master13 天前更新阅读 6 min00

本地 H2 跑得通,换成 MySQL 或 PostgreSQL 就报表不存在。根因是 JdbcChatMemoryRepository 的 schema 自动初始化只认嵌入式数据库,外部库不会自动建表。另外 Spring AI 2.0.0 起表结构多了 sequence_id 列,老表不加列会报 Unknown column。

Issue & Env(案发现场与环境)

  • 业务场景:用 JdbcChatMemoryRepository 存多轮对话记忆,本地开发连 H2,测试或生产连 MySQL、PostgreSQL。
  • 现象一:换库后启动或首次对话就报表不存在。
  • 现象二:升级 Spring AI 2.0 后,老表报列不存在。
  • 环境:Spring AI 1.1.x / 2.0.0,Spring Boot 3.x,H2 / MySQL / PostgreSQL。

同一份代码、同一份配置,只换了 JDBC 连接串,结果就不一样。问题不在业务代码,在表的初始化策略。

两现象经常被当成两个独立问题,其实是同一套记忆表在不同阶段暴露出来的。表都没建,先撞表不存在;表建了但结构旧,就撞列不存在。

StackTrace(堆栈日志)

MySQL 上表没建,SQL 直接失败:

org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [SELECT conversation_id, content, type, timestamp FROM SPRING_AI_CHAT_MEMORY WHERE conversation_id = ? ORDER BY timestamp]
Caused by: java.sql.SQLSyntaxErrorException: Table 'demo.SPRING_AI_CHAT_MEMORY' doesn't exist

升级 Spring AI 2.0 之后,老表缺列:

Caused by: java.sql.SQLSyntaxErrorException: Unknown column 'sequence_id' in 'field list'

这两类报错指向同一个根:表状态和框架预期对不上。前一个是表根本没有,后一个是表在但结构旧。

Reproduce(复现代码)

依赖里带上 OpenAI 与两个数据库驱动:

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>

Bean 定义只声明记忆仓库,不手工建表:

@Bean
ChatMemory chatMemory(JdbcTemplate jdbcTemplate) {
    return MessageWindowChatMemory.builder()
        .chatMemoryRepository(new JdbcChatMemoryRepository(jdbcTemplate))
        .maxMessages(20)
        .build();
}

这一段代码,连 H2 能自动建表,连 MySQL 就报表不存在。

Root Cause(根因分析)

自动建表只认嵌入式库

JdbcChatMemoryRepository 的 schema 初始化,默认只对嵌入式数据库执行,比如 H2、HSQL、Derby。MySQL、PostgreSQL 这些外部库不会自动建表。

所以本地 H2 一切正常,一换库就崩。这不是配置写错,是初始化策略的默认行为。默认值就是给「本地跑得起来」服务的,不是给生产用的。

为什么 H2 有表 MySQL 没有

根源在初始化条件。框架启动时判断当前数据库是不是嵌入式类型,是才执行建表脚本。H2 属于嵌入式,MySQL、PostgreSQL 不属于,脚本就被跳过。

同一份代码,在两种库上走出了两条分支。表现是「换库就报错」,实际是初始化根本没在外部库上发生过。

2.0 起表结构多了 sequence_id 列

Spring AI 2.0.0 起,记忆表新增了 sequence_id BIGINT 列,用来做会话内消息排序。旧实现靠 timestamp 排序,同一毫秒内写入的多条消息顺序会乱掉,快问快答时尤其明显。

老表不加这一列,框架查询里却带了 sequence_id,于是报 Unknown column。表在、列不在,报错就从「表不存在」换成了「列不存在」。

截至撰写时未经核实:sequence_id 具体在 2.0.0 的哪个小版本引入,未逐版核对,升级前建议对照升级说明确认一遍。

The Fix(解决方案)

手工建表(推荐)

外部库上手工建表,别依赖应用自动建。建表语句:

CREATE TABLE IF NOT EXISTS SPRING_AI_CHAT_MEMORY (
    conversation_id VARCHAR(36)  NOT NULL,
    content         TEXT         NOT NULL,
    type            VARCHAR(10)  NOT NULL,
    timestamp       TIMESTAMP    NOT NULL,
    sequence_id     BIGINT       NOT NULL,
    PRIMARY KEY (conversation_id, sequence_id)
);

字段说明:

  • conversation_id:会话 ID,应用侧显式传入。
  • content:消息正文。
  • type:消息类型,如 USER、ASSISTANT、SYSTEM。
  • timestamp:写入时间。
  • sequence_id:会话内排序序号,2.0 起用于稳定排序。

类型名按数据库微调:PostgreSQL 可用 BIGSERIAL 或配序列,MySQL 可用 BIGINT AUTO_INCREMENT,按主键策略选一种。

老表升级加列

已有 1.x 表结构,补一列即可:

ALTER TABLE SPRING_AI_CHAT_MEMORY
    ADD COLUMN sequence_id BIGINT NOT NULL DEFAULT 0;

加完建议回填一次历史数据的序号,再考虑加唯一约束。否则 sequence_id 全是 0,排序又会退化成原来那样。

让应用自动初始化

内置的初始化也可以对非嵌入式库放开:

spring:
  ai:
    chat:
      memory:
        repository:
          jdbc:
            initialize-schema: always

该属性的取值是 embedded(默认)与 always。生产上更推荐手工迁移:让应用自动改表风险不小,尤其是有历史数据和主键约束的时候。自动建表适合本地和测试环境,生产用一次性的迁移脚本更可控。

迁移脚本要纳入版本管理,跟应用发布一起走。别在本地手工建好表就算完,测试和生产环境同样要跑一遍。漏了一处,报错会在最不该出现的时候冒出来。

References(参考来源)

最后更新于 2026-09-28

这篇帮到你了吗?

刚解决了一个棘手的报错?花两分钟记录下来,帮助下一个遇到同样问题的开发者。

贡献一条解法