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

Spring Data JDBC子对象聚合ID填充及集合存储问题咨询

Spring Data JDBC 1:N关联实体的两个问题及解决方法

表结构与实体类定义

数据库表SQL

DROP TABLE IF EXISTS product;

CREATE TABLE product (
    product_id int AUTO_INCREMENT  PRIMARY KEY,
    name varchar(250) not null,
    description varchar(512) not null
);

DROP TABLE IF EXISTS product_line;

CREATE TABLE product_line (
    product_id int constraint fk_product_line_product references product(product_id),
    label varchar(250) not null
);

Java实体类

@Data
@Builder
public class Product {

    @Id
    private Long productId;

    private String name;

    private String description;

    @Singular
    @MappedCollection(idColumn = "product_id", keyColumn = "product_id")
    private Set<ProductLine> lines;
}

@Data
@Builder
public class ProductLine {

    private Long productId;

    private String label;
}

问题1:保存后关联实体的外键未自动填充

问题描述

执行以下测试用例时,断言ProductLine的productId与主实体Product的productId相等失败,实际ProductLine的productId未被填充:

@SpringBootTest
class SpringDataJdbcApplicationTests {

    @Autowired
    private ProductRepository productRepository;

    @Test
    void saveTest() {

        Product product = Product.builder()
                .name("Product-1")
                .description("Description")
                .line(ProductLine
                        .builder()
                        .label("Line-label")
                        .build())
                .build();

        this.productRepository.save(product);

        assertThat(product.getProductId()).isNotNull();
        assertThat(product.getLines()).isNotNull().isNotEmpty().hasSize(1);
        assertThat(product.getLines().stream().findFirst()).isPresent();
        // 此处断言失败:ProductLine的productId为null
        assertThat(product.getLines().stream().findFirst().get().getProductId()).isNotNull().isEqualTo(product.getProductId());

    }
}

解答

这是Spring Data JDBC的预期行为。Spring Data JDBC在保存关联实体时,只会将外键值写入数据库,不会自动将该值回写到内存中的关联实体对象里。

如果需要让ProductLine的productId被填充,有两种可行方式:

  • 保存主实体后,手动遍历lines集合,给每个ProductLine设置productId;
  • 从数据库重新查询保存后的Product对象,此时关联的ProductLine会包含正确的productId。

问题2:将Set改为List后触发完整性约束异常

问题描述

把Product类中的Set<ProductLine>改为List<ProductLine>后,保存时抛出JdbcSQLIntegrityConstraintViolationException,日志显示插入product_line时product_id被设为0:

2022-09-10 22:33:12.393 DEBUG 18460 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [INSERT INTO "PRODUCT_LINE" ("LABEL", "PRODUCT_ID") VALUES (?, ?)]
2022-09-10 22:33:12.393 TRACE 18460 --- [           main] o.s.jdbc.core.StatementCreatorUtils      : Setting SQL statement parameter value: column index 1, parameter value [Line-label], value class [java.lang.String], SQL type 12
2022-09-10 22:33:12.393 TRACE 18460 --- [           main] o.s.jdbc.core.StatementCreatorUtils      : Setting SQL statement parameter value: column index 2, parameter value [0], value class [java.lang.Integer], SQL type 4

解答

出现这个问题是因为Spring Data JDBC处理List类型的关联集合时,需要额外的排序列来维护列表的顺序,但你的product_line表没有定义这个列。

当使用List作为关联集合类型时,需要做以下修改:

  1. 修改数据库表,添加排序列(比如line_order):
ALTER TABLE product_line ADD COLUMN line_order int NOT NULL;
  1. 修改Product类的@MappedCollection注解,指定排序列:
@Singular
@MappedCollection(idColumn = "product_id", keyColumn = "line_order")
private List<ProductLine> lines;

修改后Spring Data JDBC会自动维护列表的顺序,同时正确填充product_id的值,避免插入时设为0的问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 15:55:16