首页 > 解决方案 > Join Fetch JPQL 返回一个代理对象,与 Hibernate 一起使用

问题描述

实体:

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);

我正在使用 JOIN FETCH 获取 BattingsStats 和 Bowling Stats,查询被正确触发,第一次获取 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=?

从第一个查询中获取数据

从第二个查询中获取数据

有人可以帮我弄这个吗?

PS:错误不是hibernate,错误是Jackson尝试解析数据时;现在,我只是直接返回实体。

我的问题不是如何消除这个错误?我的问题是为什么休眠的这种行为?虽然我在同一个会话中多次获取相同的记录(正如亚历克斯所说的那样是一个糟糕的模式,我改变了它,但是由于糟糕的臭代码,我可以在这里学到一些东西!!!)我正在获取相同的记录在同一个会话中有两个不同的查询,尽管触发了两个不同的查询来获取关联,为什么第二个关联总是被视为代理(就像延迟加载一样)-我说第二个获取的关联,因为正如 @Alex 提到的,我交换了查询和相同的问题现在关联第二。尽管有两个不同的查询触发(这是正确的),但我得到了这个。我认为这与会话管理持久实体有关。请让我知道或任何文章,

谢谢。

标签: hibernatejpaspring-data-jpa

解决方案


如果您在同一持久性单元的上下文中使用查询两次获取同一实体,那么第二次您将获得该实体的完全相同的实例(请注意,PlayerDetails两种情况下的对象 ID 都是相同的)。因此,第二个查询中的 JOIN FETCH 不可能有任何效果。

也很明显,为什么 JPA 被设计为永远不允许同一实体的两个实例进入单个持久性上下文:如果您调用playerDetails.setName("John")and resultList2.setName("Gary"),哪个版本在持久化时“获胜”?


推荐阅读