简体   繁体   中英

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. 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!


        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:

@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:

@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 :

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? Thanks!

Looks like the second query is for Recipe recipe here

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

Just use 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. The recipe will be already in the cache.

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

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 .

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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