k13*_*13i 4 java hibernate jpa eager-loading spring-data
我有两个具有单向one to many关系的实体。
@Entity
public class Basket {
@Id
@GeneratedValue
private Long id;
private int capacity;
}
@Entity
public class Item {
@Id
@GeneratedValue
private Long id;
private String name;
@ManyToOne
private Basket basket;
}
Run Code Online (Sandbox Code Playgroud)
我保存了两个对象:
Basket basket1 = new Basket(100);
Basket basket2 = new Basket(200);
Basket basket3 = new Basket(300);
basketRepository.save(asList(basket1, basket2, basket3));
Item item1 = new Item("item1", basket1);
Item item11 = new Item("item11", basket1);
Item item2 = new Item("item2", basket2);
Item item22 = new Item("item22", basket2);
Item item3 = new Item("item3", basket3);
Item item33 = new Item("item33", basket3);
itemRepository.save(asList(item1, item11, item2, item22, item3, item33));
// Loading one item. Basket fetched eagerly.
itemRepository.findOne(1L);
// Loading many items. Baskets are not loaded (n+1 select problem).
itemRepository.findAll();
Run Code Online (Sandbox Code Playgroud)
@ManyToOne注释eager fetch默认使用。当我Item使用加载一个时findOne(),Hibernate 使用生成查询,left outer join并Basket在同一查询中获取。但是,当我使用findAll()Hibernate 时,它首先获取所有内容Items,然后执行N selects(每个执行一个Basket),从而导致(n+1) select problem。为什么Hiberante不急于Basket使用findAll()method来获取对象以及如何解决这个问题?
根据JPA 2.0规范,@ ManyToOne默认为EAGER。
现在,当您使用findAll()它时,就相当于触发了JPQL查询entityManager.createQuery(...),并且默认情况下,它items首先加载第一个查询,随后each item又加载该basket实体并导致N + 1问题。
您可以采用以下两种方法之一:
覆盖通过在方法上指定@Query注释findAll而使用的默认查询,并将查询与join类似使用select i from Item i left join fetch i.basket。
在类上使用@NamedEntityGraph命名basket,Item并指定Item需要急于加载图形的哪一部分。在findAll方法上,使用@EntityGraph(value = "basket")。请注意,根据spring jpa实体图,我们还可以使用attributePath通过定义临时实体图@EntityGraph without the need of having to explicitly add @NamedEntityGraph to your domain types。
您可以在存储库中使用 @Query 注释覆盖 findAll 方法。下面是示例代码
public interface ItemRepository extends CrudRepository<Item, Long> {
@Override
@Query("select item from Item item left join fetch item.basket")
Iterable<Item> findAll();
}
Run Code Online (Sandbox Code Playgroud)
然后你可以记录你的sql查询,看看只做了一个查询
Hibernate: select item0_.id as id1_1_0_, basket1_.id as id1_0_1_, item0_.basket_id as basket_i3_1_0_, item0_.name as name2_1_0_, basket1_.capacity as capacity2_0_1_ from item item0_ left outer join basket basket1_ on item0_.basket_id=basket1_.id
Run Code Online (Sandbox Code Playgroud)
而在此之前
2018-03-09 13:26:52.269 INFO 4268 --- [ main] o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactory
Hibernate: select item0_.id as id1_1_, item0_.basket_id as basket_i3_1_, item0_.name as name2_1_ from item item0_
Hibernate: select basket0_.id as id1_0_0_, basket0_.capacity as capacity2_0_0_ from basket basket0_ where basket0_.id=?
Hibernate: select basket0_.id as id1_0_0_, basket0_.capacity as capacity2_0_0_ from basket basket0_ where basket0_.id=?
Hibernate: select basket0_.id as id1_0_0_, basket0_.capacity as capacity2_0_0_ from basket basket0_ where basket0_.id=?
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3986 次 |
| 最近记录: |