Spring Data JPA不必要的左连接

7
我有以下模型:

enter image description here

我想要获取所有指定sectorIdInstitutions(机构)。
tbInstitucion模型中,我与tbSector建立了关系:
 @ManyToOne(fetch=FetchType.LAZY)
 @JoinColumn(name="`sectorId`")
 private Sector sector;

有没有一种方法可以获得像这样的查询:
select * 
from tbInstitucion 
where sectorId = ?

我尝试了以下代码:findBySector(Sector sector),但需要另外一个查询来找到该部门,而findBySector生成的查询如下:
select
        generatedAlias0.institucionId,
        generatedAlias0.institucionNombre 
    from
        Institucion as generatedAlias0 
    left join
        generatedAlias0.sector as generatedAlias1 
    where
        generatedAlias1=:param0

尝试使用另一个:

findBySector_sectorId

生成了上述查询。将查询改为以下形式是否更好:
select * 
from tbInstitucion 
where sectorId = ?

有没有一种方法可以得到上述查询呢?为什么JPA会生成左连接?
2个回答

10

实体模型的简单概述

@Entity
class Institucion {
  @ManyToOne(fetch=FetchType.LAZY)
  @JoinColumn(name="`sectorId`")
  private Sector sector;
}

等价于:

@Entity
class Institucion {
  @ManyToOne(cascade = {}
             , fetch=FetchType.LAZY
             , optional = true
             , targetEntity = void.class)
  @JoinColumn(columnDefinition = ""
             , foreignKey = @ForeignKey
             , insertable = true
             , name="`sectorId`"
             , nullable = true
             , referencedColumnName = ""
             , table = ""
             , unique = false
             , updatable = false)
  private Sector sector;
}

请注意@ManyToOne(optional = true)@JoinColumn(nullable = true)。这表示ORM将Institucionsector属性视为可选的,并且可能不会始终设置(为非空值)。


实体模型如何影响存储库查询

现在考虑以下存储库:

public interface InstitucionRepository extends CrudRepository<Institucion, Long> {
  List<Institucion> findAllByInstitucionNombre(String nombre);

  List<Institucion> findAllByInstitucionEmail(String email);
}

考虑到上述实体声明,仓库方法应该生成如下的查询:

select
    generatedAlias0 
from
    Institucion as generatedAlias0 
left join
    generatedAlias0.sector as generatedAlias1 
where
    generatedAlias0.institucionNombre=:param0

and
select
    generatedAlias0 
from
    Institucion as generatedAlias0 
left join
    generatedAlias0.sector as generatedAlias1 
where
    generatedAlias0.institucionEmail=:param0

这是因为实体模型表明sector是可选的,因此ORM需要加载Institucion而不必担心它们的sector

按照这种模式,以下存储库方法:

  List<Institucion> findAllBySector(Sector sector);

翻译为:
select
    generatedAlias0 
from
    Institucion as generatedAlias0 
left join
    generatedAlias0.sector as generatedAlias1 
where
    generatedAlias1=:param0
解决方案1 如果Institucion.sector不是可选的,请在模型中也将其设置为必需:
  @ManyToOne(fetch=FetchType.LAZY, optional = false)
  @JoinColumn(name="`sectorId`", nullable = false)
  private Sector sector;

解决方案2

如果Institucion.sector确实是可选的,只有像@MaciejKowalski答案中所示的手动查询才能起作用。


简化查询

以下查询也可以使用:

  List<Institucion> findAllBySectorSectorId(Long id);

假设模型属性名称与帖子中完全相同。


嗨,我按照你建议的做了同样的事情,但我仍然遇到了相同的问题。@MapsId("subscriptionId") @ManyToOne(fetch = FetchType.LAZY,optional = false) @JoinColumn(name = "id_alert_subscription",nullable = false) - Chandresh Mishra

2

左连接是一种默认的隐式连接策略,在使用 @EntityGraph 功能时也是如此。

我建议使用显式的 @Query 定义:

@Query("select i from institution i inner join i.sector s where s.id = :sectorId")
public Institution getBySector(@Param("sectorId") Integer sectorId); 

1
但为什么它会生成一个连接,使用select * from tbInstitucion where sectorId = ?不是更好吗? - rena
这不是ORM中编写查询的方式...你需要获取相关实体,然后获取其ID。 - Maciej Kowalski

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接