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作为关联集合类型时,需要做以下修改:
- 修改数据库表,添加排序列(比如
line_order):
ALTER TABLE product_line ADD COLUMN line_order int NOT NULL;
- 修改
Product类的@MappedCollection注解,指定排序列:
@Singular @MappedCollection(idColumn = "product_id", keyColumn = "line_order") private List<ProductLine> lines;
修改后Spring Data JDBC会自动维护列表的顺序,同时正确填充product_id的值,避免插入时设为0的问题。
内容的提问来源于stack exchange,提问作者Rana_S
相关产品推荐
相关产品推荐

