组装自己的 MySpecification
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import lombok.Getter;
import org.springframework.data.jpa.domain.Specification;
import javax.persistence.criteria.*;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Getter
public class MySpecification<T> {
private final Map<Class<?>, MySpecificationJoin> mySpecificationJoinMap;
private final Map<Class<?>, Join> joineMap;
private Specification<T> specification;
public MySpecification() {
this.specification = Specification.where(null);
this.mySpecificationJoinMap = new ConcurrentHashMap<>();
this.joineMap = new ConcurrentHashMap<>();
}
/**
* 连表
*
* @param joinClass 联接类
* @param joinType 联接类型
* @return {@link MySpecification}<{@link T}>
*/
public MySpecification<T> join(Class<?> joinClass, JoinType joinType) {
return this.join(joinClass, null, joinType);
}
/**
* 连表
*
* @param joinClass 联接类
* @param joinFieldName 联接字段名称
* @param joinType 联接类型
* @return {@link MySpecification}<{@link T}>
*/
public MySpecification<T> join(Class<?> joinClass, String joinFieldName, JoinType joinType) {
String joinClassName = StrUtil.lowerFirst(joinClass.getSimpleName());
MySpecificationJoin join = MySpecificationJoin.builder().joinClass(joinClass).joinClassName(joinClassName).joinType(joinType).build();
if (EmptyUtil.isNotEmpty(joinFieldName)) {
join.setJoinFieldName(joinFieldName);
join.setJoinColumnName(StrUtil.toUnderlineCase(joinFieldName));
}
this.mySpecificationJoinMap.put(joinClass, join);
return this;
}
/**
* 获取联接类型
*
* @param joinClass 联接类
* @return {@link JoinType}
*/
private <K> Join<T, K> getJoin(Root<T> root, Class<K> joinClass) {
if (EmptyUtil.isEmpty(joinClass)) {
return null;
}
Join<T, K> join = joineMap.get(joinClass);
if (EmptyUtil.isNotEmpty(join)) {
return join;
}
MySpecificationJoin mySpecificationJoin = mySpecificationJoinMap.get(joinClass);
KeduAssert.isNotEmpty(mySpecificationJoin, ErrorCode.MY_SPECIFICATION_JOIN_IS_NULL, joinClass.getSimpleName());
if (EmptyUtil.isNotEmpty(mySpecificationJoin.getJoinFieldName())) {
join = root.join(mySpecificationJoin.getJoinFieldName(), mySpecificationJoin.getJoinType());
} else {
join = root.join(mySpecificationJoin.getJoinClassName(), mySpecificationJoin.getJoinType());
}
joineMap.put(joinClass, join);
return join;
}
/**
* 模糊查询
*
* @param fieldName 字段名称
* @param value 价值
* @return {@link MySpecification}<{@link T}>
*/
public MySpecification<T> like(String fieldName, String value) {
return this.like(null, fieldName, value, EmptyUtil.isBizAllNotEmpty(fieldName, value));
}
/**
* 模糊查询
*
* @param fieldName 字段名称
* @param value 价值
* @param condition 条件
* @return {@link MySpecification}<{@link T}>
*/
public MySpecification<T> like(String fieldName, String value, Boolean condition) {
return this.like(null, fieldName, value, condition);
}
/**
* 模糊查询
*
* @param joinClass 联接类
* @param fieldName 字段名称
* @param value 价值
* @return {@link MySpecification}<{@link T}>
*/
public MySpecification<T> like(Class<?> joinClass, String fieldName, String value) {
return this.like(joinClass, fieldName, value, EmptyUtil.isBizAllNotEmpty(fieldName, value));
}
/**
* 模糊查询
*
* @param joinClass 联接类
* @param fieldName 字段名称
* @param value 价值
* @param condition 条件
* @return {@link MySpecification}<{@link T}>
*/
public <K> MySpecification<T> like(Class<K> joinClass, String fieldName, String value, Boolean condition) {
if (BooleanUtil.isTrue(condition)) {
Specification<T> spec = (Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) -> {
Join<T, K> join = this.getJoin(root, joinClass);
Expression<String> fieldExpression;
if (EmptyUtil.isBizEmpty(join)) {
fieldExpression = root.get(fieldName);
} else {
fieldExpression = join.get(fieldName);
}
return cb.like(fieldExpression, "%" + value + "%");
};
this.specification = this.specification.and(spec);
}
return this;
}
/**
* 相等
*
* @param fieldName 字段名称
* @param value 价值
* @return {@link MySpecification}<{@link T}>
*/
public MySpecification<T> eq(String fieldName, Object value) {
return this.eq(null, fieldName, value, EmptyUtil.isBizAllNotEmpty(fieldName, value));
}
/**
* 相等
*
* @param fieldName 字段名称
* @param value 值
* @param condition 条件
* @return {@link MySpecification}<{@link T}>
*/
public MySpecification<T> eq(String fieldName, Object value, Boolean condition) {
return this.eq(null, fieldName, value, condition);
}
/**
* 相等
*
* @param joinClass 联接类
* @param fieldName 字段名称
* @param value 值
* @return {@link MySpecification}<{@link T}>
*/
public MySpecification<T> eq(Class<?> joinClass, String fieldName, Object value) {
return this.eq(joinClass, fieldName, value, EmptyUtil.isBizAllNotEmpty(fieldName, value));
}
/**
* 相等
*
* @param joinClass 联接类
* @param fieldName 字段名称
* @param value 价值
* @param condition 条件
* @return {@link MySpecification}<{@link T}>
*/
public <K> MySpecification<T> eq(Class<K> joinClass, String fieldName, Object value, Boolean condition) {
if (BooleanUtil.isTrue(condition)) {
Specification<T> spec = (Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) -> {
Join<T, K> join = this.getJoin(root, joinClass);
Expression<String> fieldExpression;
if (EmptyUtil.isBizEmpty(join)) {
fieldExpression = root.get(fieldName);
} else {
fieldExpression = join.get(fieldName);
}
return cb.equal(fieldExpression, value);
};
this.specification = this.specification.and(spec);
}
return this;
}
/**
* IN
*
* @param fieldName 字段名称
* @param values 价值
* @return {@link MySpecification}<{@link T}>
*/
public <J> MySpecification<T> in(String fieldName, Collection<J> values) {
return this.in(null, fieldName, values, EmptyUtil.isBizAllNotEmpty(fieldName, values));
}
/**
* IN
*
* @param fieldName 字段名称
* @param values 价值
* @param condition 条件
* @return {@link MySpecification}<{@link T}>
*/
public <J> MySpecification<T> in(String fieldName, Collection<J> values, Boolean condition) {
return this.in(null, fieldName, values, condition);
}
/**
* IN
*
* @param joinClass 联接类
* @param fieldName 字段名称
* @param values 价值
* @return {@link MySpecification}<{@link T}>
*/
public <J> MySpecification<T> in(Class<?> joinClass, String fieldName, Collection<J> values) {
return this.in(joinClass, fieldName, values, EmptyUtil.isBizAllNotEmpty(fieldName, values));
}
/**
* 模糊查询
*
* @param fieldName 字段名称
* @param joinClass 联接类
* @param values 价值观
* @param condition 条件
* @return {@link MySpecification}<{@link T}>
*/
public <K, J> MySpecification<T> in(Class<K> joinClass, String fieldName, Collection<J> values, Boolean condition) {
if (BooleanUtil.isTrue(condition)) {
Specification<T> spec = (Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) -> {
Join<T, K> join = this.getJoin(root, joinClass);
Path<J> fieldExpression;
if (EmptyUtil.isBizEmpty(join)) {
fieldExpression = root.get(fieldName);
} else {
fieldExpression = join.get(fieldName);
}
CriteriaBuilder.In<J> inClause = cb.in(fieldExpression);
values.forEach(inClause::value);
return inClause;
};
this.specification = this.specification.and(spec);
}
return this;
}
/**
* 大于
*
* @param fieldName 字段名称
* @param value 价值
* @return {@link MySpecification}<{@link T}>
*/
public <K extends Comparable<? super K>> MySpecification<T> gt(String fieldName, K value) {
return this.gt(fieldName, value, EmptyUtil.isBizAllNotEmpty(fieldName, value));
}
/**
* 大于
*
* @param fieldName 字段名称
* @param value 价值
* @return {@link MySpecification}<{@link T}>
*/
public <K extends Comparable<? super K>> MySpecification<T> gt(String fieldName, K value, Boolean condition) {
if (BooleanUtil.isTrue(condition)) {
Specification<T> spec = (Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) ->
cb.greaterThan(root.get(fieldName), value);
this.specification = this.specification.and(spec);
}
return this;
}
/**
* 大于等于
*
* @param fieldName 字段名称
* @param value 价值
* @return {@link MySpecification}<{@link T}>
*/
public <K extends Comparable<? super K>> MySpecification<T> gte(String fieldName, K value) {
return this.gte(fieldName, value, EmptyUtil.isBizAllNotEmpty(fieldName, value));
}
/**
* 大于等于
*
* @param fieldName 字段名称
* @param value 价值
* @return {@link MySpecification}<{@link T}>
*/
public <K extends Comparable<? super K>> MySpecification<T> gte(String fieldName, K value, Boolean condition) {
if (BooleanUtil.isTrue(condition)) {
Specification<T> spec = (Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) ->
cb.greaterThanOrEqualTo(root.get(fieldName), value);
this.specification = this.specification.and(spec);
}
return this;
}
/**
* 小于
*
* @param fieldName 字段名称
* @param value 价值
* @return {@link MySpecification}<{@link T}>
*/
public <K extends Comparable<? super K>> MySpecification<T> lt(String fieldName, K value) {
return this.lt(fieldName, value, EmptyUtil.isBizAllNotEmpty(fieldName, value));
}
/**
* 小于
*
* @param fieldName 字段名称
* @param value 价值
* @return {@link MySpecification}<{@link T}>
*/
public <K extends Comparable<? super K>> MySpecification<T> lt(String fieldName, K value, Boolean condition) {
if (BooleanUtil.isTrue(condition)) {
Specification<T> spec = (Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) ->
cb.lessThan(root.get(fieldName), value);
this.specification = this.specification.and(spec);
}
return this;
}
/**
* 小于等于
*
* @param fieldName 字段名称
* @param value 价值
* @return {@link MySpecification}<{@link T}>
*/
public <K extends Comparable<? super K>> MySpecification<T> lte(String fieldName, K value) {
return this.lte(fieldName, value, EmptyUtil.isBizAllNotEmpty(fieldName, value));
}
/**
* 小于等于
*
* @param fieldName 字段名称
* @param value 价值
* @return {@link MySpecification}<{@link T}>
*/
public <K extends Comparable<? super K>> MySpecification<T> lte(String fieldName, K value, Boolean condition) {
if (BooleanUtil.isTrue(condition)) {
Specification<T> spec = (Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) ->
cb.lessThan(root.get(fieldName), value);
this.specification = this.specification.and(spec);
}
return this;
}
}
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.criteria.JoinType;
/**
* MySpecification-连表
*
* @author 帅哥
* @date 2024/05/30
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class MySpecificationJoin {
/**
* 联接类
*/
private Class<?> joinClass;
/**
* 联接类名,首字母小写
*/
private String joinClassName;
/**
* 联接字段名称,驼峰格式
*/
private String joinFieldName;
/**
* 联接列名,下划线格式
*/
private String joinColumnName;
/**
* 联接类型
*/
private JoinType joinType;
}
MySpecificationJoin 支持 like、eq(等于)、in、gt(大于)、gte(大于等于)、lt(小于)、lte (小于等于)和inner 查询
举例
CostType 费用类型
import lombok.*;
import lombok.experimental.Accessors;
import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.Entity;
import javax.persistence.Table;
/**
* @author 帅哥
* @date 2024-05-15 17:46:15
*/
@Data
@Entity
@DynamicUpdate
@Table(name = "sc_cost_type")
public class CostType extends BaseData {
private static final long serialVersionUID = 1L;
/**
* 唯一标识ID
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long id;
/**
* 是否生效,1 - 生效 0 - 失效
*/
protected Boolean isEnable;
/**
* 创建时间
*/
protected Timestamp createTime;
/**
* 更新时间
*/
protected Timestamp updateTime;
/**
* 名称
*/
private String name;
/**
* 父ID,一级的父类ID为0
*/
private Long parentId;
}
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* @author 帅哥
* @date 2024-05-15 17:46:15
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel("系统配置-费用类型 列表请求参数")
public class CostTypeListReq implements Serializable {
@ApiModelProperty("名称")
private String name;
@ApiModelProperty("父ID,一级的父类ID为0")
private Long parentId;
}
public List<CostType> list(CostTypeListReq req) {
MySpecification<CostType> mySpecification = new MySpecification<>();
mySpecification.eq(OtherConstant.IS_ENABLE, true);
mySpecification.eq(getFieldName(CostType::getParentId), req.getParentId(), EmptyUtil.isNotEmpty(req.getParentId()));
mySpecification.like(getFieldName(CostType::getName), req.getName());
return costTypeRepository.findAll(mySpecification.getSpecification());
}
上例中 CostType 支持 name模糊查询,parentId 相等查询
连表查询
@Data
@Accessors(chain = true)
@Entity
@DynamicUpdate
@Table(name = "check_price")
public class CheckPrice {
private static final long serialVersionUID = 1L;
/**
* 唯一标识ID
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long id;
/**
* 是否生效,1 - 生效 0 - 失效
*/
protected Boolean isEnable;
/**
* 创建时间
*/
protected Timestamp createTime;
/**
* 更新时间
*/
protected Timestamp updateTime;
/**
* 报价申请(报价审核)ID
*/
@ManyToOne
@JoinColumn(name = "quote_apply_id")
private QuoteApply quoteApply;
@Column(name = "quote_apply_id", insertable = false, updatable = false)
private Long quoteApplyId;
/**
* 医院名称
*/
private String hospitalName;
......
}
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.DynamicUpdate;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.Where;
import javax.persistence.*;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* Created by ChenGong on 2020/11/25
*/
@Entity
@Table(name = "quote_apply")
@DynamicUpdate
@Getter
@Setter
public class QuoteApply {
/**
* 唯一标识ID
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long id;
/**
* 是否生效,1 - 生效 0 - 失效
*/
protected Boolean isEnable;
/**
* 创建时间
*/
protected Timestamp createTime;
/**
* 更新时间
*/
protected Timestamp updateTime;
/**
* 医院名称
*/
private String hospitalName;
/**
* 申请状态
*/
private String auditStatus;
......
}
/**
* @author 帅哥
* @date 2024-05-15 17:46:15
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel("核价 分页请求参数")
public class CheckPricePageReq extends PageReq implements Serializable {
@ApiModelProperty("医院名称")
private String hospitalName;
@ApiModelProperty("报价状态")
private String quoteApplyStatus;
}
public Page<CheckPrice> page(CheckPricePageReq req) {
Pageable pageable = getPageable(req);
MySpecification<CheckPrice> mySpecification = new MySpecification<>();
mySpecification.join(QuoteApply.class, JoinType.INNER);
mySpecification.eq(OtherConstant.IS_ENABLE, true);
mySpecification.like(getFieldName(CheckPrice::getHospitalName), req.getHospitalName());
mySpecification.eq(QuoteApply.class, getFieldName(QuoteApply::getAuditStatus),
return checkPriceRepository.findAll(mySpecification.getSpecification(), pageable);
}
上列中,QuoteApply 和 CheckPrice 一对多,通过check_price.quote_apply_id = quote_apply.id 关联。
医院名称 查询表 check_price.hospital_name ,报价状态 连表查询 quote_apply.audit_status