如何对多个实体使用单个 JPA 规范 class 和方法
How to use single JPA specification class and methods for multiple entities
我正在创建一个 Spring 启动应用程序,它具有诸如 Product、Category、Machinery、UsageLocation 等实体。所有这些实体的共同点是它们都有一个名为 name 的 String 属性,并且可以是使用名称从 UI 过滤。我已经编写了一个使用名称过滤的产品规范,它正在运行。下面是代码
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;
}
}
现在的问题是我必须再次编写相同的代码来过滤其他实体,唯一的区别是 class 名称(产品)、字段名称(PRODUCT_NAME)和 return方法的类型。我可以创建一个通用 class 和方法,我可以将 class 名称和字段名称作为参数传递给它,并且它 return 各自 return 类型的规范。
首先使您的 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;
}
}
然后在控制器@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;
}
您可以为 SpecificationsBuilder 创建您自己的通用库,我有一个。您可以找到详细信息 here
我能够使用 Abinash 的回答解决这个问题。下面是可重用规范的工作代码 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);
}
}
这是实体规范的代码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;
}
我正在创建一个 Spring 启动应用程序,它具有诸如 Product、Category、Machinery、UsageLocation 等实体。所有这些实体的共同点是它们都有一个名为 name 的 String 属性,并且可以是使用名称从 UI 过滤。我已经编写了一个使用名称过滤的产品规范,它正在运行。下面是代码
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;
}
}
现在的问题是我必须再次编写相同的代码来过滤其他实体,唯一的区别是 class 名称(产品)、字段名称(PRODUCT_NAME)和 return方法的类型。我可以创建一个通用 class 和方法,我可以将 class 名称和字段名称作为参数传递给它,并且它 return 各自 return 类型的规范。
首先使您的 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;
}
}
然后在控制器@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;
}
您可以为 SpecificationsBuilder 创建您自己的通用库,我有一个。您可以找到详细信息 here
我能够使用 Abinash 的回答解决这个问题。下面是可重用规范的工作代码 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);
}
}
这是实体规范的代码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;
}