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

Viewed 1787

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 next method:

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

    @EntityGraph(value = "product-excel-entity-graph")
    List<Product> 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<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 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 = "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 single value:

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

and

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

How to fix it?

1 Answers

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<Product, Long> {

@EntityGraph(attributePaths = {"productCategoryList" , 
"productImageList" })
List<Product> 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 = {""userCategories" ,"userCategories.userSubCategories"})
List<BnbUser> 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

Related