【问题标题】:Spring JPA inserts duplicate select columns when using @MapsId, @AttributeOverride on embedded composite keySpring JPA 在嵌入的复合键上使用 @MapsId、@AttributeOverride 时插入重复的选择列
【发布时间】:2021-07-22 17:25:28
【问题描述】:

我正在开发一个 Spring Boot REST API 应用程序,但在为 Transact-SQL (SQL Server) 方言生成 SQL 时遇到了问题,我不确定我在哪里做错了。

该应用程序是关于存储管理的,我有两个实体:PartStock。我已将结构简化为尽可能简单。

我有复合 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。

【问题讨论】:

    标签: sql-server spring-boot hibernate jpa orm


    【解决方案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 查询。
    猜你喜欢
    • 1970-01-01
    • 2015-01-24
    • 2016-09-17
    • 2014-09-12
    • 2017-06-19
    • 2015-10-24
    • 1970-01-01
    • 2022-01-03
    • 2019-02-06
    相关资源
    最近更新 更多