2016-09-15 72 views
1

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

@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 。select star0_.id as id1_2_,star0_.background_image as backgrou2_2_,star0_.created as created3_2_,star0_.description as descript4_2_,star0_.name as name5_2_,star0_.nickname as nickname6_2_,star0_.thumbnail as thumbnai7_2_,star0_.updated as从superfan_star star0_内 updated8_2_加入关于star0_.id = medias1_.star_id其中star0_.id = 19

20160915 20 superfan_media medias1_: 52:59.173 [http-nio-8080-exec-1] DEBUG j.sqlonly - org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:82) 9. select medias0_.star_id as star_id11_2_0_,medias0_ .ID如id1_1_0_,medias0_.id如id1_1_1_,medias0_.created如created2_1_1_, medias0_.description如descript3_1_1_,medias0_.end_time如 end_time4_1_1_,medias0_.is_approve如is_appro5_1_1_, medias0_.is_approved_final如is_appro6_1_1_,medias0_.is_pushed如 is_pushe7_1_1_ ,medias0_.is_represent如is_repre8_1_1_, medias0_.length如length9_1_1_,medias0_.released如release10_1_1_, medias0_.star_id如star_id11_1_1_,medias0_.teleport_media_id如 telepor12_1_1_,medias0_.thumbnail如thumbna13_1_1_,medias0_.title 作为title14_1_1_,medias0_.work_end如work_en15_1_1_, medias0_.work_start如work_st16_1_1_,medias0_.youtube_id如 youtube17_1_1_,medias0_.youtube_title如youtube18_1_1_从 superfan_media medias0_其中medias0_.star_id = 19

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

+0

虽然是一个公认的答案,我怀疑有可能在这里玩别的东西。我注意到你有一个Lombok @Data,我认为覆盖equals和基于JPA实体中危险的所有字段的散列码,因为当关联项添加到基于散列的集合时,它可以触发大量额外的数据加载。 如果你删除了这个,会发生什么? –

+0

@AlanHay是啊,我发现龙目岛正在为列表造成问题,因为它正在为每个Star查询媒体。我试图看看是否有方法可以在不查询所有内容的情况下使用龙目岛,但似乎没有办法。 – Aesis

+0

添加为答案 –

回答

1

这按预期工作。第一个查询从数据库获取ID = 19的Star实体,第二个查询从数据库获取该实体的链接Media实体。 (仔细查看SQL语句的日志以了解正在查询的内容)。您在Star类的medias字段中指定FetchType.EAGER

注:

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

渴望获取意味着,当你对一个或多个Star对象做一个查询,马上休眠获取链接Media对象 - 而不是以延迟提取,这意味着第二个查询不会立即完成,但仅在必要时(当您访问medias成员变量时)。

+0

嗯,谢谢。我认为第一个查询的内部连接是获取对象所需的全部内容,并没有意识到需要第二个查询来链接媒体实体。 – Aesis

1

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

是的,我发现龙目岛正在为列表造成问题,因为它正在查询每个星的媒体。我试图看看是否有方法可以在不查询所有内容的情况下使用龙目岛,但似乎没有办法。

首先,我建议根据你的实体的各个领域不实施equals()hashcode():那是你的问题的根本原因,是没有意义的呢 - 立足他们独特的业务键,如果你有一个可用。实质上,如果两个实体具有相同的ID,但实质上相同,但在此处看到:

The JPA hashCode()/equals() dilemma

此外,hashCode()方法,应根据不可改变的领域 - 在这里看到:

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

龙目岛的@Data只是聚合其他个人的注释。所以,你可以将其删除,请使用个别@Getter@Setter@ToString龙目岛的注释并在需要时写平等的),你自己的理智实现(和hashCode():

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