Присоединитесь к Fetch JPQL, возвращая прокси-объект, используемый с Hibernate - PullRequest
0 голосов
/ 20 марта 2020

Объект:

public class PlayerDetails implements Serializable{
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumn(name = "batting_stat_id", referencedColumnName = "id")
    private BattingStats battingStats;

    @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumn(name = "bowling_stat_id", referencedColumnName = "id")
    private BowlingStats bowlingStats;
    }

Код репозитория:

 String queryString = "select p from PlayerDetails p inner join fetch p.battingStats b where p.name = :name";
TypedQuery<PlayerDetails> query = entityManager.createQuery(queryString, PlayerDetails.class);
query.setParameter("name", name);

PlayerDetails playerDetails = query.getSingleResult();

String queryString2 = "select p from PlayerDetails p inner join fetch p.bowlingStats b where p.name = :name";
TypedQuery<PlayerDetails>  query2 = entityManager.createQuery(queryString2, PlayerDetails.class);
query2.setParameter("name", name);
PlayerDetails resultList2 = query2.getSingleResult();

playerDetails.setBowlingStats(resultList2.getBowlingStats());

return Optional.ofNullable(playerDetails);

Я получаю статистику BattingsStats и Bowling Stats с помощью JOIN FETCH запросы выполняются корректно, при первом получении поля PlayerDetails вместе с полями BattingStats.

Второй запрос выбирает поля PlayerDetails и BowlingStats. Но когда я проверяю каждый извлеченный объект, BowlingStat загружается лениво и возвращает прокси. Во время сериализации я получаю:

            com.fasterxml.jackson.databind.exc.InvalidDefinitionException: 
        No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor 
    and no properties discovered to create BeanSerializer (to avoid exception, disable
     SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: 
    com.example.model.response.ResponseModel["playerDetails"]-
com.example.entity.PlayerDetails["bowlingStats"]-
com.example.entity.BowlingStats$HibernateProxy$5RK3X4j1["hibernateLazyInitializer"])

Запросы запускаются:

select playerdeta0_.id as id1_2_0_, 
    battingsta1_.id as id1_0_1_, 
    playerdeta0_.batting_stat_id as batting_7_2_0_, 
    playerdeta0_.bowling_stat_id as bowling_8_2_0_, 
    playerdeta0_.name as name2_2_0_, 
    playerdeta0_.nationality as national3_2_0_, 
    playerdeta0_.role as role4_2_0_, 
    playerdeta0_.team as team5_2_0_, 
    playerdeta0_.value as value6_2_0_, 
    battingsta1_.batting_average as batting_2_0_1_, 
    battingsta1_.centuries as centurie3_0_1_, 
    battingsta1_.fours as fours4_0_1_, 
    battingsta1_.half_centuries as half_cen5_0_1_, 
    battingsta1_.highest_score as highest_6_0_1_, 
    battingsta1_.innings as innings7_0_1_, 
    battingsta1_.matches as matches8_0_1_, 
    battingsta1_.sixes as sixes9_0_1_, 
    battingsta1_.strike_rate as strike_10_0_1_ 
from player_details playerdeta0_ 
inner join batting_stats battingsta1_ 
    on playerdeta0_.batting_stat_id=battingsta1_.id 
where playerdeta0_.name=?


select playerdeta0_.id as id1_2_0_, 
        bowlingsta1_.id as id1_1_1_, 
        playerdeta0_.batting_stat_id as batting_7_2_0_, 
        playerdeta0_.bowling_stat_id as bowling_8_2_0_, 
        playerdeta0_.name as name2_2_0_, 
        playerdeta0_.nationality as national3_2_0_, 
        playerdeta0_.role as role4_2_0_, 
        playerdeta0_.team as team5_2_0_, 
        playerdeta0_.value as value6_2_0_, 
        bowlingsta1_.balls_bowled as balls_bo2_1_1_, 
        bowlingsta1_.economy as economy3_1_1_, 
        bowlingsta1_.five_wicket_haul as five_wic4_1_1_, 
        bowlingsta1_.wickets as wickets5_1_1_ 
    from player_details playerdeta0_ 
    inner join bowling_stats bowlingsta1_ 
        on playerdeta0_.bowling_stat_id=bowlingsta1_.id 
    where playerdeta0_.name=?

Fetched Data from the first query

Fetched Data from the second query

Может кто-нибудь помочь мне с этим?

PS: ошибка не в спящем режиме, ошибка в том, что Джексон пытается проанализировать данные; сейчас я просто возвращаю сущность напрямую.

Мой вопрос не в том, как устранить эту ошибку? Мой вопрос, почему это поведение в спящем режиме? Хотя я получаю одну и ту же запись несколько раз в одном сеансе (что, как сказал Алекс, является плохим шаблоном, и я изменил его, но благодаря плохому вонючему коду я могу кое-что узнать здесь !!!) Я получаю одну и ту же запись с двумя разными запросами в одном и том же сеансе и хотя два разных запроса запускаются для извлечения ассоциаций, почему вторая ассоциация всегда обрабатывается как прокси-сервер (как если бы она была загружена лениво) - я говорю, что ассоциация с второй выборкой, так как, как упомянуто @Alex, я поменялась местами запросы и та же проблема теперь связываются извлекаются вторыми. несмотря на два разных запроса (которые являются правильными), я получил это. Я думаю, что это как-то связано с сеансом управления постоянным объектом. Пожалуйста, дайте мне знать или любую статью, потому что я начинаю думать, мое понимание здесь не так.

Спасибо.

Ответы [ 2 ]

1 голос
/ 21 марта 2020

Если вы извлекаете одну и ту же сущность, используя запросы дважды, находясь в контексте одной и той же единицы постоянства, во второй раз вы получите точно такой же экземпляр этой сущности (обратите внимание, что идентификаторы объектов PlayerDetails совпадают в обоих случаях). Таким образом, JOIN FETCH во втором запросе не может иметь никакого эффекта.

Также совершенно очевидно, почему JPA был спроектирован так, чтобы никогда не допускать двух экземпляров одного и того же объекта в один контекст постоянства: если вы вызываете playerDetails.setName("John") и resultList2.setName("Gary"), какая версия «выигрывает» в постоянное время?

1 голос
/ 21 марта 2020

Зачем вам два запроса? Поля объекта должны управляться менеджером объекта. Вы можете установить поля только для сохранения в базе данных. В противном случае вы должны использовать проекцию dto вместо объекта в качестве результата запроса.

Попробуйте это

String queryString = "select p from PlayerDetails p join fetch p.battingStats bts join fetch p.bowlingStats bws where p.name = :name";
TypedQuery<PlayerDetails> query = entityManager.createQuery(queryString, PlayerDetails.class);
query.setParameter("name", name);

return Optional.ofNullable(query.getSingleResult());
...