没有对相关实体的额外查询
no extra query for related entity
我有这个实体:
@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;
}
在 repo 中,我得到的所有植物区域都是这样的:
@Override
public List<PlantArea> getPlantAreas() {
return plantAreaRepository.findAll();
}
所以,没什么特别的。
我有两个问题:
Hibernate 触发两个查询:一个用于 selecting plantareas,一个用于 selecting 相关植物。如何避免这种情况以及如何强制休眠只执行一个查询?就像在 sql(伪)中一样:select 所有植物区域都在内部加入植物。我想我必须写一些 jpql/hql,无论查询是什么?
用简单的 findAll()
查询植物区得到这个 json 结果:
[
{
“编号”:1,
“代码”:“122”,
“名称”:“汽车”,
“植物”: {
“编号”:1,
“代码”:“130”,
“位置”:“某个城市”,
“公司”: {
“编号”:1,
"name": "测试公司长名",
"shortName": "测试公司"
}
}
} ]
如你所见,每个工厂也都有一个公司。所以,我得到了相关对象的完整链:plantaraea -> plant -> company。问题是,如何抑制 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
(1) 那些对 select Plant
的额外查询是因为您在 PlantArea.plant
中配置了 FetchType.EAGER
这是一种代码味道(参考 this for details). To solve it , you can use '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,并将 return 映射回客户端。
@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;
}
我有这个实体:
@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;
}
在 repo 中,我得到的所有植物区域都是这样的:
@Override
public List<PlantArea> getPlantAreas() {
return plantAreaRepository.findAll();
}
所以,没什么特别的。
我有两个问题:
Hibernate 触发两个查询:一个用于 selecting plantareas,一个用于 selecting 相关植物。如何避免这种情况以及如何强制休眠只执行一个查询?就像在 sql(伪)中一样:select 所有植物区域都在内部加入植物。我想我必须写一些 jpql/hql,无论查询是什么?
用简单的
findAll()
查询植物区得到这个 json 结果:[ { “编号”:1, “代码”:“122”, “名称”:“汽车”, “植物”: { “编号”:1, “代码”:“130”, “位置”:“某个城市”, “公司”: { “编号”:1, "name": "测试公司长名", "shortName": "测试公司" } } } ]
如你所见,每个工厂也都有一个公司。所以,我得到了相关对象的完整链:plantaraea -> plant -> company。问题是,如何抑制 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
(1) 那些对 select Plant
的额外查询是因为您在 PlantArea.plant
中配置了 FetchType.EAGER
这是一种代码味道(参考 this for details). To solve it , you can use '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,并将 return 映射回客户端。
@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;
}