/sparktool

Spark SQL运行中间件,运行在Spark SQL上的动态SQL利器。参与贡献请前往 https://gitee.com/tenmg/sparktool

Primary LanguageJavaApache License 2.0Apache-2.0

Sparktool

maven

简介

Sparktool是运行Spark SQL的中间件,它能够运行动态化的、可传参的Spark SQL,解决Spark SQL传参的难题。所谓“可传参”即执行Spark SQL时可接收参数,而不是写死的SQL代码;所谓“动态化”即执行Spark SQL时最终执行的SQL可根据传入参数不同而不同(参见DSQL)。使用Sparktool,开发者不再需要繁杂的逻辑判断和SQL拼接。

起步

1. 添加依赖

如果采用Maven构建项目,可以通过在pom.xml中添加如下配置引入Sqltool。其中,${sparktool.version}为版本号,可定义属性或直接使用版本号替换。

<!-- https://mvnrepository.com/artifact/cn.tenmg/sparktool -->
<dependency>
    <groupId>cn.tenmg</groupId>
    <artifactId>sparktool</artifactId>
    <version>${sparktool.version}</version>
</dependency>

2. 调用API

Sparktool通过SparkDao向外提供API服务,它可以让Spark SQL迸发魔力。目前SparkDao只有一种实现即DSQLSparkDao,构建DSQLSparkDao需要指定DSQLFactory:

// 构建SparkDao
SparkDao sparkDao = DSQLSparkDao.build(new XMLFileDSQLFactory(basePackages, suffix));

// 如果已经构建了Sqltool的Dao,构建SparkDao时可以重用DSQLFactory
SparkDao sparkDao = DSQLSparkDao.build(dao.getDSQLFactory());

// 使用SparkDao解析执行DSQL从数据库加载数据集
Dataset<Row> dataset1 = sparkDao.load(sparkSession, dbOptions, dsqlId, params);

// 使用SparkDao将带参数DSQL提交给Spark SQL执行
Dataset<Row> dataset2 = sparkDao.sql(sparkSession, plainDSQLText, params);

参数

普通参数

使用:加参数名表示普通参数,例如,:staffName。

嵌入参数

使用#加参数名表示(例如,#staffName)嵌入参数,嵌入参数会被以字符串的形式嵌入到脚本中。 值得注意的是:在SQL脚本中使用嵌入参数,会有SQL注入风险,一定注意不要使用前端传参直接作为嵌入参数使用 。1.1.3版本开始支持嵌入参数,即对应dsl版本为1.2.2,单独升级dsl也可以支持。

动态参数

动态参数是指,根据具体情况确定是否在动态脚本中生效的参数,动态参数是动态片段的组成部分。动态参数既可以是普通参数,也可以嵌入参数。

静态参数

静态参数是相对动态参数而言的,它永远会在动态脚本中生效。在动态片段之外使用的参数就是静态参数。静态参数既可以是普通参数,也可以嵌入参数。

参数访问符

参数访问符包括两种,即.[], 使用Map传参时,优先获取键相等的值,只有键不存在时才会将键降级拆分一一访问对象,直到找到参数并返回,或未找到返回null。其中.用来访问对象的属性,例如:staff.name#staff.age[]用来访问数组、集合的元素,例如:array[0]#map[key]。理论上,支持任意级嵌套使用,例如:list[0][1].name#map[key][1].staff.name。1.1.3版本开始支持参数访问符,即对应dsl版本为1.2.2,单独升级dsl也可以支持。

DSQL

DSQL的全称是动态结构化查询语言(Dynamic Structured Query Language),它使用特殊字符#[]标记动态片段。当实际执行查询时,判断实际传入参数值是否为空(null)决定是否保留该片段,同时自动去除#[]。以此来避免程序员手动拼接繁杂的SQL,使得程序员能从繁杂的业务逻辑中解脱出来。

简单例子

假设有如下动态查询语句:

SELECT
  *
FROM STAFF_INFO S
WHERE S.STATUS = 'VALID'
#[AND S.STAFF_ID = :staffId]
#[AND S.STAFF_NAME LIKE :staffName]

参数staffId为空(null),而staffName为非空(非null)时,实际执行的语句为:

SELECT
   *
 FROM STAFF_INFO S
 WHERE S.STATUS = 'VALID'
 AND S.STAFF_NAME LIKE :staffName

相反,参数staffName为空(null),而staffId为非空(非null)时,实际执行的语句为:

SELECT
   *
 FROM STAFF_INFO S
 WHERE S.STATUS = 'VALID'
 AND S.STAFF_ID = :staffId

或者,参数staffId、staffName均为空(null)时,实际执行的语句为:

SELECT
   *
 FROM STAFF_INFO S
 WHERE S.STATUS = 'VALID'

最后,参数staffId、staffName均为非空(非null)时,实际执行的语句为:

SELECT
   *
 FROM STAFF_INFO S
 WHERE S.STATUS = 'VALID'
 AND S.STAFF_ID = :staffId
 AND S.STAFF_NAME LIKE :staffName

通过上面这个小例子,我们看到了动态结构化查询语言(DSQL)的魔力。这种魔力的来源是巧妙的运用了一个值:空(null),因为该值往往在结构化查询语言(SQL)中很少用到,而且即便使用也是往往作为特殊的常量使用,比如:

NVL(EMAIL,'无')

WHERE EMAIL IS NOT NULL

等等。

进一步了解

更多有关DSQL的介绍,详见https://gitee.com/tenmg/dsql

相关链接

DSQL开源地址:https://gitee.com/tenmg/dsql

DSL开源地址:https://gitee.com/tenmg/dsl