返回介绍

18.5. 执行自定义 SQL

发布于 2023-09-17 23:40:35 字数 7763 浏览 0 评论 0 收藏 0

Flowable提供了与数据库交互的高级API。例如,要获取数据,查询API与原生(Native)查询API各有用武之地。但有时会需要更高的灵活性。下面介绍如何在Flowable流程引擎范围内(所以可以使用相同的事务设置等)的数据存储中,执行完全自定义的SQL语句(select、insert、update与delete都可以)。

Flowable引擎使用其底层框架MyBatis的功能自定义SQL语句。可以在MyBatis用户手册找到更多信息。

18.5.1. 基于注解的映射语句

使用基于注解的映射语句(Annotation based Mapped Statement)时,首先要做的是创建一个MyBatis映射类。例如,假设在某个用例中,不需要完整的任务数据,而只需要其中很少一部分,就可以通过映射类(Mapper)完成,像是这样:

public interface MyTestMapper {

  @Select("SELECT ID_ as id, NAME_ as name, CREATE_TIME_ as createTime FROM ACT_RU_TASK")
  List<Map<String, Object>> selectTasks();

}

必须像下面这样为流程引擎配置映射类:

...
<property name="customMybatisMappers">
  <set>
  <value>org.flowable.standalone.cfg.MyTestMapper</value>
  </set>
</property>
...

请注意这是一个接口。底层的MyBatis框架会构造一个它的实例,并在运行时使用。也请注意方法的返回值没有类型,而只是一个map的list(代表了带有列数据的行的列表)。如果需要,可以通过MyBatis映射类设置类型。

使用managementService.executeCustomSql方法执行上面的查询。这个方法使用CustomSqlExecution的实例作为包装器,将引擎需要处理的内部数据隐藏起来。

不幸的是,Java泛型降低了可读性。下面的两个泛型类是映射类与其返回类型类。只是简单的调用映射方法,并返回其结果(若有)。

CustomSqlExecution<MyTestMapper, List<Map<String, Object>>> customSqlExecution =
      new AbstractCustomSqlExecution<MyTestMapper, List<Map<String, Object>>>(MyTestMapper.class) {

  public List<Map<String, Object>> execute(MyTestMapper customMapper) {
  return customMapper.selectTasks();
  }

};

List<Map<String, Object>> results = managementService.executeCustomSql(customSqlExecution);

在这个例子里,上面列出的Map只包含id, name与创建时间,而不是完整的任务对象。

按照上面的方法可以使用任何SQL。另一个更复杂的例子:

  @Select({
    "SELECT task.ID_ as taskId, variable.LONG_ as variableValue FROM ACT_RU_VARIABLE variable",
    "inner join ACT_RU_TASK task on variable.TASK_ID_ = task.ID_",
    "where variable.NAME_ = #{variableName}"
  })
  List<Map<String, Object>> selectTaskWithSpecificVariable(String variableName);

任务表join变量表,只选择变量有特定名字的记录,并返回任务id与对应的数字值。

请参考单元测试org.flowable.standalone.cfg.CustomMybatisMapperTest及src/test/java/org/flowable/standalone/cfg/与src/test/resources/org/flowable/standalone/cfg/目录中的其它类与资源,了解基于注解的映射语句的更多使用例子。

18.5.2. 基于XML的映射语句

可以使用基于XML的映射语句(XML based Mapped Statement),在XML文件中定义语句。对于不需要完整的任务数据,而只需要其中很少一部分数据的用例,XML文件像是下面这样:

<mapper namespace="org.flowable.standalone.cfg.TaskMapper">

  <resultMap type="org.flowable.standalone.cfg.CustomTask">
  <id property="id" column="ID_" jdbcType="VARCHAR"/>
  <result property="name" column="NAME_" jdbcType="VARCHAR"/>
  <result property="createTime" column="CREATE_TIME_" jdbcType="TIMESTAMP" />
  </resultMap>

  <select resultMap="customTaskResultMap">
  select RES.ID_, RES.NAME_, RES.CREATE_TIME_ from ACT_RU_TASK RES
  </select>

</mapper>

结果映射为如下的org.flowable.standalone.cfg.CustomTask类的实例:

public class CustomTask {

  protected String id;
  protected String name;
  protected Date createTime;

  public String getId() {
  return id;
  }
  public String getName() {
  return name;
  }
  public Date getCreateTime() {
  return createTime;
  }
}

必须像下面这样为流程引擎配置提供映射XML文件:

...
<property name="customMybatisXMLMappers">
  <set>
  <value>org/flowable/standalone/cfg/custom-mappers/CustomTaskMapper.xml</value>
  </set>
</property>
...

这样执行语句:

List<CustomTask> tasks = managementService.executeCommand(new Command<List<CustomTask>>() {

    @SuppressWarnings("unchecked")
    @Override
    public List<CustomTask> execute(CommandContext commandContext) {
    return (List<CustomTask>) commandContext.getDbSqlSession().selectList("selectCustomTaskList");
    }
  });

XML映射语句在需要更复杂语句的时候很好用。Flowable内部就使用XML映射语句,所以能使用底层功能。

假设某个用例下,需要基于id、name、type、userId等字段,查询附件数据。要实现这个用例,可以创建下面这样的扩展了org.flowable.engine.impl.AbstractQuery的查询类AttachmentQuery

public class AttachmentQuery extends AbstractQuery<AttachmentQuery, Attachment> {

  protected String attachmentId;
  protected String attachmentName;
  protected String attachmentType;
  protected String userId;

  public AttachmentQuery(ManagementService managementService) {
  super(managementService);
  }

  public AttachmentQuery attachmentId(String attachmentId){
  this.attachmentId = attachmentId;
  return this;
  }

  public AttachmentQuery attachmentName(String attachmentName){
  this.attachmentName = attachmentName;
  return this;
  }

  public AttachmentQuery attachmentType(String attachmentType){
  this.attachmentType = attachmentType;
  return this;
  }

  public AttachmentQuery userId(String userId){
  this.userId = userId;
  return this;
  }

  @Override
  public long executeCount(CommandContext commandContext) {
  return (Long) commandContext.getDbSqlSession()
           .selectOne("selectAttachmentCountByQueryCriteria", this);
  }

  @Override
  public List<Attachment> executeList(CommandContext commandContext, Page page) {
  return commandContext.getDbSqlSession()
      .selectList("selectAttachmentByQueryCriteria", this);
  }

请注意在扩展AbstractQuery时,子类需要为super构造函数传递一个ManagementService的实例,并需要实现executeCountexecuteList来调用映射语句。

包含映射语句的XML文件如下:

<mapper namespace="org.flowable.standalone.cfg.AttachmentMapper">

  <select parameterType="org.flowable.standalone.cfg.AttachmentQuery" resultType="long">
  select count(distinct RES.ID_)
  <include refid="selectAttachmentByQueryCriteriaSql"/>
  </select>

  <select parameterType="org.flowable.standalone.cfg.AttachmentQuery" resultMap="org.flowable.engine.impl.persistence.entity.AttachmentEntity.attachmentResultMap">
  ${limitBefore}
  select distinct RES.* ${limitBetween}
  <include refid="selectAttachmentByQueryCriteriaSql"/>
  ${orderBy}
  ${limitAfter}
  </select>

  <sql>
  from ${prefix}ACT_HI_ATTACHMENT RES
  <where>
   <if test="attachmentId != null">
   RES.ID_ = #{attachmentId}
   </if>
   <if test="attachmentName != null">
   and RES.NAME_ = #{attachmentName}
   </if>
   <if test="attachmentType != null">
   and RES.TYPE_ = #{attachmentType}
   </if>
   <if test="userId != null">
   and RES.USER_ID_ = #{userId}
   </if>
  </where>
  </sql>
</mapper>

可以在语句中使用例如分页、排序、表名前缀等功能(因为parameterType为AbstractQuery的子类)。可以使用引擎定义的org.flowable.engine.impl.persistence.entity.AttachmentEntity.attachmentResultMap来映射结果。

最后,可以这样使用AttachmentQuery

....
// 获取附件的总数
long count = new AttachmentQuery(managementService).count();

// 获取id为10025的附件
Attachment attachment = new AttachmentQuery(managementService).attachmentId("10025").singleResult();

// 获取前10个附件
List<Attachment> attachments = new AttachmentQuery(managementService).listPage(0, 10);

// 获取用户kermit上传的所有附件
attachments = new AttachmentQuery(managementService).userId("kermit").list();
....

请参考单元测试org.flowable.standalone.cfg.CustomMybatisXMLMapperTest_及src/test/java/org/flowable/standalone/cfg/与src/test/resources/org/flowable/standalone/cfg/目录中的其它类与资源,了解基于XML的映射语句的更多使用例子。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文