Skip to content

jdbc应用

说明

专通过jdbc来与关系数据库交互的应用,标准jdbc应用的基类是AbstractJdbcApp它为工作流提供了基础的SQL执行能力包括事务支持。

一个流程中的所有sql的执行都是在事务中处理的,再第一次执行SQL的时候开启事务,流程结束后关闭事务,以即执行提交或回滚的操作。多个sql任务处在同一事务需满足以下三个条件:

  1. 同一个流程的同一次执行
  2. 同一个jdbc声明的任务
  3. 同一个线程执行的任务

AbstractJdbcApp会在线程变量ThreadLocal中存储一个Sql会话,该应用下的声明的所有Sql任务会共享该会话,并在流程结束后自动关闭。目前暂不支持SQL任务配置独立事务。

配置

可在config.groovy中配置标准jdbc应用,对应基本配置如下:

  • url: jdbc URL路径如 jdbc:mysql://localhost:3306/数据库名?useSSL=false&serverTimezone=UTC&characterEncoding=utf8
  • user: 数据库用户名
  • password: 数据库密码

注意事项:

  • AbstractJdbcApp是一个抽象类不可以直接配置,上述配置是针对其实现类的 如MySqlAppPostgreSqlApp
  • 目前暂不支持基于连接池的配置。

任务声明

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语句,类型仅限StringGString
  • params:sql参数列表对应?占位符参数值。

sql任务底层基于groovy.sql.Sql实现,核心特性之一支持将值转换占位符,规避sql注入风险同时保证sql语句的可读性,特性如下:

  • 所有Sql 插值部分${ }自动转换成占位符,所以不能拼接表名或列名
  • 如果插值表达示被单双引号包裹将不转换而是直接拼接如where name='${input.name}'
  • 如果params设置了任意一个值 所有插值表达示都是拼接而不在转换占位符
  • 如果延迟插值${->表达示} 会在设参时取值并做为占位参数值。

声明方式

sql任务不是基础任务不能直接声明,只能通过jdbc应用来声明如MySqlAp、PostgreSqlApp等

示例:假设已在config.groovy配置了mySql应用实例:

groovy
// 基础声明 省略声明方法
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>
    • 多个数据集时不简化valuerawValue为同一个对象。
  • 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;
    }
 }