简体   繁体   English

JPQL 查询导致多次往返数据库甚至 EAGER 和 JOIN FETCH

[英]JPQL Query results in multiple roundtrips to database even EAGER and JOIN FETCH

Im trying to understand why my query results in 2 calls to database.我试图理解为什么我的查询会导致 2 次调用数据库。 In my understanding I have an EAGER loading with FETCH keyword in the query that should result in one roundtrip, but in below this isnt the case.. Grateful for any tips!据我了解,我在查询中使用 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);

The Recipe Class:配方Class:

@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;
}

First Join table RecipeIngredient:首先加入表 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;
.....
}

Second join table:第二个连接表:

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

The JPQL query results in following two calls to DB both containing left outer join to table users : JPQL 查询导致以下两次对 DB 的调用都包含对表 users 的左外连接

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=?

I was hoping for a join to Users table once, not twice.. Any ideas?我希望加入 Users 表一次,而不是两次。有什么想法吗? Thanks!谢谢!

Looks like the second query is for Recipe recipe here看起来第二个查询是这里的Recipe recipe

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

Just use FetchType.LAZY只需使用FetchType.LAZY

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

You would not have the second query, if you use entityManager.find() method.如果您使用entityManager.find()方法,则不会有第二个查询。 The recipe will be already in the cache. recipe将已经在缓存中。

But for JPQL Hibernate considers that recipe , that is found in the first query, is not in the cache, so it gets it again (even it is the same recipe ).但是对于 JPQL Hibernate 认为在第一个查询中找到的recipe不在缓存中,所以它再次获取它(即使它是相同的recipe )。

Advice建议

Always use lazy loading everywhere.始终在任何地方使用延迟加载。 It is impossible to disable eager loading in runtime.在运行时禁用预加载是不可能的。 Also, it will be very hard to test everything, if you will want to change eager to lazy .此外,如果您想将eager更改为lazy ,将很难测试所有内容。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM