【问题标题】:no extra query for related entity没有对相关实体的额外查询
【发布时间】:2022-01-17 07:02:49
【问题描述】:

我有这个实体:

@Entity
public class PlantArea {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_plantarea")
    @SequenceGenerator(name = "seq_plantarea", allocationSize = 1)
    @Column(nullable = false, updatable = false)
    private Long id;
    private String code;
    private String name;
    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name="plantid", nullable = false)
    private Plant plant;
}

在回购中,我得到了所有像这样的植物区:

@Override
    public List<PlantArea> getPlantAreas() {
        return plantAreaRepository.findAll();
    }

所以,没什么特别的。

对此我有两个问题:

  1. Hibernate 触发两个查询:一个用于选择植物区域,另一个用于选择相关植物。我怎样才能避免这种情况,我怎样才能强制休眠只做一个查询?就像在 sql 中(伪):选择所有植物区域内连接植物。我想我必须写一些 jpql/hql,不管是什么查询?

  2. 使用简单的findAll() 查询植物区域会得到以下 json 结果:

    [ { “身份证”:1, “代码”:“122”, “名称”:“汽车”, “植物”: { “身份证”:1, “代码”:“130”, “位置”:“某个城市”, “公司”: { “身份证”:1, "name": "测试公司长名称", "shortName": "测试公司" } } } ]

如您所见,每家工厂也都有一家公司。所以,我得到了相关对象的完整链:植物 -> 植物 -> 公司。问题是,我怎样才能抑制 company 对象?基本上我需要这样的查询(伪sql):

select plantarea.*, plant.*, company.* from plantarea 
inner join plant on plantarea.plant_id=plant.id
inner join company on plant.company_id=company.id

【问题讨论】:

    标签: hibernate jpa spring-data-jpa spring-data


    【解决方案1】:

    (1) 选择Plant 的附加查询是因为您在PlantArea.plant 中配置了FetchType.EAGER,这是一种代码异味(有关详细信息,请参阅this)。要解决它,您可以使用 'fetch join' 获取PlantArea 及其Plant 的列表。喜欢的东西:

    public interface PlantAreaRepository extends JpaRepository<PlantArea, Long> {
        
        @Query("select p from PlantArea p left join fetch p.plant")
        List<PlantArea> findAllWithPlant(); 
    
    }
    

    (2) 对我来说另一种代码味道。对于一个不平凡的应用程序,我宁愿为 API JSON 响应创建另一个 DTO,而不是直接将实体暴露给外界。它只会让您难以发展您的应用程序,因为每当您更改实体时,您都必须担心它可能会影响现有的 API 客户端。因此,只需创建一个单独的 DTO,其结构与您的 API 响应完全相同。然后将PlantArea映射到这些DTO并返回给客户端。

    @Data
    public class PlantAreaDto {
        private Long id;
        private String code;
        private String name;
        private PlantDto plant;
    }
    
    @Data
    public class PlantDto {
        private Long id;
        private String location;
    }
    

    【讨论】:

    • 存在如何抑制加载company 实体的问题仍处于打开状态。通过上面的查询,我得到了带有植物的工厂区域,并且由于我获取了植物,因此也获取了关联公司。如何避免这种情况?
    • 我猜这是因为您还将 Plant 的公司映射为 FetchType.EAGER。改成懒人
    • 我已经将其更改为LAZY,但由于我使用查询手动获取plant,我强制休眠以急切地加载plant 实体及其所有相关实体,对吗?在我看来,最后我会写一个原生查询。
    • 是的。如果您不希望休眠发出单独的 SELECT 查询,您最终需要自己编写查询。 (顺便说一句,此查询称为 JPQL/HQL 但不是本机查询)。或者您尝试使用@fladdimir 提到的EntityGraph,您不需要自己编写查询,但允许您以声明方式配置获取连接。
    猜你喜欢
    • 1970-01-01
    • 2021-08-20
    • 1970-01-01
    • 2012-12-18
    • 2011-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多