1. 说明

JParser 是一个好用的SQL处理工具,它的强大之处在于可以解析SQL并根据条件随意修改SQL

对于需要某些全局特性的SQL,如添加租户,删除SQL改为逻辑删除、构建动态表等可以实现统一处理,避免业务开发疏忽某些条件参数等。

当然,SQL改写避免不了性能的损耗,它需要解析SQL,修改完成之后再构造SQL,对于高并发场景还是应当慎用

2. 概念

对于JParser来说,我也是一个初学者,现在将一些对于框架的理解分析一下。

2.1. Expression:表达式

大多数方法都是基于它的实现,而且基本上后续大部分的入参都是它,基本属于顶级的存在

2.2. Select 查询

这是对于查询语句的封装,是一个抽象接口,select查询语句就会解析成它。

其实现类有以下集中

  • PlainSelect 一般的查询语句
  • ParenthesedSelect:带括号的查询语句,一般用于in子句中
  • WithItem: with as( ) 语句解析成的查询。
  • SetOperationList
  • TableStatement
  • Values
  • LateralSubSelect

一般的查询语句基本用其实现PlainSelectParenthesedSelect这两个类

下面是一个最简单的select查询分解

1
2
3
4
5
6
//使用CCJSqlParserUtil工具解析SQL,这是一个强大的SQL解析器
Statement parse = CCJSqlParserUtil.parse("select * from user t_u");
//转换成select
Select select = (Select) parse;
//获取到PlainSelect
PlainSelect plainSelect = select.getPlainSelect();

也可以使用

1
2
3
4
5
6
@Test
public void plainSelect2() throws JSQLParserException {
Select select = SelectUtils.buildSelectFromTable(new Table("user"));
PlainSelect plainSelect = select.getPlainSelect();
System.out.println("plainSelect = " + plainSelect);
}

最终都可以得到PlainSelect,内部方法很多,关于join 、where、 with as 、group by、order by 、limit 等都有,如下示例

1
2
3
4
5
6
7
select distinct t_u.id u_id,t_r.id r_id 
from user t_u
inner join role t_r on t_u.id = t_r.id
where t_u.id = 1 and r.id=1 or r.name=2
group by t_u.id
order by t_u.id
limit 0,10

经过转换得到PlainSelect对象:

1
PlainSelect plainSelect = ((Select)CCJSqlParserUtil.parse(sql)).getPlainSelect();

11669ff4be07363660543443e462729d

可以看出,基本都已经包含了,我们可以直接通过对应的方法拿内部的数据

2.3. Table 表

表字段,就是我们SQL语句中的表相关的封装, 包括from后的主表和join的联表

2.4. ParenthesedSelect 带括号的查询(子查询)

子查询用到的封装,SQL语法中子查询用括号扩起来并在括号后面用表别名命名。
子查询的结果视为一张表,封装之后用ParenthesedSelect表示
如下:

1
select * from t_user t_u left join (select * from t_role where id<50) t_r where t_u.id=t_r.user_id

(select * from t_role where id<50) t_r即封装成一个ParenthesedSelect

2.5. SelectItem 查询的字段

select查询中最后提取的字段的封装,把所有的返回字段封装成一个列表,每一个对象为一个SelectItem
如SQL:

1
select id,name from t_user 

其中,id和name 会封装成两个SelectItem

2.6. Where 中表达式

where查询全部使用Expression抽象,因为where中的匹配其实是多样的,包括and or in exists like between...and... = !=
常用的表达式封装有

  • AndExpression: and连接
  • OrExpression: or连接
  • InExpression: in连接
  • LikeExpression: like连接
  • BetweenExpression: between连接
  • EqulesTo: 等号连接
  • NotEqulesTo: 不等于

一般都会使用left 和 right 两个属性分别存等号左右两侧的封装

表达式封装的left、rigth属性对应的也是Expression表达式,因为它也可以包含多种类型

  • Column: 表字段名,即我们一般说的id=1中id就封装成一个Colume
  • StringValue/LongValue:传入的值,即id=1中的1就会封装成一个StringValue或LongValue,根据是字符还是数字来定
  • ParenthesedSelect:带括号子查询,一般是in和exists后面使用

JSqlParser巧妙的把这些都封装好了,使用起来就可以根据SQL语句灵活构造

3. JsqlParser 嵌套理解

JsqlParser整体上是一个递归思想的解析流程,select包含了查询的各项数据,其子元素如SelectItemWhere中都有可能实现递归操作

where后面的语法其实是一个树形的条件语法封装,如and、or、in等两边入参都是Expression,而Expression本身也是这些实现

因此,理论上可以无限递归下去,因此用树形的数据结构可以很好的解释
如下嵌入多层的SQL示例(当然,实际基本不可能这么写):

1
2
3
4
5
6
7
8
9
10
11
12
13
select * from user 
where age=1 +
and id in (
select user_id
from user_role
where role_id=1
or role_name in (
select role_name
from role
where role_type='管理'
)
) +
or hobby like '%爬山%'

构造的树形结构如下

5dc9a16d378523c732d9c24520864075

如果加上SelectItem,则不只是where上的分叉,总的来说是满足树形结构的。