ПРИСОЕДИНЯЙТЕСЬ к связям @ManyToMany, создайте N + 1 запросов - PullRequest
0 голосов
/ 18 апреля 2020

В моем приложении есть следующее @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 FETCH или LEFT JOIN FETCH должны помочь мне избавиться от последних двух запросов, но они оба не сработали. Я не мог найти жизнеспособных решений во время моего исследования. Я был бы очень признателен, если бы вы указали мне направление.

1 Ответ

0 голосов
/ 19 апреля 2020

Это может произойти, когда объект уже находится в кэше первого уровня или также в кэше второго уровня. Попробуйте использовать entityManager.clear() перед вызовом find()

...