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
相关产品推荐
相关产品推荐

