关于workflow:Flowable-流程部署与删除

39次阅读

共计 6121 个字符,预计需要花费 16 分钟才能阅读完成。

本文咱们一起来看看流程的部署等细节问题。

其实当咱们应用了 Spring Boot 之后,默认状况下流程是会主动部署的,基本上不须要咱们额定做什么事件。不过这些操作里还是有不少细节,明天松哥就来带大家一起来梳理一下。

1. 默认行为

首先咱们先来梳理一下默认行为。

默认状况下,咱们放在 resources/processes 目录下的所有流程文件会主动被部署,流程文件的后缀有两种模式 bpmn20.xml 或者 bpmn。当然,无论是寄存流程文件的地位,还是流程文件的格局,都是能够定制的,波及到的属性次要有三个,可在 application.properties 中进行配置:

flowable.check-process-definitions=false
flowable.process-definition-location-prefix=classpath*:/processes/
flowable.process-definition-location-suffixes=**.bpmn20.xml,**.bpmn
  • flowable.check-process-definitions:这个示意是否在我的项目启动的时候,去查看文件目录是否有对应的流程文件,该属性为 true 示意如果有流程文件就主动部署,false 示意不查看,那么也就不会主动部署。
  • flowable.process-definition-location-prefix:这个是流程文件的地位,默认就是 classpath*:/processes/,当然开发者也能够进行配置。
  • flowable.process-definition-location-suffixes:这个是流程文件的后缀,默认有两个,别离是 **.bpmn20.xml**.bpmn,当然开发者也能够进行配置。

这个配置应该没啥好说的。

2. 动静部署

有的时候,咱们的流程可能并不是提前设计好的,而是我的项目启动之后,动静部署的,例如我的项目启动胜利之后,动静上传一个流程的 XML 文件进行部署,这也是一种比拟常见的场景,对于这种状况,咱们能够依照如下形式进行部署:

@RestController
public class ProcessDeployController {

    @Autowired
    RepositoryService repositoryService;

    @PostMapping("/deploy")
    public RespBean deploy(MultipartFile file) throws IOException {DeploymentBuilder deploymentBuilder = repositoryService.createDeployment()
                .category("javaboy 的工作流分类")
                .name("javaboy 的工作流名称")
                .addInputStream(file.getOriginalFilename(), file.getInputStream())
                .key("javaboy 的工作流 key");
        Deployment deployment = deploymentBuilder
                .deploy();
        return RespBean.ok("部署胜利",deployment.getId());
    }
}

我这里给了一个简略的文件上传接口,对于文件上传局部我就不多说了。咱们来看下流程部署:

  • 首先通过 repositoryService.createDeployment() 办法来创立一个流程部署构建器,即 DeploymentBuilder。
  • 接下来为 DeploymentBuilder 设置分类、名称以及 key 等属性。
  • 要害的办法是 addInputStream,通过该办法去指定流程文件。官网的提供的指定流程文件的形式有好几种;除了 addInputStream 之外,另外还有一个 addString,这个就是将流程文件转为一个字符串传入进来;addBytes 是将流程文件转为字节数组传进来;addClasspathResource 办法则是间接从 classpath 目录上来加载流程文件,这几个办法依据本人的应用场景抉择一个适合的办法去调用即可。
  • 这里有一个须要跟大家强调的中央,就是 addInputStream/addBytes/addString 等办法都须要设置资源名,这个名称是能够随便设置的,然而留神名称的后缀,须要是 bpmn20.xml 或者 bpmn,否则流程没有部署。 为什么流程名后缀要是 bpmn20.xml 或者 bpmn 呢?参考第一大节。

3. 表剖析

在咱们的流程部署过程中,一共有三张表参加到咱们的工作中了,尽管松哥之前曾经写过文章和大家梳理 flowable 中各个数据表的作用,不过过后只是大抵上介绍了一下,没有细说,这次咱们就先来看看这次波及到的三张表。

ACT_RE_DEPLOYMENT

这个表是流程部署表,每部署一个流程,这张表中就会新增一条记录,用来形容咱们刚刚定义好的流程:

这里的 ID_、NAME_、CATEGORY_ 等等,就是咱们在部署流程的时候设置的参数。

ACT_RE_PROCDEF

这是流程定义表,咱们每定义的一个流程,都会记录在这张表中:

这张表中的字段比拟多,我这里只是列出来了其中一部分。这这表中有一个 DEPLOYMENT_ID 字段,和 ACT_RE_DEPLOYMENT 表进行关联,所以 ACT_RE_DEPLOYMENT 和 ACT_RE_PROCDEF 表的关系,其实是一对一的关系,部署表中的一条记录对应定义表中的一条记录。

另外,该表中有一个 CATEGORY_ 的字段,这个字段示意流程的分类,留神这个和部署的分类可不一样,流程部署的分类参数第二大节的代码,流程的分类,说白了其实就是咱们流程定义 XML 文件中的 targetNamespace 属性,如下图:

大家可依据本人的理论需要去批改 targetNamespace 属性的值,这个值改了之后,ACT_RE_PROCDEF 表中的 CATEGORY_ 字段也会跟着发生变化。

另外,该表中还有一个 VERSION_ 字段,这个看名字就晓得是形容记录的版本号,当咱们批改了流程的内容之后,重新部署的时候,ACT_RE_DEPLOYMENT 表和 ACT_RE_PROCDEF 表均会主动减少一条记录数,其中,流程定义表 ACT_RE_PROCDEF 中的记录的 VERSION_ 字段的值会主动加 1,这样咱们就可能看到不同历史版本的流程定义。

那么零碎是怎么辨认批改后的流程和前一个流程是同一个呢?次要是靠流程的 id 属性,如下图:

这个流程的 id 属性,对应到表中,就是 ACT_RE_PROCDEF 表的 KEY_ 字段。

ACT_GE_BYTEARRAY

波及到的第三张表是这个通用数据存储表,这个表的字段比拟少,如下图:

小伙伴们看到,这个表中有一个 DEPLOYMENT_ID 字段,这个就是跟 ACT_RE_DEPLOYMENT 表关联的字段,一条流程部署记录在 ACT_GE_BYTEARRAY 表中对应两条记录,别离是记录 XML 文件和记录流程图片。

这个表中有一个 BYTES_ 字段,咱们部署的流程的 XML 文件就保留在这里,同时,零碎默认还会依据 XML 文件生成一张流程图片,也保留在这里,图片就像上面这种:

所以一个流程部署,在这张表中对应两条记录,一条记录 XML 文件,一条记录流程图片。

好啦,这就是流程部署波及到的三张表。

4. 查问操作

接下来,强烈建议大家在 Spring Boot 的 application.properties 中增加如下配置,开启 flowable 日志:

logging.level.org.flowable=debug

这个配置示意开启 flowable 的日志,开启日志的益处是咱们能够看到底层的 SQL,学习 flowable,调用 API 的时候,不能只把握 API 的应用,调用 API 的时候,心里想着这是操作哪张表,学起来更快。

4.1 查问部署信息

例如咱们当初想要查问流程的部署信息,如下:

@Test
void test01() throws IOException {List<Deployment> list = repositoryService.createDeploymentQuery().list();
    for (Deployment deployment : list) {logger.info("id:{};key:{}", deployment.getId(), deployment.getKey());
    }
}

创立一个查询器,而后返回所有的流程部署信息并打印进去,咱们看下此时 IDEA 控制台打印进去的 SQL 信息:

能够看到,底层执行的 SQL 其实就是去查问 ACT_RE_DEPLOYMENT 表。

当初咱们再去看一些查问的办法,应该就很容易明确其含意了:

小伙伴们看到,咱们能够利用流程部署的名字、分类、ID 等各种信息去查问,能够准确匹配也能够含糊匹配。

例如我想查问 key 为 javaboy 的工作流 key 的流程部署文件,然而这个流程我之前部署过屡次(版本升级),当初我想查问最近一次的流程部署信息,查问形式如下:

@Test
void test01() throws IOException {Deployment deployment = repositoryService.createDeploymentQuery().deploymentKey("javaboy 的工作流 key").latest().singleResult();
    logger.info("id:{};key:{}", deployment.getId(), deployment.getKey());
}

咱们来看下控制台打印进去的 SQL:

--- [main] i.p.e.D.selectDeploymentsByQueryCriteria : ==>  Preparing: SELECT RES.* from ACT_RE_DEPLOYMENT RES WHERE RES.KEY_ = ? and RES.DEPLOY_TIME_ = (select max(DEPLOY_TIME_) from ACT_RE_DEPLOYMENT where KEY_ = RES.KEY_ and DERIVED_FROM_ is null and ((TENANT_ID_ IS NOT NULL and TENANT_ID_ = RES.TENANT_ID_) or (TENANT_ID_ IS NULL and RES.TENANT_ID_ IS NULL) ) ) order by RES.ID_ asc
--- [main] i.p.e.D.selectDeploymentsByQueryCriteria : ==> Parameters: javaboy 的工作流 key(String)
--- [main] i.p.e.D.selectDeploymentsByQueryCriteria : <==      Total: 1

这个 SQL 写的有点简单,然而认真看就一个意思,给定查问的 key 是 javaboy 的工作流 key,查问工夫是一个最大的工夫,这就很好懂了。

4.2 查问流程定义信息

接下来咱们再来看看查问流程的定义信息。查问所有的流程定义信息,如下:

@Test
void test02() {List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().list();
    for (ProcessDefinition pd : list) {logger.info("id:{};key:{};version:{};",pd.getId(),pd.getKey(),pd.getVersion());
    }
}

来看看控制台打印的 SQL:

能够看到,就是去流程定义表 ACT_RE_PROCDEF 去查看所有。

基于此,其余的查问 API 就都好了解了,例如依据流程定义的 KEY 去查问所有的流程定义,这个 KEY 其实就是流程定义 XML 文件中的 id:

@Test
void test02() {List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().processDefinitionKey("javaboy_submit_an_expense_account").list();
    for (ProcessDefinition pd : list) {logger.info("id:{};key:{};version:{};",pd.getId(),pd.getKey(),pd.getVersion());
    }
}

咱们来看下查问 SQL:

其余的查问 API 我就不挨个演示了,办法基本上都是见名知意的。

4.3 原生查问

要是感觉用 API 查问 不过瘾,咱们也能够本人写 SQL 查问。

跟后面一样,例如我想查问 key 为 javaboy 的工作流 key 的流程部署文件,然而这个流程我之前部署过屡次(版本升级),当初我想查问最近一次的流程部署信息,查问形式如下:

@Test
void test03() {Deployment deployment = repositoryService.createNativeDeploymentQuery().sql("SELECT RES.* from ACT_RE_DEPLOYMENT RES WHERE RES.KEY_ = #{key} and RES.DEPLOY_TIME_ = (select max(DEPLOY_TIME_) from ACT_RE_DEPLOYMENT where KEY_ = RES.KEY_) order by RES.ID_ asc").parameter("key", "javaboy 的工作流 key").singleResult();
    logger.info("id:{};key:{}", deployment.getId(), deployment.getKey());
}

本人写 SQL 即可,参数的占位符用 #,因为这里底层的 SQL 操作实际上是 MyBatis。

雷同的情理,我想依据流程定义的 KEY 去查问流程定义信息,应用原生查问形式如下:

@Test
void test04() {List<ProcessDefinition> list = repositoryService.createNativeProcessDefinitionQuery()
            .sql("SELECT RES.* from ACT_RE_PROCDEF RES WHERE RES.KEY_ = #{key} order by RES.ID_ asc")
            .parameter("key", "javaboy_submit_an_expense_account").list();
    for (ProcessDefinition pd : list) {logger.info("id:{};key:{};version:{};", pd.getId(), pd.getKey(), pd.getVersion());
    }
}

好啦,对于流程的部署和定义就和大家聊这么多,下篇文章咱们来看流程实例~

正文完
 0