实现一个通用分页查询接口

时间:2022-02-13 12:42:58

我们使用的持久层框架是:jpa + hibernate 经常使用到里面的动态查询。

最开始使用的时候觉得:窝草!还能这样玩。

写了一年多代码后.....

尼玛,不想写了。

于是乎,我最近想写个通用型的分页查询接口,只需要传递一个实体类就执行查询了,

再也不需要为了做一个查询还写repository,serivec,serviceImpl。想想都开心!

于是乎我决定先做一个demo版:

由于对jpa、hibernate源码,反射使用的都还不熟.....导致了一些问题,我这里也统统记录下来;

思路以及版本演进: 0.1:在原有方法上进行改进,将原来的从实体类上获取字段以及属性值改为通过反射获取;

实现一个通用分页查询接口

问题:需要创建相关实体类的repository接口!还需要依赖接口调用,而通过反射调用方法的时候需要对象实例化,接口不能被实例化; 改进:尝试通过spring注入JpaSpecificationExecutor接口,没有成功! 0.2:修改思路,通过实例化JpaSpecificationExecutor 的实现类 SimpleJpaRepository ,直接调用findAll; 问题:SimpleJpaRepository 的构造函数中需要 EntityManager,尝试多个方法后使用了注解 @PersistenceContext 获取到了实例; 代码(只对String类型的属性做模糊查询): 这不是一个工具类,而是一个service实现类。因为注入功能需要加载到Spring容器中。 反射获取不到父类中的属性,介于我们先在项目中的实体id是继承而来,下面的代码做了特殊处理;
@PersistenceContext
private EntityManager entityManager;

/**
* 通过反射匹配对象中的属性分页查询数据库
*
*
@param entity 实体类
*
@param pageable 分页对象
*
@return
*/
@Override
public Object findByPage(final Object entity, Pageable pageable) {
final Class<?> entityClass = entity.getClass();
SimpleJpaRepository simpleJpaRepository
= new SimpleJpaRepository(entityClass, entityManager);
return simpleJpaRepository.findAll(new Specification() {
@Override
public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) {
List
<Predicate> predicateList = new ArrayList<>();
Field[] fields
= entityClass.getDeclaredFields();
//属性名
String fieldName;
//方法名
String getMethodName;
//方法
Method method;
//属性值
Object value;
//属性类型
String type;
try {
//获取父类以及继承属性
Class<?> supper = entityClass.getSuperclass();
//获取继承的id属性
Field id = supper.getDeclaredField("id");
type
= id.getGenericType().toString();
Method sm
= supper.getDeclaredMethod("getId");
value
= sm.invoke(entity);
if (type.equals("class java.lang.String") && StringUtils.isNotBlank(value.toString())) {
Path p
= root.get("id");
predicateList.add(cb.equal(p, value.toString()));
}
}
catch (Exception e) {
e.printStackTrace();
}
for (Field field : fields) {
fieldName
= field.getName();
type
= field.getGenericType().toString();
getMethodName
= "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
try {
method
= entityClass.getDeclaredMethod(getMethodName);
value
= method.invoke(entity);
if (value == null) {
continue;
}
if (type.equals("class java.lang.String") && StringUtils.isNotBlank(value.toString())) {
Path p
= root.get(fieldName);
//对id做eq对比 非id的做like对比
predicateList.add(cb.like(p, "%" + value.toString() + "%"));
}
}
catch (Exception e) {
e.printStackTrace();
}
}
query.where(predicateList.toArray(
new Predicate[predicateList.size()]));
return query.getRestriction();

}
}, pageable);
}

0.3:实体类是由 EntityManager 来管理的,既然已经获取到了EntityManager那我们就直接拼JPQL查询吧,没必要再封装一次了!

代码如下:
/**
* 根据对象属性的值通过entityManager查询
*
*
@param entity
*
@param searchParam
*
@return
*/
@Override
public Object findByPageEntity(Object entity, SearchParam searchParam) {
Class
<?> entityClass = entity.getClass();
Field[] fields
= entityClass.getDeclaredFields();
//属性名
String fieldName;
//方法名
String getMethodName;
//方法
Method method;
//属性值
Object value;
//属性类型
String type;
//标记是否有where条件
boolean w = true;
//构建查询JPQL语句
StringBuffer jpql = new StringBuffer("SELECT c FROM " + entityClass.getName() + " c ");
StringBuffer where
= new StringBuffer();
Map
<String, Object> map = new HashedMap();
try {
//获取父类以及继承属性
Class<?> supper = entityClass.getSuperclass();
//获取继承的id属性
Field id = supper.getDeclaredField("id");
type
= id.getGenericType().toString();
Method sm
= supper.getDeclaredMethod("getId");
value
= sm.invoke(entity);
if (value != null && type.equals("class java.lang.String") && StringUtils.isNotBlank(value.toString())) {
w
= false;
where.append(
" c.id").append(" = :id AND");
map.put(
"id", value);
}
}
catch (Exception e) {
e.printStackTrace();
}
for (Field field : fields) {
fieldName
= field.getName();
type
= field.getGenericType().toString();
getMethodName
= "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
try {
method
= entityClass.getDeclaredMethod(getMethodName);
value
= method.invoke(entity);
if (value == null) {
continue;
}
//字符串类型的参数才允许模糊查询
if (type.equals("class java.lang.String") && StringUtils.isNotBlank(value.toString())) {
map.put(fieldName,
"%" + value.toString() + "%");
where.append(
" c.").append(fieldName).append(" LIKE :").append(fieldName).append(" AND");
}
}
catch (IllegalAccessException e) {
e.printStackTrace();
}
catch (Exception e) {
e.printStackTrace();
}
}
//去掉多余的and
if (!w) {
jpql.append(
"WHERE");
where.delete(where.length()
- 4, where.length());
jpql.append(where);
}
List
<OrderFiled> orderFiles = searchParam.getOrderFiled();
if (orderFiles.size() > 0) {
jpql.append(
" ORDER BY");
//拼接排序语句 key为排序字段 value为排序类型
for (OrderFiled orderFiled : orderFiles) {
jpql.append(
" c.").append(orderFiled.getOrderFiled()).append(" ").append(orderFiled.getOrderType()).append(" ,");
}
jpql.deleteCharAt(jpql.length()
- 1);
}
Query query
= entityManager.createQuery(jpql.toString());
//循环map为查询语句条件赋值
for (Map.Entry<String, Object> entry : map.entrySet()) {
query.setParameter(entry.getKey(), entry.getValue());
}
Integer number
= searchParam.getPageNumber() - 1;
Integer size
= searchParam.getPageSize();
query.setFirstResult(number
* size);
query.setMaxResults(size);
return query.getResultList();
}
0.4:自定义字段匹配条件,通过字段注解来实现自定义注解类
/**
* 用于实体类属性查询时的判断条件
* Created by nankeyimeng on 6/28/2017.
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SearchLimit {
/**
* 条件枚举
* 分别对应模糊查询,等于,不等于,大于,大于等于,小于,小于等于
*
*
@author peida
*/
enum Where {
like, eq, ne, gt, ge, lt, le
}

Where searchLimit()
default Where.eq;
}

demo出炉,这里的排序和分页是自己封装的对象

/**
* 根据对象属性的值通过entityManager查询
*
*
@param entity
*
@param searchParam
*
@return
*/
@Override
public Object findByPageEntity(Object entity, SearchParam searchParam) {
Class
<?> entityClass = entity.getClass();
Field[] fields
= entityClass.getDeclaredFields();
//属性名
String fieldName;
//方法名
String getMethodName;
//方法
Method method;
//属性值
Object value;
//属性类型
String type;
//标记是否有where条件
boolean w = true;
//构建查询JPQL语句
StringBuffer jpql = new StringBuffer("SELECT c FROM " + entityClass.getName() + " c ");
StringBuffer where
= new StringBuffer();
Map
<String, Object> map = new HashedMap();
try {
//获取父类以及继承属性
Class<?> supper = entityClass.getSuperclass();
//获取继承的id属性
Field id = supper.getDeclaredField("id");
type
= id.getGenericType().toString();
Method sm
= supper.getDeclaredMethod("getId");
value
= sm.invoke(entity);
if (value != null && type.equals("class java.lang.String") && StringUtils.isNotBlank(value.toString())) {
w
= false;
where.append(
" c.id").append(" = :id AND");
map.put(
"id", value);
}
}
catch (Exception e) {
e.printStackTrace();
}
for (Field field : fields) {
fieldName
= field.getName();
if (field.isAnnotationPresent(SearchLimit.class)) {
type
= field.getGenericType().toString();
getMethodName
= "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
//获取自定义注解
SearchLimit searchLimit = field.getAnnotation(SearchLimit.class);
//获取注解值
String limitAnnotation = searchLimit.searchLimit().toString();
try {
method
= entityClass.getDeclaredMethod(getMethodName);
value
= method.invoke(entity);
if (value == null) {
continue;
}
switch (limitAnnotation) {
case "like":
if (w) {
w
= false;
}
//字符串类型的参数才允许模糊查询
if (type.equals("class java.lang.String") && StringUtils.isNotBlank(value.toString())) {
map.put(fieldName,
"%" + value.toString() + "%");
where.append(
" c.").append(fieldName).append(" LIKE :").append(fieldName).append(" AND");
}
break;
case "eq":
if (w) {
w
= false;
}
map.put(fieldName, value);
where.append(
" c.").append(fieldName).append(" = :").append(fieldName).append(" AND");
break;
case "ne":
if (w) {
w
= false;
}
map.put(fieldName, value);
where.append(
" c.").append(fieldName).append(" != :").append(fieldName).append(" AND");
break;
case "gt":
if (w) {
w
= false;
}
map.put(fieldName, value);
where.append(
" c.").append(fieldName).append(" > :").append(fieldName).append(" AND");
break;
case "ge":
if (w) {
w
= false;
}
map.put(fieldName, value);
where.append(
" c.").append(fieldName).append(" >= :").append(fieldName).append(" AND");
break;
case "lt":
if (w) {
w
= false;
}
map.put(fieldName, value);
where.append(
" c.").append(fieldName).append(" < :").append(fieldName).append(" AND");
break;
case "le":
if (w) {
w
= false;
}
map.put(fieldName, value);
where.append(
" c.").append(fieldName).append(" <= :").append(fieldName).append(" AND");
break;
default:
//没有加注解的不加入查询条件
}
}
catch (Exception e) {
e.printStackTrace();
}
}
}
//去掉多余的and
if (!w) {
jpql.append(
"WHERE");
where.delete(where.length()
- 4, where.length());
jpql.append(where);
}
List
<OrderFiled> orderFiles = searchParam.getOrderFiled();
if (orderFiles.size() > 0) {
jpql.append(
" ORDER BY");
//拼接排序语句 key为排序字段 value为排序类型
for (OrderFiled orderFiled : orderFiles) {
jpql.append(
" c.").append(orderFiled.getOrderFiled()).append(" ").append(orderFiled.getOrderType()).append(" ,");
}
jpql.deleteCharAt(jpql.length()
- 1);
}
Query query
= entityManager.createQuery(jpql.toString());
//循环map为查询语句条件赋值
for (Map.Entry<String, Object> entry : map.entrySet()) {
query.setParameter(entry.getKey(), entry.getValue());
}
Integer number
= searchParam.getPageNumber() - 1;
Integer size
= searchParam.getPageSize();
query.setFirstResult(number
* size);
query.setMaxResults(size);
return query.getResultList();
}
测试代码:实体类:
@Entity
@DynamicInsert(
true)
@DynamicUpdate(
true)
@Table(name
= "UM_SYS_USER_MEMBER")
public class SysUserMember extends IdEntity implements java.io.Serializable {

private static final long serialVersionUID = 1531492635701976701L;
private String userName;//用户名(昵称)
private String userPassword;
private String userSalt;
private List<SysRole> sysRoles = new ArrayList<SysRole>();
private List<SysResource> sysResources = new ArrayList<SysResource>();
private List<SysOrganization> sysOrganizations = new ArrayList<SysOrganization>();
private String available;
private String alias;
@JsonFormat(pattern
= "yyyy-MM-dd", timezone = "GMT+08:00")
@SearchLimit(searchLimit
= SearchLimit.Where.lt)
private Date createTime;
@SearchLimit(searchLimit
= SearchLimit.Where.eq)
private Integer isAdmin;
  ........

测试方法

@Test
public void findByPageEntity() throws Exception {
SysUserMember sysUserMember
= new SysUserMember();
sysUserMember.setIsAdmin(
1);
sysUserMember.setCreateTime(
new Date());
List
<OrderFiled> order = new ArrayList<>();
order.add(
new OrderFiled("createTime", "asc"));
order.add(
new OrderFiled("isAdmin", "desc"));
SearchParam searchParam
= new SearchParam(1, 10, order);
Object object
= searchService.findByPageEntity(sysUserMember, searchParam);
System.out.print(object.toString());
}

测试结果符合预期,就先酱紫了。。。。已知的缺陷还是蛮多了,比如范围之类的怎么处理等等......等我有时间了再改吧。欢迎大家提意见啊.....提想法