GraphQL DataLoader для двунаправленных сущностей Hibernate и проблема N+1
Я получил эти двунаправленные объекты:
@Getter
@Setter
@Entity
@Table(name = "category")
public class CategoryEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Integer id;
@Column(name = "name")
private String name;
@Column(name = "parent")
private Integer parent;
@Column(name = "product_id")
@ElementCollection
private List<Integer> productIds;
}
@Getter
@Setter
@Entity
@Table(name = "product")
public class ProductEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Integer id;
@Column(name = "productId")
private String productId;
@Column(name = "description")
private String description;
@Column(name = "item_id")
@ElementCollection
private List<Integer> itemIds;
@Column(name = "category_id")
private Integer categoryId;
}
И я получил эту схему:
extend type Query {
getProduct(id: Int!) : ProductResponse
getProducts : [ProductResponse]
getLimitedProducts(limit: Int) : [ProductResponse]
}
type ProductResponse {
id: Int!,
productId: String!,
description: String!,
category: CategoryResponse,
items: [ItemResponse]
}
type CategoryResponse {
id: Int,
name: String,
parent: Int,
products: [Int]
}
Чтобы избежать проблемы N + 1, я реализовал DataLoader для разрешения категории для каждого продукта:
private DataLoader<Integer, Category> createCategoryDataLoader() {
MappedBatchLoader<Integer, Category> categoryMappedBatchLoader =
categoryIds -> CompletableFuture.supplyAsync(
() -> categoryRepository.findAllById(categoryIds).stream()
.map(c -> conversionService.convert(c, Category.class))
.filter(Objects::nonNull)
.collect(Collectors.toMap(Category::getId, Function.identity())));
return DataLoaderFactory.newMappedDataLoader(categoryMappedBatchLoader);
}
Теперь, когда я выбираю товары без двунаправленных полей, все выглядит нормально:
{
getLimitedProducts(limit: 15) {
category {
id
}
}
}
Hibernate: select productent0_.id as id1_3_, productent0_.category_id as category2_3_, productent0_.description as descript3_3_, productent0_.product_id as product_4_3_ from product productent0_ limit ?
Hibernate: select count(productent0_.id) as col_0_0_ from product productent0_
Hibernate: select categoryen0_.id as id1_0_, categoryen0_.name as name2_0_, categoryen0_.parent as parent3_0_ from category categoryen0_ where categoryen0_.id in (? , ? , ? , ? , ?)
Но когда я добавляю [Product] для получения категории, я сталкиваюсь с проблемой N+1:
{
getLimitedProducts(limit: 15) {
category {
id,
products
}
}
}
Hibernate: select productent0_.id as id1_3_, productent0_.category_id as category2_3_, productent0_.description as descript3_3_, productent0_.product_id as product_4_3_ from product productent0_ limit ?
Hibernate: select count(productent0_.id) as col_0_0_ from product productent0_
Hibernate: select categoryen0_.id as id1_0_, categoryen0_.name as name2_0_, categoryen0_.parent as parent3_0_ from category categoryen0_ where categoryen0_.id in (? , ? , ? , ? , ?)
Hibernate: select productids0_.category_entity_id as category1_1_0_, productids0_.product_id as product_2_1_0_ from category_entity_product_ids productids0_ where productids0_.category_entity_id=?
Hibernate: select productids0_.category_entity_id as category1_1_0_, productids0_.product_id as product_2_1_0_ from category_entity_product_ids productids0_ where productids0_.category_entity_id=?
Hibernate: select productids0_.category_entity_id as category1_1_0_, productids0_.product_id as product_2_1_0_ from category_entity_product_ids productids0_ where productids0_.category_entity_id=?
Hibernate: select productids0_.category_entity_id as category1_1_0_, productids0_.product_id as product_2_1_0_ from category_entity_product_ids productids0_ where productids0_.category_entity_id=?
Hibernate: select productids0_.category_entity_id as category1_1_0_, productids0_.product_id as product_2_1_0_ from category_entity_product_ids productids0_ where productids0_.category_entity_id=?
Как можно решить этот вопрос?