简体   繁体   English

如何使用单个 JPA 规范 class 和多个实体的方法

[英]How to use single JPA specification class and methods for multiple entities

I am creating a Spring Boot application having entities like Product, Category, Machinery, UsageLocation etc.. Thing that is common in all these entities is that they all have a String attribute called name and can be filtered from UI using name.我正在创建一个 Spring 启动应用程序,其中包含 Product、Category、Machinery、UsageLocation 等实体。所有这些实体的共同点是它们都有一个名为 name 的字符串属性,并且可以使用 name 从 UI 中过滤。 I have written a specification for product to filter using name and it is working.我已经为使用名称过滤的产品编写了一个规范,它正在工作。 Below is the code下面是代码

public final class ProductSpecifications 
{

    public static Specification<Product> whereNameContains(String name)
    {
        Specification<Product> finalSpec = (Root<Product> root, CriteriaQuery<?> query, CriteriaBuilder cb)
            -> cb.like(root.get(Product_.PRODUCT_NAME), "%"+name+"%");
        return finalSpec;
    }

    public static Specification<Product> whereNameEqauls(String name)
    {
        Specification<Product> finalSpec = (Root<Product> root, CriteriaQuery<?> query, CriteriaBuilder cb)
            -> cb.equal(root.get(Product_.PRODUCT_NAME), name);
        return finalSpec;
    }
}

Now problem is that I have to write same code again for filtering other entities with only difference being the class name(Product), field name(PRODUCT_NAME) and return type of method.现在的问题是我必须再次编写相同的代码来过滤其他实体,唯一的区别是 class 名称(产品)、字段名称(产品名称)和方法的返回类型。 Can I create a generic class and method to which I can pass class name and field name as parameters and it returns specification of respective return type.我可以创建一个通用的 class 和方法,我可以将 class 名称和字段名称作为参数传递给它,它返回相应返回类型的规范。

First make your SpecificationsBuilder generic首先使您的SpecificationsBuilder通用

@Service
public final class SpecificationsBuilder<T>
{

    public static Specification<T> whereNameContains(String key,String name)
    {
        Specification<T> finalSpec = (Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb)
            -> cb.like(root.get(key), "%"+name+"%");
        return finalSpec;
    }
}

Then in controller @Autowire the SpecificationsBuilder然后在 controller @Autowire SpecificationsBuilder

@Autowire
private final SpecificationBuilder<Product> specificationBuilder;

public List<Product> getAll(String name) {
    Specification<Product> specification =
        specificationBuilder.whereNameContains(Product_.PRODUCT_NAME, name);
    List<Product> products = productRepo.findAll(specification);// pass the specifications
    return products;
  }

You can create your own generic library for SpecificationsBuilder, I have one.您可以为 SpecificationsBuilder 创建自己的通用库,我有一个。 You can find details here你可以在这里找到详细信息

I was able to solve this issue using Abinash's answer.我能够使用 Abinash 的回答来解决这个问题。 Below is the working code for reusable specification class以下是可重用规范 class 的工作代码

import java.util.List;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import com.ec.application.model.Product;
import com.ec.common.Filters.FilterAttributeData;
import com.ec.common.Filters.FilterDataList;

public class SpecificationsBuilder<T>
{

      //#######################################/#################//
     //      Level 0 - If the field that you want to query is parent entity               //
    //########################################################//    

    public Specification<T> whereDirectFieldContains(String key,List<String> names)
    {
        Specification<T> finalSpec = null;
        for(String name:names)
        {
            Specification<T> internalSpec = (Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb)
                    -> cb.like(root.get(key), "%"+ name  +"%");
            finalSpec  = specOrCondition(finalSpec,internalSpec); // to append specifications as I am filtering based on list of strings
        }
        return finalSpec;
    }

      //#######################################//
     //     Level 1 - If you want to query a child entity.          //                //
    //#######################################//

    public Specification<T> whereChildFieldContains(String childTable, String childFiledName,
            List<String> names) 
    {
        Specification<T> finalSpec = null;
        for(String name:names)
        {
            Specification<T> internalSpec = (Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb)
                    -> cb.like(root.get(childTable).get(childFiledName), "%"+ name  +"%"); 
            finalSpec  = specOrCondition(finalSpec,internalSpec);  // to append specifications as I am filtering based on list of strings
        }
        return finalSpec;
    }

      //#######################################//
     //     Reusable Spec Setter to handle  NULLs.               //
    //#######################################//

    public Specification<T> specAndCondition(Specification<T> finalSpec, Specification<T> internalSpec) 
    {
        if(finalSpec == null) return internalSpec;
        else return finalSpec.and(internalSpec);
    }

    public Specification<T> specOrCondition(Specification<T> finalSpec, Specification<T> internalSpec) 
    {
        if(finalSpec == null) return internalSpec;
        else return finalSpec.or(internalSpec);
    } 
}

And this is the code for entity specification class这是实体规范 class 的代码


public static Specification<Product> getSpecification(FilterDataList filterDataList)
    {
        List<String> productNames = specbldr.fetchValueFromFilterList(filterDataList,"product");
        List<String> categoryNames = specbldr.fetchValueFromFilterList(filterDataList,"category");
        Specification<Product> finalSpec = null;
        if(productNames != null && productNames.size()>0)
            finalSpec = specbldr.specAndCondition(finalSpec, specbldr.whereDirectFieldContains(Product_.PRODUCT_NAME, productNames));

        if(categoryNames != null && categoryNames.size()>0)
        {
            finalSpec = specbldr.specAndCondition(finalSpec,
                    specbldr.whereChildFieldContains(Product_.CATEGORY,Category_.CATEGORY_NAME, categoryNames));
        }   
        return finalSpec;   
    }


声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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