Hibernate делает дополнительный оператор SQL с @ManyToOne и @Lazy, выбирающим объект - PullRequest
2 голосов
/ 22 января 2020

Я хотел бы, чтобы кто-то объяснил мне, почему Hibernate делает одно дополнительное SQL заявление в моем прямом случае. В основном у меня есть этот объект:

@Entity
class ConfigurationTechLog (
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        val id: Long?,

        val configurationId: Long,

        val type: String,

        val value: String?
) {
        @JsonIgnore
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "configurationId", insertable = false, updatable = false)
        val configuration: Configuration? = null
}

Так что, как видите, ничего особенного там нет. И когда я выполняю этот запрос:

@Query(value = "SELECT c FROM ConfigurationTechLog c where c.id = 10")
fun findById10() : Set<ConfigurationTechLog>

В моей консоли я вижу это:

Hibernate: 
    /* SELECT
        c 
    FROM
        ConfigurationTechLog c 
    where
        c.id = 10 */ select
            configurat0_.id as id1_2_,
            configurat0_.configuration_id as configur2_2_,
            configurat0_.type as type3_2_,
            configurat0_.value as value4_2_ 
        from
            configuration_tech_log configurat0_ 
        where
            configurat0_.id=10
Hibernate: 
    select
        configurat0_.id as id1_0_0_,
        configurat0_.branch_code as branch_c2_0_0_,
        configurat0_.country as country3_0_0_,
        configurat0_.merchant_name as merchant4_0_0_,
        configurat0_.merchant_number as merchant5_0_0_,
        configurat0_.org as org6_0_0_,
        configurat0_.outlet_id as outlet_i7_0_0_,
        configurat0_.platform_merchant_account_name as platform8_0_0_,
        configurat0_.store_type as store_ty9_0_0_,
        configurat0_.terminal_count as termina10_0_0_ 
    from
        configuration configurat0_ 
    where
        configurat0_.id=?

Может кто-нибудь объяснить мне, что здесь происходит? Откуда этот второй запрос?

Ответы [ 4 ]

1 голос
/ 24 января 2020

Чтобы сделать ассоциацию ленивой, Hibernate должен создать экземпляр прокси вместо использования реального объекта, то есть он должен создать экземпляр динамически генерируемого подкласса класса ассоциации.

Так как в Kotlin все классы являются окончательными по умолчанию, Hibernate не может создать его подкласс, поэтому он должен немедленно создать реальный объект и инициализировать ассоциацию. Чтобы убедиться в этом, попробуйте объявить класс Configuration как open.

Чтобы решить эту проблему без явного объявления всех сущностей open, это проще сделать через kotlin -allopen плагин компилятора .

1 голос
/ 31 января 2020

Эта ссылка может быть полезна для понимания того, что это за (общая) проблема N + 1 Проблема


Позвольте мне привести пример:

У меня есть три курса, и у каждого из них есть связанные студенты. Я хотел бы выполнить «ВЫБОР * ИЗ Курсов». Это первый запрос, который я хочу (+ 1), но Hibernate в фоновом режиме, чтобы получить подробную информацию о студентах для каждого курса, который выбирает * данный нам, выполнит еще три запроса, по одному для каждого курса (N, есть три Курс идет от избранных *). В конце я увижу 4 запроса в журналы Hibernate

Учитывая приведенный выше пример, вероятно, именно это и происходит в вашем случае: вы выполняете первый запрос, который хотите получить, получив Id конфигурации = 10, но после этого, Hibernate, возьмет объект, связанный с этой Конфигурацией, затем будет выполнен новый запрос для получения этого связанного объекта.

Эта проблема должна быть указана в спецификациях c для отношений (конечно же) и LAZY Fetch. Это не проблема, которую вы вызвали, но это проблема производительности Hibernate с LAZY Fetch, рассматривайте это как ошибку или поведение по умолчанию

Чтобы решить эту проблему, я не знаю, будет ли в вашем случае, но ... я знаю три способа:

  • EAGER Fetch Type (но не самый удачный вариант)
  • Запрос с JOIN FETCH между курсами и студентами
  • Создание объекта EntityGraph, который представляет курс и подграф, который представляет студентов и добавляется в EntityGraph
1 голос
/ 22 января 2020

Я предполагаю, что вы используете Kotlin класс данных. Класс данных kotlin будет генерировать методы toString, hashCode и equals, использующие все поля-члены. Так что, если вы используете возвращенные значения в своем коде таким образом, что вызов любого из этих методов может вызвать эту проблему.

Кстати, использование Kotlin предложений данных противоречит основным c требованиям для JPA Entity как классы данных являются конечными классами, имеющими конечных членов.

0 голосов
/ 31 января 2020

Глядя на ваш вопрос, это выглядит как ожидаемое поведение.

Поскольку вы настроили configuration для ленивого извлечения с помощью @ManyToOne(fetch = FetchType.LAZY), первый sql просто запрашивает другие переменные. Когда вы пытаетесь получить доступ к объекту configuration, hibernate снова запрашивает базу данных. Вот что такое ленивая загрузка. Если вы хотите, чтобы Hibernate использовал объединения и извлекал все значения одновременно, попробуйте установить @ManyToOne(fetch = FetchType.EAGER).

...