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

Spring Data JDBC一对多关系中Set与List的使用报错问题

Spring Data JDBC中List集合一对多关联插入失败问题

初始正常代码(使用Set)

数据库Schema

-- schema.sql
CREATE TABLE JOBS (ID INT AUTO_INCREMENT PRIMARY KEY);
CREATE TABLE JOB_TASKS (JOB INT REFERENCES JOBS(ID));

实体类

@Table("JOB_TASKS")
public record Task() {
}
@Table("JOBS")
public record Job(@Id Integer id, @MappedCollection(idColumn = "JOB") Set<Task> tasks) {
}

Repository与测试代码

public interface JobRepository extends ListCrudRepository<Job, Integer> {
}
// 主类中的测试代码
@Bean
public CommandLineRunner commandLineRunner(final JobRepository jobRepository) {
    return args -> {
        final Set<Task> tasks = new HashSet<>();
        tasks.add(new Task());
        final var job = new Job(null, tasks);
        jobRepository.save(job);
    };
}

改为List后的报错

将Set<Task>改为List<Task>后,插入操作失败,报错如下:

H2数据库报错

Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Column "JOBS_KEY" not found; SQL statement:
INSERT INTO "JOB_TASKS" ("JOB", "JOBS_KEY") VALUES (?, ?) [42122-224]

Oracle数据库报错

Caused by: org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [INSERT INTO "JOB_TASKS" ("JOBS_KEY", "JOB") VALUES (?, ?)]
Caused by: java.sql.BatchUpdateException: ORA-00904: "JOBS_KEY": invalid identifier

尝试添加keyColumn后的问题

根据@MappedCollection文档说明,keyColumn适用于List或Map集合,于是修改Job类:

@Table("JOBS")
public record Job(@Id Integer id, @MappedCollection(idColumn = "JOB", keyColumn = "JOB") List<Task> tasks) {
}

但出现约束违规:

H2数据库报错

Caused by: org.h2.jdbc.JdbcBatchUpdateException: Referential integrity constraint violation: "CONSTRAINT_3: PUBLIC.JOB_TASKS FOREIGN KEY(JOB) REFERENCES PUBLIC.JOBS(ID) (0)"; SQL statement:
INSERT INTO "JOB_TASKS" ("JOB") VALUES (?) [23506-224]

Oracle数据库报错

Caused by: java.sql.BatchUpdateException: ORA-02291: integrity constraint (...) violated - parent key not found

移除H2约束后发现,JOB_TASKS表中JOB列插入值为0,而非预期的1。

解决方案

问题根源是Spring Data JDBC处理List集合时,默认需要一个索引列来维护List的顺序,而Set不需要(因为无序)。

步骤1:修改数据库Schema,添加索引列

给JOB_TASKS表添加一个用于维护List顺序的列,比如POSITION:

CREATE TABLE JOBS (ID INT AUTO_INCREMENT PRIMARY KEY);
CREATE TABLE JOB_TASKS (
    JOB INT REFERENCES JOBS(ID),
    POSITION INT NOT NULL,
    PRIMARY KEY (JOB, POSITION) -- 可选,确保每个Job下的Task位置唯一
);

步骤2:修改Job实体类的@MappedCollection配置

指定keyColumn为新增的POSITION列:

@Table("JOBS")
public record Job(@Id Integer id, @MappedCollection(idColumn = "JOB", keyColumn = "POSITION") List<Task> tasks) {
}

原理说明

  • List是有序集合,Spring Data JDBC需要通过keyColumn指定的列来存储元素的顺序索引,这样在查询时才能按原顺序返回。
  • 之前错误地将keyColumn设为JOB,导致框架把顺序索引值写入了外键列,而此时Job的ID还未生成(插入Job和Task分两步执行,先插入Job获取ID,再插入Task),所以写入了默认值0,触发外键约束错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 12:57:11