-
引言
- 在使用hibernate查询数据库时需要面对的问题:
- 编写的非常多的重复代码[比如空值判断],代码是sql的3倍,
- 部分api记不住
- 不能链式构造条件
- 解决方案:
- 对hibernate进行二次封装,解决以上需求
- 源码在文章末尾,由于源码简单依赖少就没有单独推到git
- 在使用hibernate查询数据库时需要面对的问题:

-
Hibernate查询二次封装概述
- 设计思路:
- 对常用的api进行封装,只需简单调用即可实现条件构造
- 使用cglib代理对对方法进行缓存与统一调用
- 大量使用lambda与泛型解决手动写字段名称与字段入参约束,实现入参预检
- 设计了两种调用模式,
- 模式一:作为一个简单条件构造工具的封装
- 模式二:使用cglib代理实现方法的缓存与统一调用
- 设计思路:
-
实现查询操作
- 模式一
- 使用示例
- 模式一
List<UserEntity> list = userService.list((root, qu, cb) -> MyJpaUtil.create(root, qu, cb)
.equal(UserEntity::getStatus, DataStatus.NORMAL.getStatus())
.like(query::getName)
//其他条件
.order(Order.DESC(UserEntity::getCreatedTime))
.builder());
- 模式二
- 使用示例1
List<UserEntity> list = userService.list(MyJpaUtil.create(UserEntity.class)
.equal(UserEntity::getStatus,DataStatus.NORMAL.getStatus())
.like(query::getName)
//其他条件
.order(Order.DESC(UserEntity::getCreatedTime)));
- 使用示例2
MyJpaUtil<UserEntity> jpa = MyJpaUtil.create();
jpa.equal(UserEntity::getStatus, DataStatus.NORMAL.getStatus());
jpa.like(query::getName);
//其他条件
jpa.order(Order.DESC(UserEntity::getCreatedTime));
List<UserEntity> list = userService.list(jpa);
更多使用示例直接参考源码注释即可,源码在文章末尾
-
参考资料
-
cglib代理使用教程
-
hutool参考文档【 入门和安装 (oschina.io) 】
-
源码
相关依赖:
hutool,cglib[springboot自带],hibernate框架
<!--hibernate依赖-->
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
<version>1.0.2.Final</version>
</dependency>
<!--spring-jpa依赖-->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.7.15</version>
</dependency>
<!--hutool工具-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.23</version>
</dependency>
工具源码:
最底层条件封装工具
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.func.LambdaUtil;
import cn.hutool.core.util.StrUtil;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* @Author: wanger
* @Date: 2023/12/27 11:41
* @Description: 具体构造jpa查询的工具,本工具类只对具体的操作进行封装处理<br/>
* 扩展需遵循规范:<br/>
* 1.方法前面的入参时必须这四个参数predicates,root,qu,cb必须在前,后面才是自己需要的参数,通常为两种模式【数据库字段,操作值】,【数据库字段】<br/>
* 2.当前方法功能为【数据库字段 操作符 操作值】模式时,<br/>
* 2.1.方法名将标识操作符,<br/>
* 2.2.第一个入参为数据库字段,且为字符串类型<br/>
* 2.3.第二个入参为操作值,并且该操作值需要加入必要的泛型约束其类型为操作值对应的类型,如equal看为任意类型,like为字符串类型,lessThanOrEqualTo为可比较的类型<br/>
* 3.方法内部需合法地调用jpa的api<br/>
* 4.方法内部需进行必要的空指针,空值判定<br/>
* 5.<span style="color:red">禁止</span>在方法内写定制化代码,如:【<br/>
* <pre>{@code
* //定制代码
* if ("userId".equals(field)) {
* //定制逻辑
* }
* }</pre>
* 】<br/>
*/
public class JpaConditionUtil {
/**
* 排序操作【order by 字段1 排序类型,字段2 排序类型,...】
*
* @param root r
* @param qu q
* @param cb c
* @param orders 排序字段列表
* @param <T> 操作对象泛型类型
*/
@SafeVarargs
public static <T> void order(Root<T> root, CriteriaQuery<?> qu, CriteriaBuilder cb, Order<T>... orders) {
for (Order<T> order : orders) {
if (Order.ASC.equals(order.getSort())) {
JpaConditionUtil.orderAsc(root, qu, cb, LambdaUtil.getFieldName(order.getFieldFun()));
} else if (Order.DESC.equals(order.getSort())) {
JpaConditionUtil.orderDesc(root, qu, cb, LambdaUtil.getFieldName(order.getFieldFun()));
}
}
}
/**
* 顺序排序order by 字段 asc
*
* @param root r
* @param qu q
* @param cb c
* @param field 数据库字段名称
* @param <T> 操作对象泛型类型
*/
private static <T> void orderAsc(Root<T> root, CriteriaQuery<?> qu, CriteriaBuilder cb, String field) {
qu.orderBy(cb.asc(root.get(field)));
}
/**
* 逆序排序【order by 字段 desc】
*
* @param root r
* @param qu q
* @param cb c
* @param field 数据库字段名称
* @param <T> 操作对象泛型类型
*/
private static <T> void orderDesc(Root<T> root, CriteriaQuery<?> qu, CriteriaBuilder cb, String field) {
qu.orderBy(cb.desc(root.get(field)));
}
/**
* 不等于判定【字段 != 值】
*
* @param predicates p
* @param root r
* @param cb c
* @param field 数据库字段名称
* @param value 操作值
* @param <T> 操作对象泛型类型
*/
public static <T> void notEqual(List<Predicate> predicates, Root<T> root, CriteriaBuilder cb, String field, Object value) {
if (value != null) {
if (value instanceof String) {
if (StrUtil.isNotBlank(value.toString())) {
predicates.add(cb.notEqual(root.get(field), value));
}
} else {
predicates.add(cb.notEqual(root.get(field), value));
}
}
}
/**
* 等于判定【字段 = 值】
*
* @param predicates p
* @param root r
* @param cb c
* @param field 数据库字段名称
* @param value 操作值
* @param <T> 操作对象泛型类型
*/
public static <T> void equal(List<Predicate> predicates, Root<T> root, CriteriaBuilder cb, String field, Object value) {
if (value != null) {
if (value instanceof String) {
if (StrUtil.isNotBlank(value.toString())) {
predicates.add(cb.equal(root.get(field), value));
}
} else {
predicates.add(cb.equal(root.get(field), value));
}
}
}
/**
* 小于等于判定【字段 <= 值】
*
* @param predicates p
* @param root r
* @param cb c
* @param field 数据库字段名称
* @param value 操作值
* @param <T> 操作对象泛型类型
* @param <Y> 操作值类型泛型约束
*/
public static <T, Y extends Comparable<? super Y>> void lessThanOrEqualTo(List<Predicate> predicates, Root<T> root, CriteriaBuilder cb, String field, Y value) {
if (value != null) {
predicates.add(cb.lessThanOrEqualTo(root.get(field), value));
}
}
/**
* 小于判定【字段 < 值】
*
* @param predicates p
* @param root r
* @param cb c
* @param field 数据库字段名称
* @param value 操作值
* @param <T> 操作对象泛型类型
* @param <Y> 操作值类型泛型约束
*/
public static <T, Y extends Comparable<? super Y>> void lessThan(List<Predicate> predicates, Root<T> root, CriteriaBuilder cb, String field, Y value) {
if (value != null) {
predicates.add(cb.lessThan(root.get(field), value));
}
}
/**
* 大于等于判定【字段 >= 值】
*
* @param predicates p
* @param root r
* @param cb c
* @param field 数据库字段名称
* @param value 操作值
* @param <T> 操作对象泛型类型
* @param <Y> 操作值类型泛型约束
*/
public static <T, Y extends Comparable<? super Y>> void greaterThanOrEqualTo(List<Predicate> predicates, Root<T> root, CriteriaBuilder cb, String field, Y value) {
if (value != null) {
predicates.add(cb.greaterThanOrEqualTo(root.get(field), value));
}
}
/**
* 大于判定【字段 > 值】
*
* @param predicates p
* @param root r
* @param cb c
* @param field 数据库字段名称
* @param value 操作值
* @param <T> 操作对象泛型类型
* @param <Y> 操作值类型泛型约束
*/
public static <T, Y extends Comparable<? super Y>> void greaterThan(List<Predicate> predicates, Root<T> root, CriteriaBuilder cb, String field, Y value) {
if (value != null) {
predicates.add(cb.greaterThan(root.get(field), value));
}
}
/**
* 区间判定,不包含左右【字段 between startValue and endValue】
*
* @param predicates p
* @param root r
* @param cb c
* @param field 数据库字段名称
* @param startValue 起始值
* @param endValue 结束值
* @param <T> 操作对象泛型类型
* @param <Y> 操作值类型泛型约束
*/
public static <T, Y extends Comparable<? super Y>> void between(List<Predicate> predicates, Root<T> root, CriteriaBuilder cb, String field, Y startValue, Y endValue) {
if (startValue != null && endValue != null) {
predicates.add(cb.between(root.get(field), startValue, endValue));
}
}
/**
* 左右闭区间判定【字段 >= startValue and 字段 <= endValue】
*
* @param predicates p
* @param root r
* @param cb c
* @param field 数据库字段名称
* @param startValue 起始值
* @param endValue 结束值
* @param <T> 操作对象泛型类型
* @param <Y> 操作值类型泛型约束
*/
public static <T, Y extends Comparable<? super Y>> void closedInterval(List<Predicate> predicates, Root<T> root, CriteriaBuilder cb, String field, Y startValue, Y endValue) {
if (startValue != null && endValue != null) {
greaterThanOrEqualTo(predicates, root, cb, field, startValue);
lessThanOrEqualTo(predicates, root, cb, field, endValue);
}
}
/**
* 左开右闭区间判定【字段 > startValue and 字段 <= endValue】
*
* @param predicates p
* @param root r
* @param cb c
* @param field 数据库字段名称
* @param startValue 起始值
* @param endValue 结束值
* @param <T> 操作对象泛型类型
* @param <Y> 操作值类型泛型约束
*/
public static <T, Y extends Comparable<? super Y>> void openLeftAndCloseRight(List<Predicate> predicates, Root<T> root, CriteriaBuilder cb, String field, Y startValue, Y endValue) {
if (startValue != null && endValue != null) {
greaterThan(predicates, root, cb, field, startValue);
lessThanOrEqualTo(predicates, root, cb, field, endValue);
}
}
/**
* 左闭右开区间判定【字段 >= startValue and 字段 < endValue】
*
* @param predicates p
* @param root r
* @param cb c
* @param field 数据库字段名称
* @param startValue 起始值
* @param endValue 结束值
* @param <T> 操作对象泛型类型
* @param <Y> 操作值类型泛型约束
*/
public static <T, Y extends Comparable<? super Y>> void closeLeftAndOpenRight(List<Predicate> predicates, Root<T> root, CriteriaBuilder cb, String field, Y startValue, Y endValue) {
if (startValue != null && endValue != null) {
greaterThanOrEqualTo(predicates, root, cb, field, startValue);
lessThan(predicates, root, cb, field, endValue);
}
}
/**
* 包含判定【字段 in(值1,值2,...)】
*
* @param predicates p
* @param root r
* @param field 数据库字段名称
* @param value 操作值
* @param <T> 操作对象泛型类型
* @param <Y> 操作值类型泛型约束
*/
public static <T, Y extends Collection<?>> void in(List<Predicate> predicates, Root<T> root, String field, Y value) {
if (CollUtil.isNotEmpty(value)) {
//创建一个新的list防止入参为不可修改内容对象
List<Object> list = new ArrayList<>(value).stream().filter(Objects::nonNull).collect(Collectors.toList());
if (CollUtil.isNotEmpty(list)) {
predicates.add(root.get(field).in(list));
}
}
}
/**
* 包含判定【字段 not in(值1,值2,...)】
*
* @param predicates p
* @param root r
* @param field 数据库字段名称
* @param value 操作值
* @param <T> 操作对象泛型类型
* @param <Y> 操作值类型泛型约束
*/
public static <T, Y extends Collection<?>> void notIn(List<Predicate> predicates, Root<T> root, CriteriaBuilder cb, String field, Y value) {
if (CollUtil.isNotEmpty(value)) {
//创建一个新的list防止入参为不可修改内容对象
List<Object> list = new ArrayList<>(value).stream().filter(Objects::nonNull).collect(Collectors.toList());
if (CollUtil.isNotEmpty(list)) {
predicates.add(cb.not(root.get(field).in(list)));
}
}
}
/**
* 为空判定【字段 is null】
*
* @param predicates p
* @param root r
* @param cb c
* @param field 数据库字段名称
* @param <T> 操作对象泛型类型
*/
public static <T> void isNull(List<Predicate> predicates, Root<T> root, CriteriaBuilder cb, String field) {
if (StrUtil.isNotBlank(field)) {
predicates.add(cb.isNull(root.get(field)));
}
}
/**
* 不为空判定【字段 is not null】
*
* @param predicates p
* @param root r
* @param cb c
* @param field 数据库字段名称
* @param <T> 操作对象泛型类型
*/
public static <T> void isNotNull(List<Predicate> predicates, Root<T> root, CriteriaBuilder cb, String field) {
if (StrUtil.isNotBlank(field)) {
predicates.add(cb.isNotNull(root.get(field)));
}
}
/**
* 模糊匹配判定【字段 like ''】
*
* @param predicates p
* @param root r
* @param cb c
* @param field 数据库字段名称
* @param value 操作值
* @param left 是否左匹配
* @param right 是否右匹配
* @param <T> 操作对象泛型类型
*/
public static <T> void like(List<Predicate> predicates, Root<T> root, CriteriaBuilder cb, String field, String value, boolean left, boolean right) {
if (StrUtil.isNotBlank(value)) {
predicates.add(cb.like(root.get(field), (left ? "%" : "") + value + (right ? "%" : "")));
}
}
/**
* 反向模糊匹配判定【字段 not like ''】
*
* @param predicates p
* @param root r
* @param cb c
* @param field 数据库字段名称
* @param value 操作值
* @param left 是否左匹配
* @param right 是否右匹配
* @param <T> 操作对象泛型类型
*/
public static <T> void notLike(List<Predicate> predicates, Root<T> root, CriteriaBuilder cb, String field, String value, boolean left, boolean right) {
if (StrUtil.isNotBlank(value)) {
predicates.add(cb.notLike(root.get(field), (left ? "%" : "") + value + (right ? "%" : "")));
}
}
}
直接调用的工具
import cn.hutool.core.lang.func.Func0;
import cn.hutool.core.lang.func.Func1;
import cn.hutool.core.lang.func.LambdaUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.data.jpa.domain.Specification;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* @Author: wanger
* @Date: 2023/12/27 15:08
* @Description: 该工具为单表查询条件构造类,可类似MyBatis-plus的链式调用构造条件,并且不需要多余的判空操作<br/>
* 两种使用模式:<br/>
* <ol>
* <li>模式一:jpa全功能模式<br/>
* <ul>
* <li>jpa全功能模式使用相对复杂,但保留jpa原始功能比较全</li>
* <li>当前工具必须在Specification一个匿名实现类被使用,并且必须使用MyJpaUtil.create(root, qu, cb)创建,以builder()结束调用</li>
* <li>使用示例【<pre>{@code
* List<UserEntity> list = userService.list((root, qu, cb) -> MyJpaUtil.create(root, qu, cb)
* .equal(UserEntity::getStatus, DataStatus.NORMAL.getStatus())
* .like(query::getName)
* //其他条件
* .order(Order.DESC(UserEntity::getCreatedTime))
* .builder());
* }</pre>】</li>
* </ul>
* </li>
* <li>模式二:使用更加方便,但禁止调用MyJpaUtil中没有的jpa功能</li>
* <ul>
* <li>更快捷的构造jpa查询条件</li>
* <li>功能设计思路:使用cglib动态代理记录所有被调用的方法而不调用,
* 在Specification接口调用toPredicate方法时才把所有记录的方法都遍历出来一一实际调用,
* 从而避免将当前工具作为Specification接口匿名实现类的List《Predicate》构造工具,而是实际作为Specification的实现类使用</li>
* <li>为此做出的牺牲:jpa原始的Root《T》,CriteriaQuery《?》,CriteriaBuilder,List《Predicate》四个对象将被禁止在模式二中调用</li>
* <li>模式二将依赖模式一</li>
* <li>使用示例1【<pre>{@code
* List<UserEntity> list = userService.list(MyJpaUtil.create(UserEntity.class)
* .equal(UserEntity::getStatus, DataStatus.NORMAL.getStatus())
* .like(query::getName)
* //其他条件
* .order(Order.DESC(UserEntity::getCreatedTime)));
* }</pre>】</li>
* <li>使用示例2【<pre>{@code
* MyJpaUtil<UserEntity> jpa = MyJpaUtil.create();
* jpa.equal(UserEntity::getStatus, DataStatus.NORMAL.getStatus());
* jpa.like(query::getName);
* //其他条件
* jpa.order(Order.DESC(UserEntity::getCreatedTime));
* List<UserEntity> list = userService.list(jpa);
* }</pre>】</li>
* </ul>
* </ol><br/>
* 扩展需遵循规范:<br/>
* 1.当前方法功能为【数据库字段 操作符 操作值】模式时需提供两个方法,<br/>
* 1.1.方法名将标识操作符,<br/>
* 1.2.方法一为一个入参,参数为lambda函数参数入参,<br/>
* 1.3.方法二为两个入参,参数1为lambda函数入参,参数2为操作值入参,<br/>
* 2.当方法功能为【数据库字段 操作符】模式时只需提供一个方法
* 2.1.方法名将标识操作符<br/>
* 2.2.方法为一个入参,参数为lambda函数参数入参,<br/>
* 3.方法内部需遵循规范调用JpaConditionUtil中的api<br/>
* 4.方法结束需return this以保证链式调用不被中断<br/>
* 5.方法必须添加必要的字段与返回值注释,并且需要说明方法的【功能,使用示例,效果】<br/>
* 6.<span style="color:red">禁止</span>在方法内写定制化代码,如:【<br/>
* <pre>{@code
* //定制代码
* if ("userId".equals(field)) {
* //定制逻辑
* }
* }</pre>
* 】<br/>
*/
@SuppressWarnings("unused")
public class MyJpaUtil<T> implements Specification<T> {
private static final long serialVersionUID = -5346942786212881309L;
/**
* Specification接口的toPredicate方法中的Root《T》 root参数
*/
private Root<T> root;
/**
* Specification接口的toPredicate方法中的CriteriaQuery《?》 query参数
*/
private CriteriaQuery<?> query;
/**
* Specification接口的toPredicate方法中的CriteriaBuilder cb参数
*/
private CriteriaBuilder cb;
/**
* 记录jpa条件的集合
*/
private final List<Predicate> predicates = new ArrayList<>();
/**
* 方法调用代理处理类
*/
private AgentSimplification agentSimplification;
/**
* 方法调用记录
*/
private List<MethodRecord> methodRecords = new ArrayList<>();
/**
* 模糊匹配判定【字段 like ''】<br/>
* 使用示例【.like(query::getName)】<br/>
* 效果【name like '%张三%'】<br/>
*
* @param getFunc 字段与值的lambda函数
* @return this
*/
public MyJpaUtil<T> like(Func0<String> getFunc) {
JpaConditionUtil.like(predicates, root, cb, LambdaUtil.getFieldName(getFunc), getFunc.callWithRuntimeException(), true, true);
return this;
}
/**
* 模糊匹配判定【字段 like ''】<br/>
* 使用示例【.like(UserEntity::getName, "张三")】<br/>
* 效果【name like '%张三%'】<br/>
*
* @param getFunc 字段的lambda函数
* @param value 操作值
* @return this
*/
public MyJpaUtil<T> like(Func1<T, String> getFunc, String value) {
JpaConditionUtil.like(predicates, root, cb, LambdaUtil.getFieldName(getFunc), value, true, true);
return this;
}
/**
* 模糊匹配判定【字段 like ''】<br/>
* 使用示例【.likeLeft(query::getName)】<br/>
* 效果【name like '%张三'】<br/>
*
* @param getFunc 字段与值的lambda函数
* @return this
*/
public MyJpaUtil<T> likeLeft(Func0<String> getFunc) {
JpaConditionUtil.like(predicates, root, cb, LambdaUtil.getFieldName(getFunc), getFunc.callWithRuntimeException(), true, false);
return this;
}
/**
* 模糊匹配判定【字段 like ''】<br/>
* 使用示例【.likeLeft(UserEntity::getName, "张三")】<br/>
* 效果【name like '%张三'】<br/>
*
* @param getFunc 字段的lambda函数
* @param value 操作值
* @return this
*/
public MyJpaUtil<T> likeLeft(Func1<T, String> getFunc, String value) {
JpaConditionUtil.like(predicates, root, cb, LambdaUtil.getFieldName(getFunc), value, true, false);
return this;
}
/**
* 模糊匹配判定【字段 like ''】<br/>
* 使用示例【.likeRight(query::getName)】<br/>
* 效果【name like '张三%'】<br/>
*
* @param getFunc 字段与值的lambda函数
* @return this
*/
public MyJpaUtil<T> likeRight(Func0<String> getFunc) {
JpaConditionUtil.like(predicates, root, cb, LambdaUtil.getFieldName(getFunc), getFunc.callWithRuntimeException(), false, true);
return this;
}
/**
* 模糊匹配判定【字段 like ''】<br/>
* 使用示例【.likeRight(UserEntity::getName, "张三")】<br/>
* 效果【name like '张三%'】<br/>
*
* @param getFunc 字段的lambda函数
* @param value 操作值
* @return this
*/
public MyJpaUtil<T> likeRight(Func1<T, String> getFunc, String value) {
JpaConditionUtil.like(predicates, root, cb, LambdaUtil.getFieldName(getFunc), value, false, true);
return this;
}
/**
* 模糊匹配判定【字段 not like ''】<br/>
* 使用示例【.notLike(query::getName)】<br/>
* 效果【name not like '%张三%'】<br/>
*
* @param getFunc 字段与值的lambda函数
* @return this
*/
public MyJpaUtil<T> notLike(Func0<String> getFunc) {
JpaConditionUtil.notLike(predicates, root, cb, LambdaUtil.getFieldName(getFunc), getFunc.callWithRuntimeException(), true, true);
return this;
}
/**
* 模糊匹配判定【字段 not like ''】<br/>
* 使用示例【.notLike(UserEntity::getName, "张三")】<br/>
* 效果【name not like '%张三%'】<br/>
*
* @param getFunc 字段的lambda函数
* @param value 操作值
* @return this
*/
public MyJpaUtil<T> notLike(Func1<T, String> getFunc, String value) {
JpaConditionUtil.notLike(predicates, root, cb, LambdaUtil.getFieldName(getFunc), value, true, true);
return this;
}
/**
* 模糊匹配判定【字段 not like ''】<br/>
* 使用示例【.notLikeLeft(query::getName)】<br/>
* 效果【name not like '%张三'】<br/>
*
* @param getFunc 字段与值的lambda函数
* @return this
*/
public MyJpaUtil<T> notLikeLeft(Func0<String> getFunc) {
JpaConditionUtil.notLike(predicates, root, cb, LambdaUtil.getFieldName(getFunc), getFunc.callWithRuntimeException(), true, false);
return this;
}
/**
* 模糊匹配判定【字段 not like ''】<br/>
* 使用示例【.notLikeLeft(UserEntity::getName, "张三")】<br/>
* 效果【name not like '%张三'】<br/>
*
* @param getFunc 字段的lambda函数
* @param value 操作值
* @return this
*/
public MyJpaUtil<T> notLikeLeft(Func1<T, String> getFunc, String value) {
JpaConditionUtil.notLike(predicates, root, cb, LambdaUtil.getFieldName(getFunc), value, true, false);
return this;
}
/**
* 模糊匹配判定【字段 not like ''】<br/>
* 使用示例【.notLikeRight(query::getName)】<br/>
* 效果【name not like '张三%'】<br/>
*
* @param getFunc 字段与值的lambda函数
* @return this
*/
public MyJpaUtil<T> notLikeRight(Func0<String> getFunc) {
JpaConditionUtil.notLike(predicates, root, cb, LambdaUtil.getFieldName(getFunc), getFunc.callWithRuntimeException(), false, true);
return this;
}
/**
* 模糊匹配判定【字段 not like ''】<br/>
* 使用示例【.notLikeRight(UserEntity::getName, "张三")】<br/>
* 效果【name not like '张三%'】<br/>
*
* @param getFunc 字段的lambda函数
* @param value 操作值
* @return this
*/
public MyJpaUtil<T> notLikeRight(Func1<T, String> getFunc, String value) {
JpaConditionUtil.notLike(predicates, root, cb, LambdaUtil.getFieldName(getFunc), value, false, true);
return this;
}
/**
* 顺序排序操作【order by 字段 desc】<br/>
* 使用示例【.orderDesc(UserEntity::getAge))】<br/>
* 效果【order by age desc】<br/>
*
* @param getFunc 字段的lambda函数
* @return this
*/
public MyJpaUtil<T> orderDesc(Func1<T, ?> getFunc) {
return this.order(Order.DESC(getFunc));
}
/**
* 顺序排序操作【order by 字段 asc】<br/>
* 使用示例【.orderAsc(UserEntity::getAge)】<br/>
* 效果【order by age asc】<br/>
*
* @param getFunc 字段的lambda函数
* @return this
*/
public MyJpaUtil<T> orderAsc(Func1<T, ?> getFunc) {
return this.order(Order.ASC(getFunc));
}
/**
* 多值排序操作【order by 字段1 排序类型,字段2 排序类型,...】<br/>
* 使用示例【.order(Order.DESC(UserEntity::getCreatedTime), Order.ASC(UserEntity::getPhone))】<br/>
* 效果【order by created_time desc,phone asc】<br/>
*
* @param orders 排序条件数组
* @return this
*/
public MyJpaUtil<T> order(Order<T>... orders) {
JpaConditionUtil.order(root, query, cb, orders);
return this;
}
/**
* 为空判定【字段 is null】<br/>
* 使用示例【.isNull(query::getName)】<br/>
* 效果【name is null】<br/>
*
* @param getFunc 字段的lambda函数
* @return this
*/
public MyJpaUtil<T> isNull(Func1<T, ?> getFunc) {
JpaConditionUtil.isNull(predicates, root, cb, LambdaUtil.getFieldName(getFunc));
return this;
}
/**
* 为空判定【字段 is not null】<br/>
* 使用示例【.isNotNull(query::getName)】<br/>
* 效果【name is not null】<br/>
*
* @param getFunc 字段的lambda函数
* @return this
*/
public MyJpaUtil<T> isNotNull(Func1<T, ?> getFunc) {
JpaConditionUtil.isNotNull(predicates, root, cb, LambdaUtil.getFieldName(getFunc));
return this;
}
/**
* 小于等于判定【字段 <= 值】<br/>
* 使用示例【.lessThanOrEqualTo(query::getAge)】<br/>
* 效果【age <= 20】<br/>
*
* @param getFunc 字段与值的lambda函数,函数的返回值必须实现Comparable是可比较的
* @param <V> 操作值的类型
* @return this
*/
public <V extends Comparable<? super V>> MyJpaUtil<T> lessThanOrEqualTo(Func0<V> getFunc) {
JpaConditionUtil.lessThanOrEqualTo(predicates, root, cb, LambdaUtil.getFieldName(getFunc), getFunc.callWithRuntimeException());
return this;
}
/**
* 小于等于判定【字段 <= 值】<br/>
* 使用示例【.lessThanOrEqualTo(UserEntity::getAge, 20)】<br/>
* 效果【age <= 20】<br/>
*
* @param getFunc 字段的lambda函数
* @param value 操作值,必须实现Comparable是可比较的
* @param <V> 操作值的类型
* @return this
*/
public <V extends Comparable<? super V>> MyJpaUtil<T> lessThanOrEqualTo(Func1<T, V> getFunc, V value) {
JpaConditionUtil.lessThanOrEqualTo(predicates, root, cb, LambdaUtil.getFieldName(getFunc), value);
return this;
}
/**
* 小于等于判定【字段 < 值】<br/>
* 使用示例【.lessThan(query::getAge)】<br/>
* 效果【age < 20】<br/>
*
* @param getFunc 字段与值的lambda函数,函数的返回值必须实现Comparable是可比较的
* @param <V> 操作值的类型
* @return this
*/
public <V extends Comparable<? super V>> MyJpaUtil<T> lessThan(Func0<V> getFunc) {
JpaConditionUtil.lessThan(predicates, root, cb, LambdaUtil.getFieldName(getFunc), getFunc.callWithRuntimeException());
return this;
}
/**
* 小于等于判定【字段 < 值】<br/>
* 使用示例【.lessThan(UserEntity::getAge, 20)】<br/>
* 效果【age < 20】<br/>
*
* @param getFunc 字段的lambda函数
* @param value 操作值,必须实现Comparable是可比较的
* @param <V> 操作值的类型
* @return this
*/
public <V extends Comparable<? super V>> MyJpaUtil<T> lessThan(Func1<T, V> getFunc, V value) {
JpaConditionUtil.lessThan(predicates, root, cb, LambdaUtil.getFieldName(getFunc), value);
return this;
}
/**
* 小于等于判定【字段 >= 值】<br/>
* 使用示例【.greaterThanOrEqualTo(query::getAge)】<br/>
* 效果【age >= 20】<br/>
*
* @param getFunc 字段与值的lambda函数,函数的返回值必须实现Comparable是可比较的
* @param <V> 操作值的类型
* @return this
*/
public <V extends Comparable<? super V>> MyJpaUtil<T> greaterThanOrEqualTo(Func0<V> getFunc) {
JpaConditionUtil.greaterThanOrEqualTo(predicates, root, cb, LambdaUtil.getFieldName(getFunc), getFunc.callWithRuntimeException());
return this;
}
/**
* 小于等于判定【字段 >= 值】<br/>
* 使用示例【.greaterThanOrEqualTo(UserEntity::getAge, 20)】<br/>
* 效果【age >= 20】<br/>
*
* @param getFunc 字段的lambda函数
* @param value 操作值,必须实现Comparable是可比较的
* @param <V> 操作值的类型
* @return this
*/
public <V extends Comparable<? super V>> MyJpaUtil<T> greaterThanOrEqualTo(Func1<T, V> getFunc, V value) {
JpaConditionUtil.greaterThanOrEqualTo(predicates, root, cb, LambdaUtil.getFieldName(getFunc), value);
return this;
}
/**
* 小于等于判定【字段 > 值】<br/>
* 使用示例【.greaterThan(query::getAge)】<br/>
* 效果【age > 20】<br/>
*
* @param getFunc 字段与值的lambda函数,函数的返回值必须实现Comparable是可比较的
* @param <V> 操作值的类型
* @return this
*/
public <V extends Comparable<? super V>> MyJpaUtil<T> greaterThan(Func0<V> getFunc) {
JpaConditionUtil.greaterThan(predicates, root, cb, LambdaUtil.getFieldName(getFunc), getFunc.callWithRuntimeException());
return this;
}
/**
* 小于等于判定【字段 > 值】<br/>
* 使用示例【.greaterThan(UserEntity::getAge, 20)】<br/>
* 效果【age > 20】<br/>
*
* @param getFunc 字段的lambda函数
* @param value 操作值,必须实现Comparable是可比较的
* @param <V> 操作值的类型
* @return this
*/
public <V extends Comparable<? super V>> MyJpaUtil<T> greaterThan(Func1<T, V> getFunc, V value) {
JpaConditionUtil.greaterThan(predicates, root, cb, LambdaUtil.getFieldName(getFunc), value);
return this;
}
/**
* 小于等于判定【字段 in(值)】<br/>
* 使用示例【.in(query::getId)】<br/>
* 效果【id in(1,2,3)】<br/>
*
* @param getFunc 字段与值的lambda函数,函数的返回值必须实现Collection是一个集合
* @return this
*/
public <V extends Collection<?>> MyJpaUtil<T> in(Func0<V> getFunc) {
JpaConditionUtil.in(predicates, root, LambdaUtil.getFieldName(getFunc), getFunc.callWithRuntimeException());
return this;
}
/**
* 小于等于判定【字段 in(值)】<br/>
* 使用示例【.in(query::getId, userIds)】<br/>
* 效果【id in(1,2,3)】<br/>
*
* @param getFunc 字段的lambda函数,
* @param value 操作值,必须实现Collection是一个集合
* @param <V> 操作值的类型
* @param <E> 操作值的元素类型
* @return this
*/
public <E, V extends Collection<E>> MyJpaUtil<T> in(Func1<T, E> getFunc, V value) {
JpaConditionUtil.in(predicates, root, LambdaUtil.getFieldName(getFunc), value);
return this;
}
/**
* 小于等于判定【字段 not in(值)】<br/>
* 使用示例【.notIn(query::getId)】<br/>
* 效果【id in(1,2,3)】<br/>
*
* @param getFunc 字段与值的lambda函数,函数的返回值必须实现Collection是一个集合
* @param <V> 操作值的类型
* @param <E> 操作值的元素类型
* @return this
*/
public <E, V extends Collection<E>> MyJpaUtil<T> notIn(Func0<V> getFunc) {
JpaConditionUtil.notIn(predicates, root, cb, LambdaUtil.getFieldName(getFunc), getFunc.callWithRuntimeException());
return this;
}
/**
* 小于等于判定【字段 in(值)】<br/>
* 使用示例【.notIn(query::getId, userIds)】<br/>
* 效果【id not in(1,2,3)】<br/>
*
* @param getFunc 字段的lambda函数
* @param value 操作值,必须实现Collection是一个集合
* @param <V> 操作值的类型
* @param <E> 操作值的元素类型
* @return this
*/
public <E, V extends Collection<E>> MyJpaUtil<T> notIn(Func1<T, E> getFunc, V value) {
JpaConditionUtil.notIn(predicates, root, cb, LambdaUtil.getFieldName(getFunc), value);
return this;
}
/**
* 区间判定,不包含左右【字段 between startValue and endValue】<br/>
* 使用示例【.between(query::getAge, 1, 20)】<br/>
* 效果【age between 1 and 20】<br/>
*
* @param getFunc 字段的lambda函数
* @param startValue 起始值
* @param endValue 结束值
* @param <V> 操作值必须为Comparable实现类,是可比较的
* @return this
*/
public <V extends Comparable<? super V>> MyJpaUtil<T> between(Func1<T, V> getFunc, V startValue, V endValue) {
JpaConditionUtil.between(predicates, root, cb, LambdaUtil.getFieldName(getFunc), startValue, endValue);
return this;
}
/**
* 左右闭区间判定【字段 >= startValue and 字段 <= endValue】<br/>
* 使用示例【.closedInterval(query::getAge, 1, 20)】<br/>
* 效果【age >= 1 and age <= 20】<br/>
*
* @param getFunc 字段的lambda函数
* @param startValue 起始值
* @param endValue 结束值
* @param <V> 操作值必须为Comparable实现类,是可比较的
* @return this
*/
public <V extends Comparable<? super V>> MyJpaUtil<T> closedInterval(Func1<T, V> getFunc, V startValue, V endValue) {
JpaConditionUtil.closedInterval(predicates, root, cb, LambdaUtil.getFieldName(getFunc), startValue, endValue);
return this;
}
/**
* 左开右闭区间判定【字段 > startValue and 字段 <= endValue】<br/>
* 使用示例【.openLeftAndCloseRight(query::getAge, 1, 20)】<br/>
* 效果【age > 1 and age <= 20】<br/>
*
* @param getFunc 字段的lambda函数
* @param startValue 起始值
* @param endValue 结束值
* @param <V> 操作值必须为Comparable实现类,是可比较的
* @return this
*/
public <V extends Comparable<? super V>> MyJpaUtil<T> openLeftAndCloseRight(Func1<T, V> getFunc, V startValue, V endValue) {
JpaConditionUtil.openLeftAndCloseRight(predicates, root, cb, LambdaUtil.getFieldName(getFunc), startValue, endValue);
return this;
}
/**
* 左闭右开区间判定【字段 >= startValue and 字段 < endValue】<br/>
* 使用示例【.closeLeftAndOpenRight(query::getAge, 1, 20)】<br/>
* 效果【age >= 1 and age < 20】<br/>
*
* @param getFunc 字段的lambda函数
* @param startValue 起始值
* @param endValue 结束值
* @param <V> 操作值必须为Comparable实现类,是可比较的
* @return this
*/
public <V extends Comparable<? super V>> MyJpaUtil<T> closeLeftAndOpenRight(Func1<T, V> getFunc, V startValue, V endValue) {
JpaConditionUtil.closeLeftAndOpenRight(predicates, root, cb, LambdaUtil.getFieldName(getFunc), startValue, endValue);
return this;
}
/**
* 等于判定【字段 = 值】<br/>
* 使用示例【.equal(user::getId)】<br/>
* 效果【id = 1】<br/>
*
* @param getFunc 字段与值的lambda函数
* @param <V> 操作值类型约束
* @return this
*/
public <V> MyJpaUtil<T> equal(Func0<V> getFunc) {
JpaConditionUtil.equal(predicates, root, cb, LambdaUtil.getFieldName(getFunc), getFunc.callWithRuntimeException());
return this;
}
/**
* 等于判定【字段 = 值】<br/>
* 使用示例【.equal(query::getId, 1)】<br/>
* 效果【id = 1】<br/>
*
* @param getFunc 字段的lambda函数
* @param value 操作值
* @param <V> 操作值类型约束
* @return this
*/
public <V> MyJpaUtil<T> equal(Func1<T, V> getFunc, V value) {
JpaConditionUtil.equal(predicates, root, cb, LambdaUtil.getFieldName(getFunc), value);
return this;
}
/**
* 等于判定【字段 != 值】<br/>
* 使用示例【.notEqual(user::getId)】<br/>
* 效果【id != 1】<br/>
*
* @param getFunc 字段与值的lambda函数
* @param <V> 操作值类型约束
* @return this
*/
public <V> MyJpaUtil<T> notEqual(Func0<V> getFunc) {
JpaConditionUtil.notEqual(predicates, root, cb, LambdaUtil.getFieldName(getFunc), getFunc.callWithRuntimeException());
return this;
}
/**
* 等于判定【字段 != 值】<br/>
* 使用示例【.notEqual(query::getId, 1)】<br/>
* 效果【id != 1】<br/>
*
* @param getFunc 字段的lambda函数
* @param value 操作值
* @param <V> 操作值类型约束
* @return this
*/
public <V> MyJpaUtil<T> notEqual(Func1<T, V> getFunc, V value) {
JpaConditionUtil.notEqual(predicates, root, cb, LambdaUtil.getFieldName(getFunc), value);
return this;
}
/**
* 模式一中结束当前工具调用的方法并返回Predicate,该方法被@ProxyTab(useAgent = false)标记为在代理中直接调用
*
* @return p
*/
@ProxyTab(useAgent = false)
public Predicate builder() {
return query.where(predicates.toArray(new Predicate[0])).getRestriction();
}
/**
* Specification接口的toPredicate方法,在模式二中将通过Specification调用到次方法,该方法被@ProxyTab(useAgent = false)标记为在代理中直接调用
*
* @param root r
* @param query q
* @param cb c
* @return p
*/
@Override
@ProxyTab(useAgent = false)
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
try {
MyJpaUtil<T> jpa = MyJpaUtil.create(root, query, cb);
for (MethodRecord record : methodRecords) {
record.method.invoke(jpa, record.args);
}
return jpa.builder();
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
/**
* 方法记录类,记录方法方法本身,方法参数
*/
@Data
@AllArgsConstructor
private static class MethodRecord {
private Method method;
private Object[] args;
}
/**
* 代理标记注解,将标记代理中如何处理方法
*/
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
private @interface ProxyTab {
/**
* 不使用代理标记
*
* @return true:使用代理处理;false:代理中不做其他处理,直接调用
*/
boolean useAgent() default true;
/**
* 禁止在代理中调用该方法
*
* @return true:禁止代理调用,在代理中调用会抛异常;false:允许代理调用
*/
boolean disableProxyCall() default false;
}
/**
* 代理处理器,在该处理器中将根据方法使用的ProxyTab标记情况对类进行相应的处理
*/
private class AgentSimplification implements MethodInterceptor {
/**
* 代理处理实现方法
*
* @param obj 调用被代理方法的对象
* @param method 当前方法
* @param args 方法参数
* @param proxy 代理方法
* @return 返回方法的返回值
* @throws Throwable 可能的可抛出异常
*/
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
ProxyTab tab = method.getAnnotation(ProxyTab.class);
if (tab != null) {//加了代理标记,按代理标记的处理
if (tab.useAgent()) {//标记说允许使用代理
methodRecords.add(new MethodRecord(method, args));
} else {//正常调用方法
if (obj instanceof MyJpaUtil<?>) {//传递记录的方法
((MyJpaUtil<?>) obj).setMethodRecords(methodRecords);
}
return proxy.invokeSuper(obj, args);
}
if (tab.disableProxyCall()) {//标记说禁止调用就抛异常
throw new RuntimeException("当前方法为禁止代理调用方法,请使用MyJpaUtil.create(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb)创建方式调用该方法");
}
} else {//没有加标记就先缓存,后续调用toPredicate时才曾用缓存
methodRecords.add(new MethodRecord(method, args));
}
return obj;
}
}
/**
* 该构造器仅供动态代理调用
*/
protected MyJpaUtil() {
this.agentSimplification = new AgentSimplification();
}
/**
* 内部构造器,当前工具实际的唯一创建方式
*
* @param root r
* @param query q
* @param cb c
*/
private MyJpaUtil(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
this.root = root;
this.query = query;
this.cb = cb;
}
/**
* 模式一中创建MyJpaUtil的静态方法
*
* @param root r
* @param qu q
* @param cb c
* @param <T> root中定义的实体类类型
* @return this
*/
public static <T> MyJpaUtil<T> create(Root<T> root, CriteriaQuery<?> qu, CriteriaBuilder cb) {
return new MyJpaUtil<>(root, qu, cb);
}
/**
* 模式二指定通过返回值指定泛型类型的创建方式
*
* @param <T> root中定义的实体类类型
* @return this
*/
@SuppressWarnings("unchecked")
public static <T> MyJpaUtil<T> create() {
MyJpaUtil<T> jpa = new MyJpaUtil<>();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyJpaUtil.class);
enhancer.setCallback(jpa.agentSimplification);
return (MyJpaUtil<T>) enhancer.create();
}
/**
* 模式二通过class指定通过返回值指定泛型类型的创建方式
*
* @param <T> root中定义的实体类类型
* @return this
*/
@SuppressWarnings("unchecked")
public static <T> MyJpaUtil<T> create(Class<T> ignore) {
MyJpaUtil<T> jpa = new MyJpaUtil<>();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyJpaUtil.class);
enhancer.setCallback(jpa.agentSimplification);
return (MyJpaUtil<T>) enhancer.create();
}
/**
* 重写toString方法简介debug时当前工具显示异常问题
*
* @return s
*/
@Override
@ProxyTab(useAgent = false)
public String toString() {
return "MyJpaUtil{" +
"root=" + root +
", qu=" + query +
", cb=" + cb +
", predicates=" + predicates +
", methodRecords=" + methodRecords +
'}';
}
/**
* 获取Root,使用@ProxyTab(disableProxyCall = true)标记当前方法为禁止模式二调用
*
* @return Root
*/
@ProxyTab(disableProxyCall = true)
public Root<T> getRoot() {
return root;
}
/**
* 获取CriteriaQuery,使用@ProxyTab(disableProxyCall = true)标记当前方法为禁止模式二调用
*
* @return CriteriaQuery
*/
@ProxyTab(disableProxyCall = true)
public CriteriaQuery<?> getQuery() {
return query;
}
/**
* 获取CriteriaBuilder,使用@ProxyTab(disableProxyCall = true)标记当前方法为禁止模式二调用
*
* @return CriteriaBuilder
*/
@ProxyTab(disableProxyCall = true)
public CriteriaBuilder getCb() {
return cb;
}
/**
* 获取条件记录,使用@ProxyTab(disableProxyCall = true)标记当前方法为禁止模式二调用
*
* @return List<Predicate>
*/
@ProxyTab(disableProxyCall = true)
public List<Predicate> getPredicates() {
return predicates;
}
/**
* 放置方法记录,使用@ProxyTab(useAgent = false)标记当前方法为正常调用
*
* @param methodRecords 方法记录
*/
@ProxyTab(useAgent = false)
private void setMethodRecords(List<MethodRecord> methodRecords) {
this.methodRecords = methodRecords;
}
}
排序工具
import cn.hutool.core.lang.func.Func1;
import lombok.Getter;
/**
* @Author: wanger
* @Date: 2024/1/2 9:35
* @Description: 自定义排序类调用参考示例【<pre>{@code
* List<UserEntity> list = userService.list(MyJpaUtil.create(UserEntity.class)
* .equal(UserEntity::getStatus, DataStatus.NORMAL.getStatus())
* //其他条件...
* .order(Order.DESC(UserEntity::getCreatedTime), Order.ASC(UserEntity::getPhone),其他排序字段)
* );
* }</pre>】
*/
@Getter
public class Order<T> {
/**
* 顺序排序标识
*/
public static final String ASC = "ASC";
/**
* 倒序排序标识
*/
public static final String DESC = "DESC";
/**
* 当前的排序标识
*/
private final String sort;
/**
* 排序字段lambda方法,如【UserEntity::getAge】
*/
private final Func1<T, ?> fieldFun;
/**
* 私有构造器,仅供内部ASC与DESC两个方法使用
*
* @param fieldFun 排序字段lambda方法,如【UserEntity::getAge】
* @param sort 排序状态,ASC/DESC
*/
private Order(Func1<T, ?> fieldFun, String sort) {
this.fieldFun = fieldFun;
this.sort = sort;
}
/**
* 顺序排序方式,如【Order.ASC(UserEntity::getAge)】
*
* @param fieldFun 排序字段lambda方法,如【UserEntity::getAge】
* @param <T> 调用者的泛型类型
* @return 返回排序对象
*/
public static <T> Order<T> ASC(Func1<T, ?> fieldFun) {
return new Order<>(fieldFun, ASC);
}
/**
* 顺序排序方式,如【Order.DESC(UserEntity::getAge)】
*
* @param fieldFun 排序字段lambda方法,如【UserEntity::getAge】
* @param <T> 调用者的泛型类型
* @return 返回排序对象
*/
public static <T> Order<T> DESC(Func1<T, ?> fieldFun) {
return new Order<>(fieldFun, DESC);
}
}
文章针对使用Hibernate查询数据库时存在的编写大量重复代码、部分API难记、不能链式构造条件等问题,提出对Hibernate进行二次封装的解决方案。介绍了封装的设计思路,包括常用API封装、使用cglib代理等,还设计了两种调用模式并给出使用示例,最后提供了相关依赖和工具源码。

1574

被折叠的 条评论
为什么被折叠?



