activiti api文档_【白银人机】Activiti 工作流从入门到入土:完整 hello world 大比拼(API 结合实例讲解)...

点击上方“好好学java”,选择“置顶”公众号

重磅资源、干货,第一时间送达94be6f74476f14946118bae62e9de71b.png

重磅推荐 144263fcb4c377d5b40f82d2ec62216b.png

① 纯福利 | 公众号资源大汇总,一年才一次!

② 重磅!!2018年Java全套入门到进阶学习视频及项目实战

③ 2018年java架构师学习视频教程资源

④ 源码系列!!spring源码深度解析

文章源码托管:

https://github.com/OUYANGSIHAI/Activiti-learninig
欢迎 star !!!

本来想着闲来无事,前面在项目中刚刚用到了工作流 Activiti 框架,写写博客的,但是,事情总是纷纷杂杂,一直拖延到现在,这一节原本想要写一下关于 Activiti 的 API ,但是,想着太多这样的博客了,而且显得太生硬,难以理解,所以,这些 API 就在实际的 demo 中来讲解。

一、建立流程图

在开始做工作流之前,我们首先应该把具体的业务在工作流的部署流程图体现出来,并且都测试通过,这样就相当于成功了一半,后面的具体业务的开发就相对轻松一些了。

首先,我们先看一看在 idea 中有哪些控件,常用的控件进行了标注。

5fd27dc759410e6bf39180a2184e12e6.png

下面我们讲一下建立一个流程图的具体过程。

首先,我们需要拉入一个开始节点bpmn 文件中,这是图像化的界面,只需要拉入即可。

a2969a768ae44bffbbfdf06ec8177f48.png

然后,我们从控件中拉入一个 UserTask 用户任务节点到 bpmn 文件中。

97143d8a5b7282e2d2fc2af961f0801f.png

这样子就有了两个审批节点了,如果还需要其他的一些业务需求,我们还可以加入一些网关,这里就暂时不加了。

最后,我们只需要一个结束节点 EndEvent 就完成了这个工作流的部署图的绘制。

8a5e3bbac714874bc42eff9f8c0ede39.png

我们最后看一下完整的例子。

669b1f5e71cfc484be3326c7cd51228c.png

看似已经完成了整个流程图的绘制,但美中不足的是我们目前并没有设置导师审批辅导员审批到底由谁来审批,所以,我们还是需要来瞅一瞅怎么设置审批人员

首先,我们需要选中一个审批节点,例如,选中导师审批这个节点。

b0f032d969a4e7c87d7e4e85c40e1b46.png

其次,我们就显而易见的可以在 idea 编辑器的左侧看到一个名为 BPMN editor 的属性框,里面包括一个用户任务节点的可以设置的所有属性

179998aa1efd724f765346ded2868bf8.png

注意:候选用户、候选组、任务监听器,这三个属性这里暂时不讲,后面再补充。

由于,这一步我们需要设置审批人,所以,我们需要在 Assignee 这个属性中设置我们的审批人。

c694cae8b4ec771752ba24b26179cfe9.png

如上图,这里设置导师审批这个节点的审批人为 sihai 。设置审批人除了直接设置之外,还有两种方式设置,后面再补充。

另外一个审批节点也通过这种方式设置就可以完成审批人的设置了。

803580999d1297d9855aa72cc0337da3.png

very good,这样就基本完成了一个流程图的创建。接下来,我们将通过实例来具体讲解Activiti 的 API 的讲解

二、实例讲解 API

在上面这个流程图的创建中,我们还没有生成 png 图片,所以,如果不知道如何生成的,可以参考之前的这篇文章:Activiti工作流从入门到入土:整合spring。

既然是讲解 API ,那么还是先看一下主要有哪些 API 吧,这样才有一个整体把握。

b79e12ded21e55ace617aa018baa142a.png

这些 API 具体怎么用,接下来一一道来。

2.1 流程定义

既然是流程定义,那肯定少不了如何部署流程定义了。

2.1.1 部署流程定义方法1
 @Autowired
    private ProcessEngine processEngine;
    @Autowired
    private TaskService taskService;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private HistoryService historyService;

    /**
     * 部署流程定义(从classpath)
     */
    @Test
    public void deploymentProcessDefinition_classpath(){
        Deployment deployment = processEngine.getRepositoryService()//与流程定义和部署对象相关的Service
                .createDeployment()//创建一个部署对象
                .name("流程定义")//添加部署的名称
                .addClasspathResource("bpmn/hello.bpmn")//从classpath的资源中加载,一次只能加载一个文件
                .addClasspathResource("bpmn/hello.png")//从classpath的资源中加载,一次只能加载一个文件
                .deploy();//完成部署
        System.out.println("部署ID:"+deployment.getId());
        System.out.println("部署名称:"+deployment.getName());
    }

注意:这里用的是整合 spring 之后的 junit 测试环境,如何整合 spring 请看这篇文章:Activiti工作流从入门到入土:整合spring。

输出结果:

15c64676b7d26e48ec8b726160c0253d.png

这样,我们就部署了这个流程。那么具体是怎么操作的呢,我们再来看看整个过程。

  • 获取流程引擎对象:这个跟 spring 整合了。

  • 通过流程引擎获取了一个 RepositoryService 对象(仓库对象)

  • 由仓库的服务对象产生一个部署对象配置对象,用来封装部署操作的相关配置。

  • 这是一个链式编程,在部署配置对象中设置显示名,上传流程定义规则文件

  • 向数据库表中存放流程定义的规则信息。

其实,这一步操作,用到了 Activiti 数据库中的三张表,分别是:act_re_deployment(部署对象表),act_re_procdef(流程定义表),act_ge_bytearray(资源文件表)。

我们看看这三张表的变化:1)act_re_deployment

822ff820021c8da7861de7d9d990653b.png

可以看到,部署ID和部署名称就存在这张表中。

2)act_re_procdef

b49528cf2d9839a265789fb8722a2969.png

这张表中,存放了部署的Deployment_ID部署流程的id、bpmn资源文件名称、png图片名称等信息。

3)act_ge_bytearray

1ada8a2e419b01274efff9fd5ba05d50.png

存储流程定义相关的部署信息。即流程定义文档的存放地。每部署一次就会增加两条记录,一条是关于 bpmn 规则文件的,一条是图片的(如果部署时只指定了 bpmn 一个文件,activiti 会在部署时解析 bpmn 文件内容自动生成流程图)。两个文件不是很大,都是以二进制形式存储在数据库中。

2.1.2 部署流程定义方法2
 /**
     * 部署流程定义(从zip)
     */
    @Testpublic void deploymentProcessDefinition_zip(){
        InputStream in = this.getClass().getClassLoader().getResourceAsStream("bpmn/hello.zip");
        ZipInputStream zipInputStream = new ZipInputStream(in);
        Deployment deployment = processEngine.getRepositoryService()//与流程定义和部署对象相关的Service
                .createDeployment()//创建一个部署对象
                .name("流程定义")//添加部署的名称
                .addZipInputStream(zipInputStream)//指定zip格式的文件完成部署
                .deploy();//完成部署
        System.out.println("部署ID:"+deployment.getId());//
        System.out.println("部署名称:"+deployment.getName());//
    }

项目结构如下:

c78a7cbbf81f5a21296001762ccf13c0.png

输出结果:

947dd937122586ddd0efcaae83b40fe6.png

如此看来,也是没有任何问题的,唯一的区别只是压缩成zip格式的文件,使用zip的输入流用作部署流程定义,其他使用并无区别。

部署了流程定义之后,我们应该想查看一下流程定义的一些信息。

2.1.3 查看流程定义
/**
     * 查询流程定义
     */
    @Testpublic void findProcessDefinition(){
        List list = processEngine.getRepositoryService()//与流程定义和部署对象相关的Service
                .createProcessDefinitionQuery()//创建一个流程定义的查询/**指定查询条件,where条件*///                        .deploymentId(deploymentId)//使用部署对象ID查询//                        .processDefinitionId(processDefinitionId)//使用流程定义ID查询//                        .processDefinitionKey(processDefinitionKey)//使用流程定义的key查询//                        .processDefinitionNameLike(processDefinitionNameLike)//使用流程定义的名称模糊查询/**排序*/
                .orderByProcessDefinitionVersion().asc()//按照版本的升序排列//                        .orderByProcessDefinitionName().desc()//按照流程定义的名称降序排列/**返回的结果集*/
                .list();//返回一个集合列表,封装流程定义//                        .singleResult();//返回惟一结果集//                        .count();//返回结果集数量//                        .listPage(firstResult, maxResults);//分页查询if(list!=null && list.size()>0){for(ProcessDefinition pd:list){
                System.out.println("流程定义ID:"+pd.getId());//流程定义的key+版本+随机生成数
                System.out.println("流程定义的名称:"+pd.getName());//对应hello.bpmn文件中的name属性值
                System.out.println("流程定义的key:"+pd.getKey());//对应hello.bpmn文件中的id属性值
                System.out.println("流程定义的版本:"+pd.getVersion());//当流程定义的key值相同的相同下,版本升级,默认1
                System.out.println("资源名称bpmn文件:"+pd.getResourceName());
                System.out.println("资源名称png文件:"+pd.getDiagramResourceName());
                System.out.println("部署对象ID:"+pd.getDeploymentId());
                System.out.println("*********************************************");
            }
        }
    }

输出结果:

ce424b6df5e4255ff954f3c6d937f989.png

查询流程定义小结:

  • 流程定义和部署对象相关的Service都是 RepositoryService ,后面会发现关于流程定义的都是 RepositoryService

  • 通过这个 createProcessDefinitionQuery() 方法来设置一些查询参数,比如通过条件、降序升序等。

2.1.4 删除流程定义

通过删除部署 ID 为2501的信息。

/**
     * 删除流程定义
     */
    @Testpublic void deleteProcessDefinition(){
        //使用部署ID,完成删除,指定部署对象id为2501删除
        String deploymentId = "2501";
        /**
         * 不带级联的删除
         *    只能删除没有启动的流程,如果流程启动,就会抛出异常
         */
//        processEngine.getRepositoryService()//
//                        .deleteDeployment(deploymentId);

        /**
         * 级联删除
         *       不管流程是否启动,都能可以删除
         */
        processEngine.getRepositoryService()//
                .deleteDeployment(deploymentId, true);
        System.out.println("删除成功!");
    }

输出结果:

93e4c17038cd0b000ef9cfc82cafc52b.png

到数据库查看,发现 act_re_deployment 中的数据已经不存在了。

70a392e1455b26f512b53276f91c8f3a.png
  • 这里还是通过 getRepositoryService() 方法获取部署定义对象,然后指定 ID 删除信息。

2.1.5 获取流程定义文档的资源

这里的作用主要是查询图片,通过图片可以在后面做流程展示用的。我们看看具体怎么查看。

/**
     * 查看流程图
     *
     * @throws IOException
     */
    @Test
    public void viewPic() throws IOException {
        /**将生成图片放到文件夹下*/
        String deploymentId = "5001";
        //获取图片资源名称
        List<String> list = processEngine.getRepositoryService()//
                .getDeploymentResourceNames(deploymentId);
        //定义图片资源的名称
        String resourceName = "";
        if (list != null && list.size() > 0) {
            for (String name : list) {
                if (name.indexOf(".png") >= 0) {
                    resourceName = name;
                }
            }
        }

        //获取图片的输入流
        InputStream in = processEngine.getRepositoryService()//
                .getResourceAsStream(deploymentId, resourceName);

        //将图片生成到F盘的目录下
        File file = new File("F:/" + resourceName);

        //将输入流的图片写到磁盘
        FileUtils.copyInputStreamToFile(in, file);
    }

在F盘下,可以找到图片。

1375de4aaa3ed67e2d81c218d0934b5c.png
2.1.6 查询最新版本的流程定义
 /**
     * 查询最新版本的流程定义
     */
    @Test
    public void findLastVersionProcessDefinition() {
        List list = processEngine.getRepositoryService()//
                .createProcessDefinitionQuery()//
                .orderByProcessDefinitionVersion().asc()//使用流程定义的版本升序排列
                .list();/**
         map集合的特点:当map集合key值相同的情况下,后一次的值将替换前一次的值
         */
        Map map = new LinkedHashMap();if (list != null && list.size() > 0) {for (ProcessDefinition pd : list) {
                map.put(pd.getKey(), pd);
            }
        }
        List pdList = new ArrayList(map.values());if (pdList != null && pdList.size() > 0) {for (ProcessDefinition pd : pdList) {
                System.out.println("流程定义ID:" + pd.getId());//流程定义的key+版本+随机生成数
                System.out.println("流程定义的名称:" + pd.getName());//对应hello.bpmn文件中的name属性值
                System.out.println("流程定义的key:" + pd.getKey());//对应hello.bpmn文件中的id属性值
                System.out.println("流程定义的版本:" + pd.getVersion());//当流程定义的key值相同的相同下,版本升级,默认1
                System.out.println("资源名称bpmn文件:" + pd.getResourceName());
                System.out.println("资源名称png文件:" + pd.getDiagramResourceName());
                System.out.println("部署对象ID:" + pd.getDeploymentId());
                System.out.println("*********************************************************************************");
            }
        }
    }

输出结果:

831924819201c6c74b130806af7e5dcd.png
2.1.7 流程定义总结

1、部署流程定义用到了 Activiti 的下面的几张表。

  • act_re_deployment:部署对象表

  • act_re_procdef:流程定义表

  • act_ge_bytearray:资源文件表

  • act_ge_property:主键生成策略表

2、我们发现部署流程定义的操作都是在 RepositoryService 这个类下进行操作的,我们只需要通过 getRepositoryService() 拿到对象,通过链式规则就可以进行部署流程定义的所有操作。

2.2 工作流完整实例的使用

这一节,我们通过一个完整的例子,来总结一下前面讲过的一些基本的知识,这样能够更好的学习前面以及后面的知识点,这也算是一个过渡的章节。

回到第一节的建立流程图,我们已经将基本的 bpmn 图已经建立好了,但是,需要做一个完整的实例,我们还是需要补充一些内容的,这样才能够把这样的一个实例做好,我们先把第一节的那个 bpmn 图拿过来。

669b1f5e71cfc484be3326c7cd51228c.png

首先,我们需要明确:这个图到目前为止,我们只是简简单单的把流程给画出来了,比如,我们需要审核的时候,是需要具体到某一个具体的人员去审核的,所以,我们需要给每个节点设置审核的具体人员。

注意:设置节点的审核人员后面还会分一节细讲,这里只是做一个简单的实例,所以,只需要这里能够看懂,做好就ok了。

设置审核人员步骤

首先,我们需要选中一个节点,例如,下图中的“导师审批”节点。

64e9acb0f65f870c75e2bd73979f9f63.png

接下来,在左边的工具栏,我们会看到好多选项,有一项为 Assignee ,我们需要在这个选项中设置我们这个节点需要设置的审批人。

Assignee设置格式:直接使用英文或者中文都可以,例如,sihai,更复杂的设置后面再讲。

f89d1bcadf51579686492074e7903be1.png

下面的节点设置也是跟上面一模一样。

辅导员审批的审批人员是:欧阳思海。

aa36215caba960fc33aa1a54a2c8b291.png

perfect,这样流程图的任务就完成了,下面我们就可以进行这个实例的测试阶段了。

1)部署流程定义
部署流程定义,在前面的章节已经讲过了,有两种方式进行处理,一种是加载 bpmn 文件和 png 文件,还有一种是将这两个文件压缩成 zip 格式的压缩文件,然后加载。这里我们使用第一种方式进行处理。

/**
     * 部署流程定义(从classpath)
     */
    @Testpublic void deploymentProcessDefinition_classpath() {
        Deployment deployment = processEngine.getRepositoryService()//与流程定义和部署对象相关的Service
                .createDeployment()//创建一个部署对象
                .name("hello")//添加部署的名称
                .addClasspathResource("bpmn/hello.bpmn")//从classpath的资源中加载,一次只能加载一个文件
                .addClasspathResource("bpmn/hello.png")//从classpath的资源中加载,一次只能加载一个文件
                .deploy();//完成部署
        log.info("部署ID:" + deployment.getId());
        log.info("部署名称:" + deployment.getName());
    }
e28f9247a37e0c3966829e57ca1ee437.png

现在流程定义已经有了,下面我们就需要启动这个流程实例。

关于关于这一步做了什么事情,可以在前面的章节查看。

2)启动流程实例

 /**
     * 启动流程实例
     */
    @Testpublic void startProcessInstance(){
        //1、流程定义的key,通过这个key来启动流程实例
        String processDefinitionKey = "hello";
        //2、与正在执行的流程实例和执行对象相关的Service
        // startProcessInstanceByKey方法还可以设置其他的参数,比如流程变量。
        ProcessInstance pi = processEngine.getRuntimeService()
                .startProcessInstanceByKey(processDefinitionKey);//使用流程定义的key启动流程实例,key对应helloworld.bpmn文件中id的属性值,使用key值启动,默认是按照最新版本的流程定义启动
        log.info("流程实例ID:"+pi.getId());//流程实例ID
        log.info("流程定义ID:"+pi.getProcessDefinitionId());//流程定义ID
    }
3c50cf139fd1832ee4e7aad4d1143168.png
注意: processDefinitionKey 是 bpmn 文件的名称。

步骤
1 获取到 runtimeService 实例。
2 通过 bpmn 文件的名称,也就是 processDefinitionKey 来启动流程实例。
3 启动流程后,流程的任务就走到了导师审批节点。

下面就是查询个人任务了,我们可以查询导师审批节点的任务。

3)查询个人任务

/**
     * 查询当前人的个人任务
     */
    @Testpublic void findPersonalTask(){
        String assignee = "sihai";
        List list = processEngine.getTaskService()//与正在执行的任务管理相关的Service
                .createTaskQuery()//创建任务查询对象/**查询条件(where部分)*/
                .taskAssignee(assignee)//指定个人任务查询,指定办理人//                        .taskCandidateUser(candidateUser)//组任务的办理人查询//                        .processDefinitionId(processDefinitionId)//使用流程定义ID查询//                        .processInstanceId(processInstanceId)//使用流程实例ID查询//                        .executionId(executionId)//使用执行对象ID查询/**排序*/
                .orderByTaskCreateTime().asc()//使用创建时间的升序排列/**返回结果集*///                        .singleResult()//返回惟一结果集//                        .count()//返回结果集的数量//                        .listPage(firstResult, maxResults);//分页查询
                .list();//返回列表if(list!=null && list.size()>0){for(Task task:list){log.info("任务ID:"+task.getId());log.info("任务名称:"+task.getName());log.info("任务的创建时间:"+task.getCreateTime());log.info("任务的办理人:"+task.getAssignee());log.info("流程实例ID:"+task.getProcessInstanceId());log.info("执行对象ID:"+task.getExecutionId());log.info("流程定义ID:"+task.getProcessDefinitionId());log.info("********************************************");
            }
        }
    }

通过 sihai 这个审批人,查询到了下面的信息。

53001e3655e2ed58760db7f6b029f1ef.png

分析步骤
1 首先通过 getTaskService 方法,获取到 TaskService 对象。
2 通过 createTaskQuery 方法创建查询对象。
3 通过 taskAssignee 方法设置审核人。
4 对于结果的返回,我们可以通过 orderByTaskCreateTime().asc() 设置排序等其他信息。

这里需要注意一点,查询到的一个重要的信息是:任务 id(taskId),下一步,我们需要通过这个任务 id ,来完成任务。

4)办理个人任务

/**
     * 完成我的任务
     */
    @Testpublic void completePersonalTask() {
        //任务ID,上一步查询得到的。
        String taskId = "7504";
        processEngine.getTaskService()//与正在执行的任务管理相关的Service
                .complete(taskId);
        log.info("完成任务:任务ID:" + taskId);
    }

通过上一步的任务 id :7504,完成任务。

c44971fa97d978f70d4cb226c1861365.png

步骤
1 首先,通过 getTaskService 方法拿到 TaskService 对象。
2 调用 complete 方法,给定具体的任务 id 完成任务。

5)查询流程状态(判断流程走到哪一个节点)
这个接口还是十分需要的,当我们在具体的业务中,我们需要判断我们的流程的状态是什么状态,或者说我们的流程走到了哪一个节点的时候,这一个接口就让我们实现业务省了非常多的事情。

/**
     * 查询流程状态(判断流程走到哪一个节点)
     */
    @Testpublic void isProcessActive() {
        String processInstanceId = "7501";
        ProcessInstance pi = processEngine.getRuntimeService()//表示正在执行的流程实例和执行对象
                .createProcessInstanceQuery()//创建流程实例查询
                .processInstanceId(processInstanceId)//使用流程实例ID查询
                .singleResult();
        if (pi == null) {
            log.info("流程已经结束");
        } else {
            log.info("流程没有结束");
            //获取任务状态
            log.info("节点id:" + pi.getActivityId());
        }
    }
80bdf9b966bef5fc06eae2234f66d954.png

步骤:
1 获取到流程实例 ProcessInstance 对象。
2 通过 getActivityId 方法获取到实例 Id(节点 id )。

那么拿到了节点 Id 有什么作用呢?
其实,有了这个 Id 之后,我们就可以判断流程走到哪一步了。例如,上面的输出的节点 id 是 _4,这个 _4 就是对应 辅导员审批节点的 id,所以,我们就可以判读流程其实是已经走到这个节点了,后期需要在页面显示流程状态的时候就发挥作用了。

c06880cbdaead6e2397bb3f1ddff323b.png

6)查询流程执行的历史信息
通过查看 activiti 5 的官方 API 接口,发现查看历史信息有下面的查询接口。

39e699f2f00b8b04db0ed7fce3e8d91a.png

下面我们通过上面的实例对下面的方法一一进行测试。

历史活动实例查询接口

/**
     * 历史活动查询接口
     */
    @Testpublic void findHistoryActivity() {
        String processInstanceId = "7501";
        List hais = processEngine.getHistoryService()//
                .createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                .list();for (HistoricActivityInstance hai : hais) {log.info("活动id:" + hai.getActivityId()
                    + "   审批人:" + hai.getAssignee()
                    + "   任务id:" + hai.getTaskId());log.info("************************************");
        }
    }
deae31a1df3ce9ffe84cbe188a738bcc.png

通过这个接口不仅仅查到这些信息,还有其他的方法,可以获取更多的关于历史活动的其他信息。

历史流程实例查询接口

/**
     * 查询历史流程实例
     */
    @Testpublic void findHistoryProcessInstance() {
        String processInstanceId = "7501";
        HistoricProcessInstance hpi = processEngine.getHistoryService()// 与历史数据(历史表)相关的Service
                .createHistoricProcessInstanceQuery()// 创建历史流程实例查询
                .processInstanceId(processInstanceId)// 使用流程实例ID查询
                .orderByProcessInstanceStartTime().asc().singleResult();
        log.info(hpi.getId() + "    " + hpi.getProcessDefinitionId() + "    " + hpi.getStartTime() + "    "
                + hpi.getEndTime() + "     " + hpi.getDurationInMillis());
    }
3aa6c7b8f6c9d17d1c8bba751d443187.png

这个接口可以查询到关于历史流程实例的所有信息。

历史任务实例查询接口

 /**
     * 查询历史任务
     */
    @Testpublic void findHistoryTask() {
        String processInstanceId = "7501";
        List list = processEngine.getHistoryService()// 与历史数据(历史表)相关的Service
                .createHistoricTaskInstanceQuery()// 创建历史任务实例查询
                .processInstanceId(processInstanceId)//
                .orderByHistoricTaskInstanceStartTime().asc().list();if (list != null && list.size() > 0) {for (HistoricTaskInstance hti : list) {log.info("\n 任务Id:" + hti.getId() + "    任务名称:" + hti.getName() + "    流程实例Id:" + hti.getProcessInstanceId() + "\n 开始时间:"
                        + hti.getStartTime() + "   结束时间:" + hti.getEndTime() + "   持续时间:" + hti.getDurationInMillis());
            }
        }
    }
162a93e3d10717125f01614ba525da6b.png

这个查询接口可以查询到历史任务信息

历史流程变量查询接口

/**
     * 查询历史流程变量
     */
    @Testpublic void findHistoryProcessVariables() {
        String processInstanceId = "7501";
        List list = processEngine.getHistoryService()//
                .createHistoricVariableInstanceQuery()// 创建一个历史的流程变量查询对象
                .processInstanceId(processInstanceId)//
                .list();if (list != null && list.size() > 0) {for (HistoricVariableInstance hvi : list) {log.info("\n" + hvi.getId() + "   " + hvi.getProcessInstanceId() + "\n" + hvi.getVariableName()
                        + "   " + hvi.getVariableTypeName() + "    " + hvi.getValue());
            }
        }
    }

在这个实例中没有设置流程变量,所以,这里是查询不到任何历史信息的。

这个接口主要是关于历史流程变量的设置的一些信息。

历史本地接口查询接口

/**
     * 通过执行sql来查询历史数据,由于activiti底层就是数据库表。
     */
    @Testpublic void findHistoryByNative() {
        HistoricProcessInstance hpi = processEngine.getHistoryService()
                .createNativeHistoricProcessInstanceQuery()
                .sql("查询底层数据库表的sql语句")
                .singleResult();
        log.info("\n" + hpi.getId() + "    " + hpi.getProcessDefinitionId() + "    " + hpi.getStartTime()
                + "\n" + hpi.getEndTime() + "     " + hpi.getDurationInMillis());
    }

这个接口是提供直接通过 sql 语句来查询历史信息的,我们只需要在 sql() 方法中写原生的 sql 语句就可以进行数据查询。

写到这里,我想应该通过这样的一个完整的实例将 Activiti 工作流的 API 都介绍的差不多了,这一节到这里也就要说拜拜了。再回看一下文章开头的 API 接口,这也算是这一节的总结。

b79e12ded21e55ace617aa018baa142a.png

1. 重磅!!Java基础就业课程,IDEA版本

2. 物流云商项目源码及教程

3. 疯狂学习 SpringCloud 教程

4. dubbo 最新学习视频教程

0a72fe66cff78cc4a8273faccd2256e1.png

b864ba5a7f106f9e679b5c49389f6c31.png

你「在看」吗??

?点击「阅读原文」,更多精彩

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/367403.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端解读控制反转(IOC)

前言 随着前端承担的职责越来越重&#xff0c;前端应用向着复杂化、规模化的方向发展。大型项目模块化是一种趋势&#xff0c;不可避免模块之间要相互依赖&#xff0c;此外还有很多第三方包。这样的话如何去管理这些繁杂的文件&#xff0c;是一个不可避免的话题。此时作为一种…

ASP.NET MVC传递参数(model), 如何保持TempData的持久性

一看到此标题&#xff0c;相信你也会。因为路由是可以从URL地址栏传过去的。但是Insus.NET不想在地址栏传递&#xff0c;还是一个条件是jQuery的Ajax进行POST的。Insus.NET不清楚别人是怎样处理的&#xff0c;但是这个让Insus.NET花上不少时间来解决。Insus.NET实现的方法也很简…

深度学习语音降噪方法对比_人工智能-关于深度学习的基础方法

深度学习概述深度学习的一些简介&#xff0c;其要点如下&#xff1a;深度学习实际上是基于具有多个隐藏层的神经网络的学习&#xff1b;深度学习的思想来源于人类处理视觉信息的方式&#xff1b;深度学习的发展得益于数据的井喷和计算力的飙升&#xff1b;深度学习的理论基础依…

通过adb巧用monkey获取android设备中所有应用的主activity

由于工作需要&#xff0c;想获取所有应用的activity(这里仅限应用的主入口Launcher activity)&#xff0c;搜索了一下&#xff0c;网上实现的方案都是基于android编程实现的&#xff0c;对于不懂开发的测试来说稍有难度&#xff0c;而且对于PC端测试工具来说可行性略差。 给大家…

PWA · 前后端协作 · Node | JTalk 掘金线下活动第七期

通知 余票仅剩 3张&#xff0c;欢迎加入活动群获取更多通知。北京的小伙伴可以关注我们的 JTalk 第八期&#xff1a;前端安全 | 美团点评技术团队专场 ? 报名 扫码报名&#xff1a; 报名链接&#xff1a;点我报名 ? 介绍 本期 JTalk 来杭州啦&#xff01; 掘金线下活动 J…

指数函数中x的取值范围_基本初等函数I: 指数函数、对数函数和幂函数

本文大约4800字, 建议学习时间1个小时.在学习过一次函数和二次函数(修改版)后, 我们知道, 一次函数ykxb当一次项系数k大于零时是增函数, 小于零时是减函数. 二次函数yax2bxc当二次项系数a大于零时图象沿x轴从左向右先减后增, a小于零时先增后减. 可以想象, 次数更高的函数, 在定…

Linux系统之TroubleShooting(启动故障排除)

尽管Linux系统非常强大&#xff0c;稳定&#xff0c;但是我们在使用过程当中&#xff0c;如果人为操作不当&#xff0c;仍然会影响系统&#xff0c;甚至可能使得系统无法开机&#xff0c;无法运行服务等等各种问题。那么这篇博文就总结一下一些常见的故障排除方法&#xff0c;但…

皮肤可配置化:变量、样式分离

之前皮肤开发了一个版本&#xff0c;抽是抽出来了&#xff0c;但是变量只抽出了几个颜色&#xff0c;没什么价值&#xff08;上个版本开发过程&#xff09;&#xff0c; 这次我又进行了一次迭代&#xff0c;现在是一个较成熟的版本了。整体理一下思路&#xff0c;可以总结为3步…

【考试记录】4.8 Path (网络流 —— 劲题)

手抄代码 学习指针 冥思苦想一晚上终于——在一瞬间开窍了。果然题目都是这样&#xff1a;突破了一个点&#xff0c;一切都是柳暗花明。 题面描述&#xff1a; 样例&#xff1a; 这道题目&#xff0c;首先注意到给定的边的性质&#xff1a;这些边在平面上构成了一棵树&#x…

Servlet 3.0概述

Servlet 3.0 –具有许多突破性的功能&#xff0c;这些功能最终将改变开发人员编写和编写JEE Web应用程序的方式–有人可能会说&#xff0c;它的约定违背了我们对事物在理论上“应该”如何工作的大多数理解&#xff0c;但这就是其中之一。创新和持续改进&#xff1b; 挑战公约并…

MySQL(介绍,安装,密码操作,权限表)

一、数据库介绍1、数据库相关概念a、支持并发b、锁的问题c、对客户端请求进行认证d、存取效率&#xff08;降低IO次数&#xff09;数据库服务器&#xff08;本质就是一个台计算机&#xff0c;该计算机之上安装有数据库管理软件的服务端&#xff09;数据库管理管理系统RDBMS&…

matlab如何测两点的角度_【邢不行|量化小讲堂系列01-Python量化入门】如何快速上手使用Python进行金融数据分析...

引言:邢不行的系列帖子“量化小讲堂”&#xff0c;通过实际案例教初学者使用python进行量化投资&#xff0c;了解行业研究方向&#xff0c;希望能对大家有帮助。【历史文章汇总】请点击此处【必读文章】&#xff1a;【邢不行|量化小讲堂系列27-Python量化入门】EOS期现套利&…

结对项目

一、【Coding.Net项目地址】https://git.coding.net/verde/Pair_Work.git 二、【对接口进行的设计】 看教科书和其它资料中关于Information Hiding, Interface Design, Loose Coupling的章节&#xff0c;说明你们在结对编程中是如何利用这些方法对接口进行设计的。 如下图SRC的…

国家开放大学形成性考核 统一资料 参考试题

试卷代号&#xff1a;1174 水工钢筋混凝土结构&#xff08;本&#xff09;参考试题 一、选择题&#xff08;每小题2分&#xff0c;共20分&#xff0c;在所列备选项中&#xff0c;选1项正确的或最好的作为答案&#xff0c;将选项号填入各题的括号中&#xff09; 1.钢筋混凝土结…

两个向量之间的夹角公式_向量的内积

向量的内积也叫向量的数量积、点积。我们定义两个向量的内积是一个数: 其中 是这两个向量的夹角。 对于向量的内积,最重要的一个结论是: 定理1:两向量垂直的充分必要条件是它们的内积为 0,即 这个定理我们几乎不用证明了,因为从定义来看,如果两个向量都不零向量,则只能…

springcloud 入门 10 (eureka高可用)

eureka高可用: 说白了&#xff0c;就是加一个实例作为原实例的备份&#xff0c;然后一起对外提供服务。这样可以保证在一台机器宕机的时候&#xff0c;整个系统不会死掉。保证其继续对外服务。 eureka的集群化&#xff1a; 服务注册中心Eureka Server&#xff0c;是一个实例&am…

为什么REST如此重要

这篇文章致力于REST&#xff0c;一种塑造Web服务的体系结构风格&#xff0c;以及IT历史上最容易被误解的概念。 这篇文章针对的是那些正在设计Web服务api的人&#xff0c;他们并未完全了解REST的实际含义。 我想给你个主意。 这篇文章也发给了那些想知道REST意味着什么的人&…

你真的懂js获取可视区宽高吗

可能你会觉得获取可视区宽高不是很简单吗 原生js获取高度不就是就window.innerHeight一句话的事&#xff0c;可是真的这么简单吗 来看个测试页面,如果页面带有横向纵向的滚动条&#xff0c;我们打印出各个高度进行查看对比 顺便你也可以看看document.body和document.documentEl…

詹金斯的Maven报告

代码质量是一个敏感的话题。 它会影响您的维护成本以及客户满意度。 更不用说您的开发人员使用代码的动力。 谁想要修复难看的代码&#xff0c;对吗&#xff1f; 讨论代码质量总是需要事实和数字&#xff01; 因此&#xff0c;这是一个简短的教程&#xff0c;介绍如何创建一些…

python实现矩阵叉乘_矩阵乘法的纯Python实现 | 离开Python库!!

点击关注我哦一篇文章带你了解矩阵乘法的纯Python实现在《这篇文章》中&#xff0c;我们有简单提到“矩阵乘法”的相关知识&#xff0c;如果你不记得了&#xff0c;可以复习一下这张图片。想起来了没&#xff1f;本篇文章将深入探讨在没有机器学习库的情况下如何从零实现矩阵乘…