【问题标题】:JOIN FETCH on @ManyToMany relationship produce N + 1 queries@ManyToMany 关系上的 JOIN FETCH 产生 N + 1 个查询
【发布时间】:2020-07-31 21:59:09
【问题描述】:

在我的应用程序中,我有以下@Entity,其中包含@ManyToMany 关系。

@Entity(name="CommonStaff")
@Table(name="staff")
@Getter @Setter @FieldNameConstants
@NoArgsConstructor
public class Staff implements Serializable {
    ...

    @ManyToMany(cascade={ CascadeType.PERSIST, CascadeType.MERGE }, fetch=FetchType.LAZY)
    @JoinTable(name="staff_language",
               joinColumns={ @JoinColumn(name="username", referencedColumnName="username") },
               inverseJoinColumns={ @JoinColumn(name="language_code", referencedColumnName="code") })
    private Set<Language> languages = new HashSet<>();

    ...
}

@Entity(name="CommonLanguage")
@Table(name="language")
@Getter @Setter
@NoArgsConstructor
public class Language implements Serializable {
    @Id
    @Column(name="id")
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;
    @NaturalId
    private String code;
    private String name;
    @Column(name="short_name")
    private String shortName;
    private String description;
    @Column(name="order_id")
    private Integer orderId;

    @Override
    public int hashCode() {
        return Objects.hashCode(this.getCode());
    }

    @Override
    public boolean equals(Object other) {
        if (this == other)
            return true;

        if (!(other instanceof Language))
            return false;

        Language that = (Language) other;
        return Objects.equals(that.getCode(), this.getCode());
    }
}

@Repository中,我创建了以下方法来热切地获取languages

@Query(value="SELECT S"
       + "    FROM CommonStaff S"
       + "    JOIN FETCH S.languages"
       + "    WHERE S.userId = :userId")
Staff find(String userId);

我在@Controller 中创建了以下方法来测试查询。

Staff staff = staffRepo.find(userId);
if (staff != null) {
    System.out.println(staff.getName());
    staff.getLanguages().forEach(language -> System.out.println(language.getName()));
}

我在控制台中看到的内容如下。

2020-04-18 18:41:02,394 DEBUG [http-nio-9000-exec-2] org.hibernate.SQL   : 
    /* SELECT
        S    
    FROM
        CommonStaff S    
    JOIN
        FETCH S.languages    
    WHERE
        S.userId = :userId */ select
            staff0_.id as id1_24_0_,
            language2_.id as id1_9_1_,
            staff0_.email as email2_24_0_,
            staff0_.name as name3_24_0_,
            staff0_.username as username4_24_0_,
            staff0_.is_active as is_activ5_24_0_,
            staff0_.address as address6_24_0_,
            staff0_.biometric_id as biometri7_24_0_,
            staff0_.card_number as card_num8_24_0_,
            language2_.code as code2_9_1_,
            language2_.description as descript3_9_1_,
            language2_.name as name4_9_1_,
            language2_.order_id as order_id5_9_1_,
            language2_.short_name as short_na6_9_1_,
            languages1_.username as username1_25_0__,
            languages1_.language_code as language2_25_0__ 
        from
            staff staff0_ 
        inner join
            staff_language languages1_ 
                on staff0_.username=languages1_.username 
        inner join
            language language2_ 
                on languages1_.language_code=language2_.code 
        where
            staff0_.username=?
2020-04-18 18:41:02,395 TRACE [http-nio-9000-exec-2] org.hibernate.type.descriptor.sql.BasicBinder   : binding parameter [1] as [VARCHAR] - [90000010]
2020-04-18 18:41:02,411 DEBUG [http-nio-9000-exec-2] org.hibernate.SQL   : 
    /* load com.ft.common.db.customer.domain.Language */ select
        language0_.id as id1_9_0_,
        language0_.code as code2_9_0_,
        language0_.description as descript3_9_0_,
        language0_.name as name4_9_0_,
        language0_.order_id as order_id5_9_0_,
        language0_.short_name as short_na6_9_0_ 
    from
        language language0_ 
    where
        language0_.code=?
2020-04-18 18:41:02,411 TRACE [http-nio-9000-exec-2] org.hibernate.type.descriptor.sql.BasicBinder   : binding parameter [1] as [VARCHAR] - [LAN_ENG]
2020-04-18 18:41:02,420 DEBUG [http-nio-9000-exec-2] org.hibernate.SQL   : 
    /* load com.ft.common.db.customer.domain.Language */ select
        language0_.id as id1_9_0_,
        language0_.code as code2_9_0_,
        language0_.description as descript3_9_0_,
        language0_.name as name4_9_0_,
        language0_.order_id as order_id5_9_0_,
        language0_.short_name as short_na6_9_0_ 
    from
        language language0_ 
    where
        language0_.code=?
2020-04-18 18:41:02,420 TRACE [http-nio-9000-exec-2] org.hibernate.type.descriptor.sql.BasicBinder   : binding parameter [1] as [VARCHAR] - [LAN_MAL]
Edgar Rey Tann
English
Malay

据我了解,JOIN FETCHLEFT JOIN FETCH 应该可以帮助我摆脱最后两个查询,但它们都不起作用。在我的研究过程中,我找不到任何可行的解决方案。如果您能给我指明一个方向,我将不胜感激。

【问题讨论】:

  • 也添加语言类并使用left join fetch
  • @AbinashGhosh:这是一种单向关系。在Language 类中,我没有定义任何类型的关系。我也试过LEFT JOIN FETCH,结果是一样的:(
  • Language 中尝试使用 equals 和 hashcode,可能 JPA 无法识别,vladmihalcea.com/…
  • @AbinashGhosh:感谢您的提示。实际上,我的所有实体上都有equalshashCode。我过滤掉这些代码以缩短我的问题:)。我更新了我的问题以包括方法:)
  • @Mr.J4mes 你找到解决这个问题的办法了吗?

标签: java sql-server spring hibernate spring-data-jpa


【解决方案1】:

当实体已经在一级缓存或二级缓存中时,可能会发生这种情况。在调用find()之前尝试使用entityManager.clear()

【讨论】:

    猜你喜欢
    • 2019-11-21
    • 1970-01-01
    • 1970-01-01
    • 2018-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多