jdbc应用
说明
专通过jdbc来与关系数据库交互的应用,标准jdbc应用的基类是AbstractJdbcApp它为工作流提供了基础的SQL执行能力包括事务支持。
一个流程中的所有sql的执行都是在事务中处理的,再第一次执行SQL的时候开启事务,流程结束后关闭事务,以即执行提交或回滚的操作。多个sql任务处在同一事务需满足以下三个条件:
- 同一个流程的同一次执行
- 同一个jdbc声明的任务
- 同一个线程执行的任务
AbstractJdbcApp会在线程变量ThreadLocal中存储一个Sql会话,该应用下的声明的所有Sql任务会共享该会话,并在流程结束后自动关闭。目前暂不支持SQL任务配置独立事务。
配置
可在config.groovy中配置标准jdbc应用,对应基本配置如下:
url: jdbc URL路径如 jdbc:mysql://localhost:3306/数据库名?useSSL=false&serverTimezone=UTC&characterEncoding=utf8user: 数据库用户名password: 数据库密码
注意事项:
AbstractJdbcApp是一个抽象类不可以直接配置,上述配置是针对其实现类的 如MySqlApp或PostgreSqlApp- 目前暂不支持基于连接池的配置。
任务声明
AbstractJdbcApp封装默认Sql任务的声明方法,其所有的子类都将拥有该声明方法
声明sql任务
defaultTask用于声明一个默认的Sql任务,其返回一个SqlTask任务实例,实现代码如下:
public SqlTask defaultTask() {
return new SqlTask(this::getSqlSession);
}SqlTask任务在初始化时将传入一个会话获供应者,确保当任务真正被执行时才会初始化Sql会话,关键代码如下:
声明示例:
假设已在config.groovy配置了mySql应用实例:
// 基础声明
sqlTask1= mysql.defaultTask {
sql = "select * from user where id=${input.id}"
}
// 基础声明 省略声明方法
sqlTask2= mysql {
sql = "select * from user where id=${input.id}"
}
//快捷声明,省略声明方法以及任务块
sqlTask3= mysql """
select * from user where id=${->input.id}
"""SQL任务
sql任务属于Invoke调用型任务,所以支持重试、异常策略、前后置处理配置。Sql任务实现类是SqlTask 对应结构以及可配置属性代码如下:
public class SqlTask extends InvokeTask implements Releasable {
Object sql; //sql语句:类型仅限String或GString
List<Object> params = new ArrayList<>();// sql参数
}- sql: 执行的sql语句,类型仅限
String或GString - params:sql参数列表对应
?占位符参数值。
sql任务底层基于groovy.sql.Sql实现,核心特性之一支持将值转换占位符,规避sql注入风险同时保证sql语句的可读性,特性如下:
- 所有Sql 插值部分
${ }自动转换成占位符,所以不能拼接表名或列名 - 如果插值表达示被单双引号包裹将不转换而是直接拼接如
where name='${input.name}' - 如果
params设置了任意一个值 所有插值表达示都是拼接而不在转换占位符 - 如果延迟插值
${->表达示}会在设参时取值并做为占位参数值。
声明方式
sql任务不是基础任务不能直接声明,只能通过jdbc应用来声明如MySqlAp、PostgreSqlApp等
示例:假设已在config.groovy配置了mySql应用实例:
// 基础声明 省略声明方法
sqlTask= mysql {
sql = "select * from user where id=${input.id}"
}- 上例基于mysql应用实例声明了一个SQl任务。
返回结果结构:
Sql任务默认返回结构如下:
// sqlTask返回结构
static class SqlData implements Serializable {
List<?> rawValue; // 原始数据集
Object value; // 简化值
long count; // 返回的总数量
}rawValue返回的原始多数据集,如果是查询结构建是List<List<GroovyRowResult>>,如果是变更语句List<Integer>value对原始数据集进行自动简化后的值,rawValue最多情况下可表示成一个三维数组,索引分别对应:集、行、列,简化规则如下:- 1个数据集1例
value将简化成该列的值。 - 1个数据集1行1例
value将简化成该列的值。 - 1个数据集1行多例
value简化成该行的表示对象GroovyRowResult。 - 一个数据集多行
value简化成该数据集的表示对象:List<GroovyRowResult>。 - 多个数据集时不简化
value与rawValue为同一个对象。
- 1个数据集1例
count统计rawValue多结果集中返回的所有行数。注意其不等同于变更的数量。
sql任务属于Invoke调用型任务所以支持添加后置处理器的方式改变结果如:
// 判断用户是否存在
userExists= mysql {
sql = "select * from user where id=${input.id}"
doLast {
result=result.count > 0
}
}doLast后置处理器,对该任务结果进行了修改通过返回的数量来判定用户是否存在- 上例
userExists执行完成后其结果将是一个布尔值,
连接自动释放机制
SqlTask实现了 Releasable接口所以其连接支持自动释放。当流程执行完毕后会找到所有实现了Releasable接口的任务实例并调用其close方法进行释放。其特征如下:
- 只有被执行过的任务才会在流程结束后执行
close释放方法,所以不能在任务方法执行前建立任何连接,否则不会被释放。 - 当流程结束时只会释放当前线程执行过的任务,异步任务由其所在线程结束后释放。
- 当流程或任务执行失败时,不影响任务
close释放方法的执行。
以下是关于Sql会话与实例的关系代码:
public class SqlTask extends InvokeTask implements Releasable {
private Supplier<SqlSession> sqlSessionGet;//Sql会话供应者
private SqlSession sqlSession;//sql会话 只有执行任务后才有值
/**
* 应避免直接构建SqlSession实例,因为该任务未被执行的情况下将不会自动关闭。
* @param sqlSession Sql会话供应者只有获取时才应该构建Sql会话实例
*/
public SqlTask(Supplier<SqlSession> sqlSession) {
super("SQL", null);
this.sqlSessionGet = sqlSession;
}
}