SpecificationHelper 使用注解快速构建spring-boot-starter-data-jpa查询语句
前言
本来我是一名Android开发人员不应该写这篇文章的,但是耐不住无聊在家,然后又对这一块
比较喜欢,所以才有了这个东西,而且我前面也写过一篇关于构造一个Android RecyclerView
的
一个通用适配器,也不知道有没有用,唉,不废话了,还是进入我们今儿个的正题吧!
想必大叫如果看过或者用过jpa的应该都会觉得如果查询多的话,会比较繁琐,而且每
个查询的代码又都差不多,只有条件的判断什么的有点不一样,所以从最初开始我就在网上看有没有能
减少一部分开发量的库,然而看了一圈还是没有找到比较合适的库可以使用,所以才有了今天这个工具。
目标
这次库的目标就是减少,增加查询修改Repository
的次数,在最初的时候每次有新的
需求都需要对应的去添加service和Repository的代码。废话多说,直接上代码。
使用方式
1、模糊查询 @Keywords
先来看看Keywords的注解吧
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Keywords {
/**
* 需要查询的字段名,在Specification中称为path(路径)
* @return
*/
String[] value();
/**
* @see QueryGroup
* 该查询的名称,方便我们后面QueryGroup做分组查询
* @return
*/
String name() default "";
/**
* 模糊查询的前缀,默认为 %
* @return
*/
String prefix() default "%";
/**
* 模糊查询的后缀,默认为 %
* @return
*/
String suffix() default "%";
/**
* @see Operator
* 模糊查询连接条件 分为 AND 和 OR
* @return
*/
Operator operator() default Operator.OR;
}
查询条件代码
@Data
public static class KeywordsQueryCriteria implements Serializable {
/**
* 模糊查询 account 和 token 两个字段,连接条件为 OR
*/
@Keywords(value = {
"account", "token"}, suffix = "%", prefix = "%", operator = Operator.OR)
private String keywords;
}
查询生成的sql
SELECT
accountent0_.id AS id1_0_,
accountent0_.create_time AS create_t2_0_,
accountent0_.is_delete AS is_delet3_0_,
accountent0_.delete_time AS delete_t4_0_,
accountent0_.update_time AS update_t5_0_,
accountent0_.account AS account6_0_,
accountent0_.ENABLE AS enable7_0_,
accountent0_.locked AS locked8_0_,
accountent0_.register_ip AS register9_0_,
accountent0_.token AS token10_0_,
accountent0_.type AS type11_0_
FROM
tk_account accountent0_
WHERE
accountent0_.account LIKE ?
OR accountent0_.token LIKE ?
看这样子是我们想要的结果,
2、单条属性查询 @Query
注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(value = Queries.class)
public @interface Query {
/**
* @see QueryType
* 查询类型
* NOT_EQUAL 不相等
* EQUAL 相等
* GREATER_THAN 大于
* GREATER_THAN_OR_EQUAL 大于等于
* LESS_THAN 小于
* LESS_THAN_OR_EQUAL 小于等于
* IN 在集合或数组内 必须注解在 array 或者 Collection上
* BETWEEN 在范围内 必须注解在 array 或者 List 上,并且元素要大于或等于2
* @return
*/
QueryType type();
/**
* @see QueryGroup
* 该查询名称,方便我们后面QueryGroup做分组查询
* @return 默认使用属性名
*/
String name() default "";
/**
* 查询字段 path
* @return 优先使用配置的路径,然后使用名称,最后使用属性名
*/
String path() default "";
}
使用,这儿有一个需要特别注意的地方,就是两个token的查询,需要给其中一个取名,不然会只有查询其中 一个
@Data
public static class QueryCriteria implements Serializable {
/**
* 查询范围内的id,这儿例子给的是0-20
*/
@Query(type = QueryType.BETWEEN, path = "id")
private Integer[] ids;
/**
* 查询在集合内的账号,这儿给的粒子是["123", "456", "789"]
*/
@Query(type = QueryType.IN, path = "account")
private String[] accounts;
/**
* 模糊查询token,给其查询取名为keyword_token,如果不取名默认为属性名和下面的查询冲突了
*/
@Keywords(value = "token", name = "keyword_token")
/**
* 查询不为null的token,虽然没有实际意义,这儿只是给个例子,说明可以多次查同一字段
*/
@Query(type = QueryType.IS_NULL, path = "token")
private String token;
/**
* 查询delete条件
*/
@Query(type = QueryType.EQUAL)
private Boolean delete;
}
查询生成的sql
SELECT
accountent0_.id AS id1_0_,
accountent0_.create_time AS create_t2_0_,
accountent0_.is_delete AS is_delet3_0_,
accountent0_.delete_time AS delete_t4_0_,
accountent0_.update_time AS update_t5_0_,
accountent0_.account AS account6_0_,
accountent0_.ENABLE AS enable7_0_,
accountent0_.locked AS locked8_0_,
accountent0_.register_ip AS register9_0_,
accountent0_.token AS token10_0_,
accountent0_.type AS type11_0_
FROM
tk_account accountent0_
WHERE
( accountent0_.token IS NULL )
AND (
accountent0_.token LIKE ?)
AND ( accountent0_.id BETWEEN 0 AND 20 )
AND accountent0_.is_delete =?
查询使用的json
{
"ids":[0,20],
"accounts":["123", "456", "789"],
"token": "123",
"delete": true
}
大家看到这儿是不是总觉得还差些什么?生成的sql,总是不能完全满足自己的要求,而且还不能控制多个查询之间的关系。
对,觉得差就对了,我们还差一个分组查询,能够任意组合我们几个查询之间关系的这么个东西,所以下面就出来了。
3、真正厉害的查询 @QueryGroup 分组查询(不是sql中 groupBy 查询,是把其中几个查询添加合并为一个)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(QueryGroups.class)
public @interface QueryGroup {
/**
* 查询名称 可以把几个查询合并为一个查询,然后在用于其他的组
* @return
*/
String name();
/**
* 查询的目标名称
* @return
*/
String[] targetNames() default {
};
/**
* 分组查询的条件 OR 或 AND
* @return
*/
Operator operator() default Operator.AND;
}
@Data
@QueryGroup(name