为什么Hibernate要查询两次?

时间:2016-09-15 11:56:43

标签: java spring hibernate jpa

在测试JPA到Spring的实现时,我发现我的查询是查询两次而不是一次。

@Data
@Entity
@Table(name = "superfan_star")
public class Star implements Serializable
{
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(nullable = false)
    private int id;
    private String name;
    private String nickname;
    private String description;
    private String thumbnail;
    private String backgroundImage;
    private Date created;
    private Date updated;

    @OneToMany(fetch = FetchType.EAGER)
    @JoinColumn(name = "starId", referencedColumnName = "id", insertable = false, updatable = false)
    private Set<Media> medias;
}

这是模特课。

@Service
public class SuperfanStarService
{
    @Autowired
    private StarRepository starRepository;

    @PersistenceContext
    private EntityManager em;

    @Transactional
    public List<Star> getStars()
    {
        QStar qStar = QStar.star;
        QMedia qMedia = QMedia.media;

        List<Star> stars =
                new JPAQuery(em)
                .from(qStar)
                .where(qStar.id.eq(19))
                .list(qStar);

        return stars;
    }
}

这是我的服务类。

  

20160915 20:52:59.119 [http-nio-8080-exec-1] DEBUG j.sqlonly - org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:82)       9.选择star0_.id为id1_2_,star0_.background_image为backgrou2_2_,star0_.created为created3_2_,star0_.description为   descript4_2_,star0_.name为name5_2_,star0_.nickname为   nickname6_2_,star0_.thumbnail为thumbnai7_2_,star0_.updated为   来自superfan_star star0_ inner join superfan_media的updated8_2_   medias1_ on star0_.id = medias1_.star_id其中star0_.id = 19

     

20160915 20:52:59.173 [http-nio-8080-exec-1] DEBUG j.sqlonly - org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:82)       9.选择medias0_.star_id为star_id11_2_0_,medias0_.id为id1_1_0_,medias0_.id为id1_1_1_,medias0_.created为created2_1_1_,   medias0_.description as descript3_1_1_,medias0_.end_time as   end_time4_1_1_,medias0_.is_approve as is_appro5_1_1_,   medias0_.is_approved_final as is_appro6_1_1_,medias0_.is_pushed as   is_pushe7_1_1_,medias0_.is_代表is_repre8_1_1_,   medias0_.length为length9_1_1_,medias0_。发布为release10_1_1_,   medias0_.star_id为star_id11_1_1_,medias0_.teleport_media_id as   telepor12_1_1_,medias0_.thumbnail为thumbna13_1_1_,medias0_.title   作为title14_1_1_,medias0_.work_end为work_en15_1_1_,   medias0_.work_start as work_st16_1_1_,medias0_.youtube_id as   youtube17_1_1_,medias0_.youtube_title as youtube18_1_1_ from   superfan_media medias0_ where medias0_.star_id = 19

正如您所看到的,它可能会因为反向更新而两次而不是一次查询?有没有办法让我的JPA模型只查询一次?

2 个答案:

答案 0 :(得分:1)

这可以按预期工作。第一个查询从数据库中获取id = 19的Star实体,第二个查询从数据库中获取该Media实体的链接Star实体。 (仔细查看SQL语句的日志以了解正在查询的内容)。

请注意,您在课程FetchType.EAGER的{​​{1}}字段中指定了medias

Star

Eager抓取意味着当您对一个或多个@OneToMany(fetch = FetchType.EAGER) @JoinColumn(name = "starId", referencedColumnName = "id", insertable = false, updatable = false) private Set<Media> medias; 对象进行查询时,Hibernate立即获取链接的Star对象 - 而不是延迟抓取,这意味着第二个查询将不会立即完成,但仅在必要时(当您访问Media成员变量时)。

答案 1 :(得分:1)

虽然有一个公认的答案,但我怀疑这里可能还有别的东西在玩。我注意到你有一个Lombok @Data,我认为它基于JPA实体中所有危险的字段覆盖equals()hashcode(),因为它可以触发大量附加数据在关联项目时被加载被添加到基于哈希的集合中。

是的我发现Lombok导致列表出现问题,因为它会查询每个Star的媒体。我试图看看是否有一种方法可以在不查询所有内容的情况下使用Lombok,但似乎没有办法。

首先,我建议不要根据实体的所有字段实施equals()hashcode():这是问题的根本原因,无论如何都没有意义 - 将它们建立在唯一的业务键上如果你有一个可用的。基本上两个实体是相同的,如果它们具有相同的ID,但是请参见此处:

The JPA hashCode() / equals() dilemma

此外,hashcode()应基于不可变字段 - 请参阅此处:

http://blog.mgm-tp.com/2012/03/hashset-java-puzzler/

龙目岛@Data只汇总其他个别注释。所以你可以删除它,使用单独的@Getter @Setter@ToString Lombok注释,并在需要时编写你自己的equals()和hashcode()的合理实现:

https://projectlombok.org/features/Data.html