@EntityGraph with multiple "attributePaths" or @NamedEntityGraph with multiple "attributeNodes" is not working

huangapple go评论33阅读模式
英文:

@EntityGraph with multiple "attributePaths" or @NamedEntityGraph with multiple "attributeNodes" is not working

问题

I have class Product:

@NamedEntityGraph(
    name = "product-excel-entity-graph",
    attributeNodes = {
        @NamedAttributeNode(value = "productCategoryList", subgraph = "categories"),
        @NamedAttributeNode(value = "productImageList", subgraph = "images")
    },
    subgraphs = {
        @NamedSubgraph(name = "categories", attributeNodes = {@NamedAttributeNode("category")}),
        @NamedSubgraph(name = "images", attributeNodes = {@NamedAttributeNode("image")})
    }
)
public class Product {

    @Id
    @GeneratedValue(generator = "product_seq", strategy = GenerationType.SEQUENCE)
    @SequenceGenerator(sequenceName = "product_id_seq", name = "product_seq", allocationSize = 1)
    private Long id;

    @Column(name = "name")
    private String name;

    @OneToMany(mappedBy = "product")
    @JsonIgnore
    private List<CategoryProduct> productCategoryList;

    @OneToMany(mappedBy = "product")
    @JsonIgnore
    private List<ProductImage> productImageList;

}

Class CategoryProduct:

@Entity
@Table(name = "category_product")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CategoryProduct {
    @Id
    @GeneratedValue(generator = "category_product_seq", strategy = GenerationType.SEQUENCE)
    @SequenceGenerator(sequenceName = "category_product_id_seq", name = "category_product_seq", allocationSize = 1)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "product_id", foreignKey = @ForeignKey(name = "category_product_product_fk"), nullable = false)
    private Product product;

    @ManyToOne
    @JoinColumn(name = "category_id", foreignKey = @ForeignKey(name = "category_product_category_fk"), nullable = false)
    private Category category;
}

Class Category:

@Entity
@Table(name = "category")
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString(exclude = {"categoryProductList"})
public class Category {
    @Id
    @GeneratedValue(generator = "category_seq", strategy = GenerationType.SEQUENCE)
    @SequenceGenerator(sequenceName = "category_id_seq", name = "category_seq", allocationSize = 1)
    private Long id;

    @Column(name = "name", nullable = false)
    private String name;

    @OneToMany(mappedBy = "category", fetch = FetchType.LAZY)
    @JsonIgnore
    private List<CategoryProduct> categoryProductList;
}

Class ProductImage:

@Entity
@Table(name = "product_image")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ProductImage {
    @Id
    @GeneratedValue(generator = "product_image_seq", strategy = GenerationType.SEQUENCE)
    @SequenceGenerator(sequenceName = "product_image_id_seq", name = "product_image_seq", allocationSize = 1)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "product_id", foreignKey = @ForeignKey(name = "product_image_product_fk"), nullable = false)
    private Product product;

    @ManyToOne
    @JoinColumn(name = "image_id", foreignKey = @ForeignKey(name = "product_image_image_fk"), nullable = false)
    private Image image;
}

Class Image:

@Entity
@Table(name = "image")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Image {
    @Id
    @GeneratedValue(generator = "image_seq", strategy = GenerationType.SEQUENCE)
    @SequenceGenerator(sequenceName = "image_id_seq", name = "image_seq", allocationSize = 1)
    private Long id;

    @Column(name = "img_url")
    private String imgUrl;
}

In ProductRepo I have the next method:

@Repository
public interface ProductRepo extends JpaRepository<Product, Long> {

    @EntityGraph(value = "product-excel-entity-graph")
    List<Product> findAllByIdNotNull();
}

In ProductServiceImpl, the next method:

@Service
public class ProductServiceImpl implements ProductService {

    private final ProductRepo productRepo;
    private final CategoryRepo categoryRepo;
    private final CategoryProductRepo categoryProductRepo;
    private final ImageRepo imageRepo;
    private final ProductImageRepo productImageRepo;

    @Autowired
    public ProductServiceImpl(ProductRepo productRepo,
                              CategoryRepo categoryRepo,
                              CategoryProductRepo categoryProductRepo,
                              ImageRepo imageRepo,
                              ProductImageRepo productImageRepo) {
        this.productRepo = productRepo;
        this.categoryRepo = categoryRepo;
        this.categoryProductRepo = categoryProductRepo;
        this.imageRepo = imageRepo;
        this.productImageRepo = productImageRepo;
    }

    @Override
    public List<Product> getAllProducts() throws Exception {
        try {
            List<Product> products = productRepo.findAllByIdNotNull();
            if (products.isEmpty()) {
                throw new Exception("NO PRODUCT");
            }
            return products;
        } catch (Throwable t) {
            throw new Exception(t.getMessage());
        }
    }
}

The problem is when I am trying to get rows through the productService.getAllProducts(), request returns a 400 status. After I moved repo method call into try-catch, now it just shows a NullPointerException. But it works when I use "attributeNodes" with a single value, the same as code below:

@NamedEntityGraph(
    name = "product-excel-entity-graph",
    attributeNodes = @NamedAttributeNode(value = "productCategoryList", subgraph = "categories"),
    subgraphs = @NamedSubgraph(name = "categories", attributeNodes = {@NamedAttributeNode("category")})
)

or:

@NamedEntityGraph(
    name = "product-excel-entity-graph",
    attributeNodes = @NamedAttributeNode(value = "productImageList", subgraph = "images"),
    subgraphs = @NamedSubgraph(name = "images", attributeNodes = {@NamedAttributeNode("image")})
)

The same problem also with @EntityGraph. It does not work with multiple values as in code below:

@EntityGraph(attributePaths = {"productCategoryList", "productImageList"})
List<Product> findAll();

But works with a single value:

@EntityGraph(attributePaths = {"productCategoryList"})
List<Product> findAll();

and

@EntityGraph(attributePaths = {"productImageList"})
List<Product> findAll();

How to fix it?

英文:

I have class Product:

@NamedEntityGraph(
        name = &quot;product-excel-entity-graph&quot;,
        attributeNodes = {
                @NamedAttributeNode(value = &quot;productCategoryList&quot;, subgraph = &quot;categories&quot;),
                @NamedAttributeNode(value = &quot;productImageList&quot;, subgraph = &quot;images&quot;)
        }
        ,
        subgraphs = {
                @NamedSubgraph(name = &quot;categories&quot;, attributeNodes = {@NamedAttributeNode(&quot;category&quot;)}),
                @NamedSubgraph(name = &quot;images&quot;, attributeNodes = {@NamedAttributeNode(&quot;image&quot;)})
        }
)
public class Product {

    @Id
    @GeneratedValue(generator = &quot;product_seq&quot;, strategy = GenerationType.SEQUENCE)
    @SequenceGenerator(sequenceName = &quot;product_id_seq&quot;, name = &quot;product_seq&quot;, allocationSize = 1)
    private Long id;

    @Column(name = &quot;name&quot;)
    private String name;

    @OneToMany(mappedBy = &quot;product&quot;)
    @JsonIgnore
    private List&lt;CategoryProduct&gt; productCategoryList;

    @OneToMany(mappedBy = &quot;product&quot;)
    @JsonIgnore
    private List&lt;ProductImage&gt; productImageList;

}

Class CategoryProduct:

@Entity
@Table(name = &quot;category_product&quot;)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CategoryProduct {
    @Id
    @GeneratedValue(generator = &quot;category_product_seq&quot;, strategy = GenerationType.SEQUENCE)
    @SequenceGenerator(sequenceName = &quot;category_product_id_seq&quot;, name = &quot;category_product_seq&quot;, allocationSize = 1)
    private Long id;

    @ManyToOne
    @JoinColumn(name = &quot;product_id&quot;, foreignKey = @ForeignKey(name = &quot;category_product_product_fk&quot;), nullable = false)
    private Product product;

    @ManyToOne
    @JoinColumn(name = &quot;category_id&quot;, foreignKey = @ForeignKey(name = &quot;category_product_category_fk&quot;), nullable = false)
    private Category category;
}

Class Category:

@Entity
@Table(name = &quot;category&quot;)
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString(exclude = {&quot;categoryProductList&quot;})
public class Category {
    @Id
    @GeneratedValue(generator = &quot;category_seq&quot;, strategy = GenerationType.SEQUENCE)
    @SequenceGenerator(sequenceName = &quot;category_id_seq&quot;, name = &quot;category_seq&quot;, allocationSize = 1)
    private Long id;

    @Column(name = &quot;name&quot;, nullable = false)
    private String name;

    @OneToMany(mappedBy = &quot;category&quot;, fetch = FetchType.LAZY)
    @JsonIgnore
    private List&lt;CategoryProduct&gt; categoryProductList;
}

Class ProductImage:

@Entity
@Table(name = &quot;product_image&quot;)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ProductImage {
    @Id
    @GeneratedValue(generator = &quot;product_image_seq&quot;, strategy = GenerationType.SEQUENCE)
    @SequenceGenerator(sequenceName = &quot;product_image_id_seq&quot;, name = &quot;product_image_seq&quot;, allocationSize = 1)
    private Long id;

    @ManyToOne
    @JoinColumn(name = &quot;product_id&quot;, foreignKey = @ForeignKey(name = &quot;product_image_product_fk&quot;), nullable = false)
    private Product product;

    @ManyToOne
    @JoinColumn(name = &quot;image_id&quot;, foreignKey = @ForeignKey(name = &quot;product_image_image_fk&quot;), nullable = false)
    private Image image;
}

Class Image:

@Entity
@Table(name = &quot;image&quot;)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Image {
    @Id
    @GeneratedValue(generator = &quot;image_seq&quot;, strategy = GenerationType.SEQUENCE)
    @SequenceGenerator(sequenceName = &quot;image_id_seq&quot;, name = &quot;image_seq&quot;, allocationSize = 1)
    private Long id;

    @Column(name = &quot;img_url&quot;)
    private String imgUrl;
}

In ProductRepo I have next method:

@Repository
public interface ProductRepo extends JpaRepository&lt;Product, Long&gt; {

    @EntityGraph(value = &quot;product-excel-entity-graph&quot;)
    List&lt;Product&gt; findAllByIdNotNull();
}

In ProductServiceImpl next method:

@Service
public class ProductServiceImpl implements ProductService {

    private final ProductRepo productRepo;
    private final CategoryRepo categoryRepo;
    private final CategoryProductRepo categoryProductRepo;
    private final ImageRepo imageRepo;
    private final ProductImageRepo productImageRepo;

    @Autowired
    public ProductServiceImpl(ProductRepo productRepo,
                              CategoryRepo categoryRepo,
                              CategoryProductRepo categoryProductRepo,
                              ImageRepo imageRepo,
                              ProductImageRepo productImageRepo) {
        this.productRepo = productRepo;
        this.categoryRepo = categoryRepo;
        this.categoryProductRepo = categoryProductRepo;
        this.imageRepo = imageRepo;
        this.productImageRepo = productImageRepo;
    }

    @Override
    public List&lt;Product&gt; getAllProducts() throws Exception {
        try{
            List&lt;Product&gt; products = productRepo.findAllByIdNotNull();
            if (products.isEmpty()) {
                throw new Exception(&quot;NO PRODUCT&quot;);
            }
            return products;
        } catch (Throwable t){
            throw new Exception(t.getMessage());
        }
    }
}

The problem is when I am trying to get rows through the productService.getAllProducts(), request returns 400 status. After I moved repo method call into try-catch, now it just shows NullPointerException. But it works when I use "attributeNodes" with single value same as code below:

@NamedEntityGraph(
        name = &quot;product-excel-entity-graph&quot;,
        attributeNodes = @NamedAttributeNode(value = &quot;productCategoryList&quot;, subgraph = &quot;categories&quot;),
        subgraphs = @NamedSubgraph(name = &quot;categories&quot;, attributeNodes = {@NamedAttributeNode(&quot;category&quot;)})
)

or:

@NamedEntityGraph(
        name = &quot;product-excel-entity-graph&quot;,
        attributeNodes = @NamedAttributeNode(value = &quot;productImageList&quot;, subgraph = &quot;images&quot;),
        subgraphs = @NamedSubgraph(name = &quot;images&quot;, attributeNodes = {@NamedAttributeNode(&quot;image&quot;)})
)

The same problem also with @EntityGraph. It does not work with multiple values as in code below:

@EntityGraph(attributePaths = {&quot;productCategoryList&quot;, &quot;productImageList&quot;})
    List&lt;Product&gt; findAll();

But works with single value:

@EntityGraph(attributePaths = {&quot;productCategoryList&quot;})
    List&lt;Product&gt; findAll();

and

@EntityGraph(attributePaths = {&quot;productImageList&quot;})
    List&lt;Product&gt; findAll();

How to fix it?

答案1

得分: 1

The solution is fairly simple use JPA 2.1 where you need add entity graph at JPA repository only.

@Repository
public interface ProductRepo extends JpaRepository<Product, Long> {

    @EntityGraph(attributePaths = {"productCategoryList" , "productImageList" })
    List<Product> findAllByIdNotNull();
}

This will create a join between the bi-directional mapping of Product with CategoryProduct and ProductImage.

For nested level, you can do this.

@EntityGraph(attributePaths = {"userCategories", "userCategories.userSubCategories"})
List<BnbUser> findAll();

You need to override the equals method in each of these entity classes with an ID; otherwise, this will not work. You can get rid of complex multilevel NamedEntityGraph at each entity class, which is not needed.

英文:

The solution is fairly simple use JPA 2.1 where you need add entiry grapgh at JPA repository only

@Repository
public interface ProductRepo extends JpaRepository&lt;Product, Long&gt; {

@EntityGraph(attributePaths = {&quot;productCategoryList&quot; , 
&quot;productImageList&quot; })
List&lt;Product&gt; findAllByIdNotNull();
}

This will create Join between the bi-directional mapping of Product with CategoryProduct and ProductImage

For nested level you can do this

@EntityGraph(attributePaths = {&quot;&quot;userCategories&quot; ,&quot;userCategories.userSubCategories&quot;})
List&lt;BnbUser&gt; findAll();

> Blockquote you need to override equals method in each of these entity classes with ID else this will not work. You can get rid of complex multilevel NamedEntityGraph at each entity class which is not needed

huangapple
  • 本文由 发表于 2020年8月1日 19:50:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/63204876.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定