【问题标题】:JPQL Query results in multiple roundtrips to database even EAGER and JOIN FETCHJPQL 查询导致多次往返数据库甚至 EAGER 和 JOIN FETCH
【发布时间】:2020-12-21 00:04:57
【问题描述】:

我试图了解为什么我的查询会导致 2 次数据库调用。据我了解,我在查询中使用 FETCH 关键字进行了 EAGER 加载,这应该会导致一次往返,但在下面情况并非如此。感谢任何提示!


        TypedQuery<Recipe> query = em.createQuery("SELECT r FROM Recipe r" +
                "  LEFT JOIN FETCH r.ingredients ri LEFT JOIN FETCH r.author a WHERE r.id= :id ", Recipe.class);
        
        query.setParameter("id", id);

配方类:

@Entity
@Table(name = "recipes")
@Getter
@Setter
@NoArgsConstructor
@JsonIdentityInfo(
        generator = ObjectIdGenerators.PropertyGenerator.class,
        property = "id")
@JsonSerialize(using = RecipeMetaSerializer.class)
public class Recipe implements Serializable {

    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Id
    private Long id;
    
    
    @ManyToOne(fetch = FetchType.EAGER)
    private User author;


    @OneToMany(
            mappedBy = "recipe",
            orphanRemoval = true,
            fetch = FetchType.LAZY,
            cascade = CascadeType.PERSIST
    )
    private List<RecipeIngredient> ingredients;
}

第一个连接表RecipeIngredient:

@Entity
@Table(name="recipe_ingredients")
@Getter
@Setter
@NoArgsConstructor
@IdClass(RecipeIngredientId.class)
public class RecipeIngredient implements Serializable {
    
    @Id
    @ManyToOne(fetch= FetchType.EAGER)
    private Recipe recipe;

    @Id
    @ManyToOne(fetch= FetchType.LAZY)
    private Ingredient ingredient;
.....
}

第二个连接表:

@Entity
@Table(name = "users")
@Getter
@Setter
@NoArgsConstructor
public class User {
    
    @Id
    private Long id;
    
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "author")
    private List<Recipe> recipes;
    
}

JPQL 查询导致以下两个对 DB 的调用都包含 左外连接到表用户

select recipe0_.id as id1_6_0_, ingredient1_.ingredient_id as ingredie4_5_0__, user2_.img_url as img_url2_7_2_, user2_.username as username4_7_2_ from recipes recipe0_ **left outer join recipe_ingredients** ingredient1_ on recipe0_.id=ingredient1_.recipe_id **left outer join users** user2_ on recipe0_.author_id=user2_.id where recipe0_.id=?

select recipe0_.id as id1_6_0_, user1_.username as username4_7_1_ from recipes recipe0_ **left outer join users** user1_ on recipe0_.author_id=user1_.id where recipe0_.id=?

我希望加入用户表一次,而不是两次。有什么想法吗?谢谢!

【问题讨论】:

    标签: java spring hibernate jpa jpql


    【解决方案1】:

    看起来第二个查询是针对Recipe recipe 这里

    @Entity
    public class RecipeIngredient {
        
        @Id
        @ManyToOne(fetch= FetchType.EAGER)
        private Recipe recipe;
     
    }
    

    只需使用FetchType.LAZY

    @Entity
    public class RecipeIngredient {
        
        @Id
        @ManyToOne(fetch= FetchType.LAZY)
        private Recipe recipe;
     
    }
    

    如果您使用entityManager.find() 方法,您将不会有第二个查询。 recipe 将已经在缓存中。

    但是对于 JPQL,Hibernate 认为在第一个查询中找到的 recipe 不在缓存中,所以它再次获取它(即使它是相同的 recipe)。

    建议

    始终在任何地方使用延迟加载。在运行时禁用急切加载是不可能的。此外,如果您想将 eager 更改为 lazy,那么测试所有内容将非常困难。

    https://vladmihalcea.com/eager-fetching-is-a-code-smell

    【讨论】:

    • 谢谢!那行得通,我显然仍然没有得到 JPQL 的所有好处
    猜你喜欢
    • 2014-12-17
    • 2015-07-17
    • 2013-11-13
    • 2019-11-21
    • 2013-01-11
    • 2012-05-16
    • 2019-03-25
    • 2016-07-10
    • 1970-01-01
    相关资源
    最近更新 更多