Spring JPA 在嵌入的复合键上使用 @MapsId、@AttributeOverride 时插入重复的选择列
Posted
技术标签:
【中文标题】Spring JPA 在嵌入的复合键上使用 @MapsId、@AttributeOverride 时插入重复的选择列【英文标题】:Spring JPA inserts duplicate select columns when using @MapsId, @AttributeOverride on embedded composite key 【发布时间】:2021-07-22 17:25:28 【问题描述】:我正在开发一个 Spring Boot REST API 应用程序,但在为 Transact-SQL (SQL Server) 方言生成 SQL 时遇到了问题,我不确定我在哪里做错了。
该应用程序是关于存储管理的,我有两个实体:Part
和 Stock
。我已将结构简化为尽可能简单。
我有复合 PK - PartPK
:
@Data @Embeddable
class PartPK
@Column(name = "PART_ID")
private String partId;
@Column(name = "PART_ORGANIZATION_ID")
private String orgId;
... 实体Part
具有PartPK
作为@EmbeddedId
:
@Entity @Table(name = "parts")
class Part
@EmbeddedId
private PartPK id;
然后我有一个Stock
实体,它与Part
实体和商店相关联。该实体具有具有以下结构的复合 PK,其中我将覆盖来自 PartPK
的属性(给它们 STOCK_ 前缀)
@Data @Embeddable
class StockPK
@Column(name = "STOCK_STORE_ID")
private String storeId;
@Embedded
@AttributeOverrides(
@AttributeOverride(name = "partId", column = @Column(name = "STOCK_PART_ID")),
@AttributeOverride(name = "orgId", column = @Column(name = "STOCK_PART_ORGANIZATION_ID")),
)
private PartPK partId;
... 并附上 Stock
实体,我尝试使用 @MapsId
引用 Part 实体:
@Entity @Table(name = "stocks")
class Stock
@EmbeddedId
private StockPK id;
@MapsId("partId")
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumns(
@JoinColumn(name = "STOCK_PART_ID", referencedColumnName = "PART_ID"),
@JoinColumn(name = "STOCK_PART_ORGANIZATION_ID", referencedColumnName = "PART_ORGANIZATION_ID"),
)
private Part part;
编译,但在从存储库执行查询后,它会生成以下查询:
select TOP (?)
stockdb0_.stock_part_organization_id as bis_part0_0_,
stockdb0_.stock_store_id as bis_stor3_0_,
stockdb0_.stock_part_organization_id as bis_part5_0_,
stockdb0_.stock_part_id as bis_part6_0_
from stocks stockdb0_
如您所见,由于某种原因,它使用了 2 次 stock_part_organization_id
列。实体在持久化映射后具有不正确的值(具有相同 Store 但不同部分的两个 Stock 行被认为是同一实体)。从Stock
实体中删除part
属性时,查询和生成的持久性映射是正确的。
是不是我做错了什么?
我用的是Spring Boot 2.4.5(最新)和同版本的Started Data Jpa。
【问题讨论】:
【参考方案1】:我认为在这种情况下使用@IdClass
会更好:
class StockPK implements Serializable
private String storeId;
private Part part;
...
@Entity @Table(name = "stocks")
@IdClass(StockPK.class)
class Stock
@Id
private String id;
@Id
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumns(
@JoinColumn(name = "STOCK_PART_ID", referencedColumnName = "PART_ID"),
@JoinColumn(name = "STOCK_PART_ORGANIZATION_ID", referencedColumnName = "PART_ORGANIZATION_ID"),
)
private Part part;
...
但是如果你想使用@EmbeddedId
:
@Embeddable
public static class StockPK implements Serializable
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumns(
@JoinColumn(name = "STOCK_PART_ID", referencedColumnName = "PART_ID"),
@JoinColumn(name = "STOCK_PART_ORGANIZATION_ID", referencedColumnName = "PART_ORGANIZATION_ID"),
)
private Part part;
@Column(name = "STOCK_STORE_ID")
private String storeId;
@Entity @Table(name = "stocks")
class Stock
@EmbeddedId
private StockPK id;
// The association is already defined in the key
无论如何,您不必使用@MapsId
(that's for something else),您可以找到这两种方法的示例以及更多详细信息in the Hibernate ORM documentation。
【讨论】:
感谢您的回复。(1) 我想避免在复合 PK 中声明实体类型,因为在特定情况下我只需要键属性,并且在访问时会获取整个实体。 (2) 关联是在 StockPK 中定义的,但我将无法从 Stock 实体中截取 Part 实体并根据 Stock 上下文中 Part 实体的非键值进行 Criteria 查询。以上是关于Spring JPA 在嵌入的复合键上使用 @MapsId、@AttributeOverride 时插入重复的选择列的主要内容,如果未能解决你的问题,请参考以下文章
是否可以在 Spring Boot 中运行两个使用 spring.jpa.generate-ddl 填充的嵌入式数据库?