工作流(Workflow) – 工作流简介
数据库
Activiti的后台是有数据库的支持,所有的表都以ACT_开头。 第二部分是表示表的用途的两个字母标识。 用途也和服务的API对应。
- ACT_RE_: 'RE’表示repository。 这个前缀的表包含了流程定义和流程静态资源 (图片,规则,等等)。
- ACT_RU_*: 'RU’表示runtime。 这些运行时的表,包含流程实例,任务,变量,异步任务,等运行中的数据。
- Activiti只在流程实例执行过程中保存这些数据, 在流程结束时就会删除这些记录。 这样运行时表可以一直很小速度很快。
- ACT_ID_*: 'ID’表示identity。 这些表包含身份信息,比如用户,组等等。
- ACT_HI_*: 'HI’表示history。 这些表包含历史数据,比如历史流程实例, 变量,任务等等。
- ACT_GE_*: 通用数据, 用于不同场景下,如存放资源文件。
表结构操作
-
资源库流程规则表
- act_re_deployment 部署信息表
- act_re_model 流程设计模型部署表
- act_re_procdef 流程定义数据表
-
运行时数据库表
- act_ru_execution 运行时流程执行实例表
- act_ru_identitylink 运行时流程人员表,主要存储任务节点与参与者的相关信息
- act_ru_task 运行时任务节点表
- act_ru_variable 运行时流程变量数据表
-
历史数据库表
- act_hi_actinst 历史节点表
- act_hi_attachment 历史附件表
- act_hi_comment 历史意见表
- act_hi_identitylink 历史流程人员表
- act_hi_detail 历史详情表,提供历史变量的查询
- act_hi_procinst 历史流程实例表
- act_hi_taskinst 历史任务实例表
- act_hi_varinst 历史变量表
-
组织机构表
- act_id_group 用户组信息表
- act_id_info 用户扩展信息表
- act_id_membership 用户与用户组对应信息表
- act_id_user 用户信息表
这四张表很常见,基本的组织机构管理,关于用户认证方面建议还是自己开发一套,组件自带的功能太简单,使用中有很多需求难以满足
-
通用数据表
- act_ge_bytearray 二进制数据表
- act_ge_property 属性数据表存储整个流程引擎级别的数据,初始化表结构时,会默认插入三条记录,
activiti 配置
activiti的配置文件
activiti.cfg.xml
-
Activiti核心配置文件,配置流程引擎创建工具的基本参数和数据库连接池参数。
定义数据库配置参数:
jdbcUrl: 数据库的JDBC URL。
jdbcDriver: 对应不同数据库类型的驱动。
jdbcUsername: 连接数据库的用户名。
jdbcPassword: 连接数据库的密码。 -
基于JDBC参数配置的数据库连接 会使用默认的MyBatis连接池。 下面的参数可以用来配置连接池(来自MyBatis参数):
jdbcMaxActiveConnections: 连接池中处于被使用状态的连接的最大值。默认为10。
jdbcMaxIdleConnections: 连接池中处于空闲状态的连接的最大值。
jdbcMaxCheckoutTime: 连接被取出使用的最长时间,超过时间会被强制回收。 默认为20000(20秒)。
jdbcMaxWaitTime: 这是一个底层配置,让连接池可以在长时间无法获得连接时, 打印一条日志,并重新尝试获取一个连接。(避免因为错误配置导致沉默的操作失败)。 默认为20000(20秒)。
核心组件介绍
工作流引擎 ProcessEngine对象
这是Activiti工作的核心。负责生成流程运行时的各种实例及数据、监控和管理流程的运行。 工作流的API所有的调用都要用到工作流引擎。
关键对象
- Deployment:流程部署对象,部署一个流程时创建。
- ProcessDefinitions:流程定义,部署成功后自动创建。
- ProcessInstances:流程实例,启动流程时创建。
- Task:任务,在Activiti中的Task仅指有角色参与的任务,即定义中的UserTask。
- Execution:执行计划,流程实例和流程执行中的所有节点都是Execution,如UserTask、ServiceTask等。
服务接口
- 一个是ProcessEngineConfiguration,这个是用来获取流程引擎配置 ,activiti基础参数的配置,如:数据源什么的。
- 一个是activiti的八个service接口,这是使用activiti的关键,每个接口都有不同的职责。而这些接口需要通过ProcessEngine来获取,activiti为了和spring整合提供了ProcessEngineFactroyBean,可通过它来获取ProcessEngin。
- RepositoryService: Activiti 中每一个不同版本的业务流程的定义都需要使用一些定义文件,部署文件和支持数据 ( 例如 BPMN2.0 XML 文件,表单定义文件,流程定义图像文件等 ),这些文件都存储在 Activiti 内建的 Repository 中。Repository Service 提供了对 repository 的存取服务。
- RuntimeService:在 Activiti 中,每当一个流程定义被启动一次之后,都会生成一个相应的流程对象实例。Runtime Service 提供了启动流程、查询流程实例、设置获取流程实例变量等功能。此外它还提供了对流程部署,流程定义和流程实例的存取服务。
- TaskService:在 Activiti 中业务流程定义中的每一个执行节点被称为一个 Task,对流程中的数据存取,状态变更等操作均需要在 Task 中完成。Task Service 提供了对用户 Task 和 Form 相关的操作。它提供了运行时任务查询、领取、完成、删除以及变量设置等功能。
- IdentityService:Activiti 中内置了用户以及组管理的功能,必须使用这些用户和组的信息才能获取到相应的 Task。Identity Service 提供了对 Activiti 系统中的用户和组的管理功能。
- ManagementService:Management Service 提供了对 Activiti 流程引擎的管理和维护功能,这些功能不在工作流驱动的应用程序中使用,主要用于 Activiti 系统的日常维护。
- HistoryService: History Service 用于获取正在运行或已经完成的流程实例的信息,与 Runtime Service 中获取的流程信息不同,历史信息包含已经持久化存储的永久信息,并已经被针对查询优化。
- FormService: Activiti 中的流程和状态 Task 均可以关联业务相关的数据。通过使用 Form Service 可以存取启动和完成任务所需的表单数据并且根据需要来渲染表单。
用引擎获取其他service方法
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();//获取引擎
DynamicBpmnService dbService = processEngine.getDynamicBpmnService();//获取需要的Service
操作
- 开始一个流程实例
在把流程定义部署到流程引擎之后,我们就能启动一个新的流程实例。对于每一个流程定义来说,会有很多典型的流程实例。当流程定义的一个流程实例正在运行期,这个流程定义是’buleprint’。
- 每一件和流程运行状态的事情,都会被RuntimeService监控。
有各种各样的方法去启动一个新的流程实例。在下面的代码片段中,我们用我们在流程定义xml中定义的key去启动一个流程实例。我们也会在流程实例启动的时候,提供一些处理过的变量。因为第一个用户任务的描述会用到他们在它的表达式中。因为他们为一个确定的流程定义中的流程实例赋予意义的时候,这些变量通常会被用到。典型的来说,这些变量是让流程实例区别于其他实例的因素。
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("employeeName", "Kermit");
variables.put("numberOfDays", new Integer(4));
variables.put("vacationMotivation", "I'm really tired!");
RuntimeService runtimeService = processEngine.getRuntimeService();
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("vacationRequest", variables);
// Verify that we started a new process instance
Log.info("Number of process instances: " + runtimeService.createProcessInstanceQuery().count());
- 正在完成的任务
当一个流程开始的时候,第一步会成为一个用户的任务。这是一个必须被系统用户执行的步骤。典型的来说,这样的用户将会有一个’inbox of tasks’,'inbox of tasks’列出了所有需要被这个用户完成的任务。下面的代码就展现了这样一个查询怎么被执行:
// Fetch all tasks for the management group
TaskService taskService = processEngine.getTaskService();
List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("management").list();
for (Task task : tasks) {
Log.info("Task available: " + task.getName());
}
继续流程实例,我们需要完成这个任务。对于Activiti引擎来说,这意味着你需要这个任务。下面的代码断展示了怎么做的:
Task task = tasks.get(0);
Map<String, Object> taskVariables = new HashMap<String, Object>();
taskVariables.put("vacationApproved", "false");
taskVariables.put("managerMotivation", "We have a tight deadline!");
taskService.complete(task.getId(), taskVariables);
流程实例会继续到下一步,在这个例子中,这又会成为第一个布骤,因为这个任务没有被审核过。
- 暂停和激活一个流程
暂停一个流程定义是可能的。当一个流程定义被暂停,一个新的流程实例不会被创建(一个异常将会被抛出)。通过RepositoryService去暂停一个流程定义。
repositoryService.suspendProcessDefinitionByKey("vacationRequest");
try {
runtimeService.startProcessInstanceByKey("vacationRequest");
} catch (ActivitiException e) {
e.printStackTrace();
}
重新激活一个流程定义,通常的调用repositoryService.activateProcessDefinitionXXX方法中的一个方法。
暂停一个流程实例也有可能。当暂停的时候,这个流程不会继续下去并且没有计划会被执行。暂停流程实例可以通过调用runtimeService.suspendProcessInstance()方法.再次激活流程实例可以调用runtimeService.activateProcessInstanceXXX方法。
- 查询API
从引擎中查询数据有两种方法:查询API和本地查询。查询API允许完全安全的用流畅的API编程。你能添加各种各样的条件到你的查询中并且精确地排序。下面的代码是个例子:
List<Task> tasks = taskService.createTaskQuery()
.taskAssignee("kermit")
.processVariableValueEquals("orderId", "0815")
.orderByDueDate().asc()
.list();
有时,你需要更强大的查询,用一个OR操作或者约束的查询,用API,你不能明确的实现。对于这种情况,我们介绍本地查询,本地查询允许你写自己的SQL查询.返回类型被你用的查询对象定义,并且数据会被映射到正确的对象.如认为,流程实例…因此,你不得不用在数据库中定义的表和字段的名字作为被映射到数据库的查询。这需要一些关于数据结构的知识,并且它推荐给我们要小心的进行本地查询。表的名字能通过API被重新寻回,以致于尽可能的远离对它的依赖。
List<Task> tasks = taskService.createNativeTaskQuery()
.sql("SELECT count(*) FROM " + managementService.getTableName(Task.class) + " T WHERE T.NAME_ = #{taskName}")
.parameter("taskName", "gonzoTask")
.list();
long count = taskService.createNativeTaskQuery()
.sql("SELECT count(*) FROM " + managementService.getTableName(Task.class) + " T1, "
+ managementService.getTableName(VariableInstanceEntity.class) + " V1 WHERE V1.TASK_ID_ = T1.ID_")
.count();
结论
-
如果是单例流程(没有分支和聚合),那么流程实例ID 和执行对象ID是相同的。
-
一个流程实例只有一个,但是执行对象可以存在多个。 (存在于不是单例流程,而包含分支和聚合的流程)
-
流程实例,执行对象,任务:
act_ru_execution 正在执行的执行对象表
act_hi_procinst 流程实例的历史表
act_tu_task 正在执行的任务表(只有节点是UserTask的时候,该表粗存在数据)
act_hi_taskinst 任务历史表(只有节点是UserTask的时候,该表存在数据)
act_hiactinst 所有活动节点的历史表