Activiti 基础知识
1、基本概念 工作流:业务过程的部分或整体在计算机应用环境下的自动化 工作流管理系统:工作流的定义和管理,按照在系统中预定义好的工作流规则进行工作流实例的执行。 工作流管理系统的目标:管理工作流程以确保工作在正确的时间被期望的人员执行–在自动化进行的业务过程中插入人工的执行和干预。 Activiy是什么 Activiti是一个针对企业用户、开发人员、系统管理员的轻量级工作流业务管理平台,其核心是使用Java开发的快速、稳定的BPMN2.0流程引擎。 Activiti的特点 数据持久化,底层使用MyBatis 引擎Service接口 流程设计器 原生支持Spring 分离运行时与历史数据
- 什么是 Activiti7
- Activiti 框架原理
- Activiti 核心 API
- Activiti 用户手册
- Activiti 数据库表结构
- Activiti 依赖事务监听器(上)
- Activiti 依赖事务监听器(下)
- Activiti6 特性
- Activiti 6.0 工作流入门
- Activiti 6.0 源码分析 helloword
- Activiti 框架原理
什么是 Activiti7
Actviti7 简介
简介
Activiti项目是一项新的基于Apache许可的开源BPM平台,从基础开始构建,旨在提供支持新的BPMN 2.0标准,包括支持对象管理组(OMG),面对新技术的机遇,诸如互操作性和云架构,提供技术实现。
创始人Tom Baeyens是JBoss jBPM的项目架构师,以及另一位架构师Joram Barrez,一起加入到创建Alfresco这项首次实现Apache开源许可的BPMN 2.0引擎开发中来。
Activiti是一个独立运作和经营的开源项目品牌,并将独立于Alfresco开源ECM系统运行。 Activiti将是一种轻量级,可嵌入的BPM引擎,而且还设计适用于可扩展的云架构。 Activiti将提供宽松的Apache许可2.0,以便这个项目可以广泛被使用,同时促进Activiti BPM引擎和BPMN 2.0的匹配,该项目现正由OMG通过标准审定。 加入Alfresco Activiti项目的是VMware的SpringSource分支,Alfresco的计划把该项目提交给Apache基础架构,希望吸引更多方面的BPM专家和促进BPM的创新。
特色
架构师Tom Baeyens说:“ Activiti有非常大的影响力来改变目前BPM的生态。Activiti的Apache授权,完整的功能,将使Activiti到达一个新的水平。Activiti将推动业界的创新,因为BPM技术可以广泛而自由地被应用。通过实现这些想法以及开源社区的努力,也让Activiti成为事实上的 BPM和BPMN标准执行“。
SpringSource的首席技术官Adrian Coyler说道:”这是一个对Spring开发人员和Java社区总体的发展非常令人兴奋的事情,长期以来一直需要一个Apache许可的流程引擎,这对许多应用系统非常实用的需求。我们认为,Activiti作为新的应用领域扩展到的Java和开源的发展,特别是在云架构上”。
Alfresco软件公司的首席技术官John Newton表示"我们发起这个项目,使内容和过程技术的使用可以更广泛和普及,我们这样做是因为,像其他开源项目,我们需要一个更宽松授权的流程引擎。我们相信,这可以改变业务流程处理领域,就像Alfresco公司已经为企业内容管理ECM领域所做的那样。”
Activiti将成为Alfresco的默认的业务流程引擎,Alfresco公司将继续支持jBPM,以及目前与其他业务流程的企业内容管理软件集成的引擎。 Alfresco公司也将与Alfresco企业版一起,提供对Activiti的支持,维护和技术保证。
1.工作流:工作的一个流程,事物发展的一个业务过程
流程: 请假流程:员工申请----部门经理-----总经理-----人事存档 传统方式下? 请假条的传递来实现 无纸化办公? 线上申请----线上审批----一条请假记录
在计算机的帮助下,能够实现流程的自动化控制,就称为工作流.
2.工作流引擎
为了实现自动化控制,Acitviti引擎就产生了。
作用:实现流程自动化控制
3.工作流系统:具有工作流的系统。
如果一个系统具备流程的自动化管理功能,这个系统就可以称为工作流系统
工作流系统,有哪些手段可以实现?
工作流系统,如何来实现流程的自动化管理? 流程自动化管理:程序员编码来实现 请假:员工申请----部门经理-----总经理-----人事存档
1,工号,部门号,姓名,日期,天数,原因,状态3
员工:0未提交1提交
部门经理:部门号=部门经理的部门编号相同,状态=1
2同意 3不同意
总经理 状态=2
4同意 5不同意
人事存档 状态=4
6同意 7不同意
问题:业务流程变更后,程序不能使用
以不变应万变
如何解决,以不变应万变?
-----Activiti就可以实现业务流程变化后,程序代码不需要改动。
使用场景,及相关业务.
SaaS-人力资源管理系统 行政审批(调薪)
为什么Activiti就可以解决业务需求变更时,源代码不需要更新,更新的是业务流程图?
原理?
图片附件
Activiti 框架原理
本文基于一个简单的Demo流程介绍了Activiti框架启动、部署、运行过程。
Demo准备
流程图文件:
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
<process id="hello" name="hello" isExecutable="true">
<!-- 流程开始节点 -->
<startEvent id="start" name="Start" ></startEvent>
<!-- serviceTask:执行me.likeyao.activiti.demo.HelloWorld的execute方法,打印hello world -->
<serviceTask id="helloworld" name="helloworld" activiti:class="me.likeyao.activiti.demo.HelloWorld"/>
<!-- 流程结束节点 -->
<endEvent id="end" name="End"></endEvent>
<!-- 流程迁移线:开始节点到serviceTask节点 -->
<sequenceFlow id="sid-1" sourceRef="start" targetRef="helloworld"></sequenceFlow>
<!-- 流程迁移线:serviceTask节点到结束节点 -->
<sequenceFlow id="sid-3" sourceRef="helloworld" targetRef="end"></sequenceFlow>
</process>
</definitions>
代码:
public class App {
public static void main(String[] args) {
//创建流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//部署流程图
processEngine.getRepositoryService().createDeployment().addClasspathResource("hello.bpmn20.xml").deploy();
//发起流程
processEngine.getRuntimeService().startProcessInstanceByKey("hello");
}
}
public class HelloWorld implements JavaDelegate{
public void execute(DelegateExecution execution) throws Exception {
System.out.println("Hello world!");
}
}
Demo实现的功能是发起一个流程,执行到流程的serviceTask节点时,打印Hello world!,然后流程结束。
源码版本:5.22.0
框架初始化
ProcessEngine类图
ProcessEngine
ProcessEngine是Activiti框架的门面,ProcessEngine本身不提供任何功能,通过getXXXService方法可以获取到对应的Service对象执行操作。Demo中涉及到的两个Service:
- RepositoryService:流程定义和流程部署相关功能。
- RuntimeService:流程实例相关功能(发起流程、获取流程实例变量)。
ProcessEngineConfiguration
ProcessEngineConfiguration负责Activiti框架的属性配置、初始化工作,初始化入口是buildProcessEngine方法,所有Activiti框架运行时需要用到的组件基本都在这里初始化:
public ProcessEngine buildProcessEngine() {
init();
return new ProcessEngineImpl(this);
}
protected void init() {
initConfigurators();
configuratorsBeforeInit();
initProcessDiagramGenerator();
initHistoryLevel();
initExpressionManager();
initDataSource();
initVariableTypes();
initBeans();
initFormEngines();
initFormTypes();
initScriptingEngines();
initClock();
initBusinessCalendarManager();
initCommandContextFactory();
initTransactionContextFactory();
initCommandExecutors();
initServices();
initIdGenerator();
initDeployers();
initJobHandlers();
initJobExecutor();
initAsyncExecutor();
initTransactionFactory();
initSqlSessionFactory();
initSessionFactories();
initJpa();
initDelegateInterceptor();
initEventHandlers();
initFailedJobCommandFactory();
initEventDispatcher();
initProcessValidator();
initDatabaseEventLogging();
configuratorsAfterInit();
}
这里有一个扩展点:ProcessEngineConfigurator。
public interface ProcessEngineConfigurator {
//组件初始化前
void beforeInit(ProcessEngineConfigurationImpl processEngineConfiguration);
//组件初始化后
void configure(ProcessEngineConfigurationImpl processEngineConfiguration);
//优先级
int getPriority();
}
在init初始化方法中,initConfigurators方法通过ServiceLoader加载ProcessEngineConfigurator。随后在configuratorsBeforeInit和configuratorsAfterInit方法中分别调用ProcessEngineConfigurator的beforeInit和configure方法,使用户可以在ProcessEngineConfiguration初始化前后编程式的修改属性,替换Activiti默认组件。
流程部署
流程部署实现的功能是将xml格式的流程图,转化为Activiti框架运行时依赖的流程定义对象。
RepositoryService
Demo中通过以下代码部署了一个流程:
processEngine.getRepositoryService().createDeployment().addClasspathResource("hello.bpmn20.xml").deploy();
createDeployment方法中创建了DeploymentBuilder对象,DeploymentBuilder对象负责读取指定路径的流程图xml文件的内容(byte数组),并缓存在DeploymentEntity对象中:
public DeploymentBuilder addInputStream(String resourceName, InputStream inputStream) {
...
byte[] bytes = IoUtil.readInputStream(inputStream, resourceName);
ResourceEntity resource = new ResourceEntity();
resource.setName(resourceName);
resource.setBytes(bytes);
deployment.addResource(resource);
return this;
}
最终DeploymentBuilder的deploy方法会调用RepositoryService的deploy方法,完成流程部署:
public Deployment deploy() {
return repositoryService.deploy(this);
}
CommandExecutor
在RepositoryService的deploy方法中,使用了CommandExecutor对象:
public Deployment deploy(DeploymentBuilderImpl deploymentBuilder) {
return commandExecutor.execute(new DeployCmd<Deployment>(deploymentBuilder));
}
在Activiti中,大部分操作都以Command模式实现,例如部署流程图的DeployCmd。CommandExecutor封装了一系列的CommandInterceptor,在内部形成CommandInterceptor链,在命令执行前后做了拦截。Activiti框架提供了一些
CommandInterceptor实现:
名称 | 作用 |
---|---|
CommandContextInterceptor | 用于生成命令执行的上下文(CommandContext)。 |
LogInterceptor | 开启日志Debug级别后,打印日志。 |
JtaTransactionInterceptor | 开启Jta事务 |
引入activiti-spring包,通过SpringTransactionInterceptor引入Spring的事务支持。
CommandExecutor在ProcessEngineConfigurationImpl的initCommandExecutors方法中初始化:
protected void initCommandExecutors() {
initDefaultCommandConfig();
initSchemaCommandConfig();
initCommandInvoker();
initCommandInterceptors();
initCommandExecutor();
}
可以设置ProcessEngineConfigurationImpl的customPreCommandInterceptors和customPostCommandInterceptors属性,添加自定义的CommandInterceptor:
protected void initCommandInterceptors() {
if (commandInterceptors==null) {
commandInterceptors = new ArrayList<CommandInterceptor>();
if (customPreCommandInterceptors!=null) {
commandInterceptors.addAll(customPreCommandInterceptors);
}
commandInterceptors.addAll(getDefaultCommandInterceptors());
if (customPostCommandInterceptors!=null) {
commandInterceptors.addAll(customPostCommandInterceptors);
}
commandInterceptors.add(commandInvoker);
}
}
这里的pre和post是指Activiti框架getDefaultCommandInterceptors()的前后。
CommandInvoker是CommandInterceptor链的最后一个对象,负责调用Command:
public class CommandInvoker extends AbstractCommandInterceptor {
@Override
public <T> T execute(CommandConfig config, Command<T> command) {
return command.execute(Context.getCommandContext());
}
}
CommandContext
CommandContext是Activit框架Command执行的上下文,主要包含各种SessionFactory:
sessionFactories = processEngineConfiguration.getSessionFactories();
SessionFactory负责生成Session,Session是Activiti操作持久化对象的统一接口:
名称 | 作用 |
---|---|
ProcessDefinitionEntityManager | 流程定义相关读写操作。 |
ExecutionEntityManager | 流程实例相关读写操作。 |
DefaultHistoryManager | 历史记录相关读写操作 |
CommandContext的生命周期
CommandConext在CommandContextInterceptor中创建,在finally代码块中销毁:
public <T> T execute(CommandConfig config, Command<T> command) {
//首先尝试从线程上下文的栈中获取CommandContext
CommandContext context = Context.getCommandContext();
boolean contextReused = false;
//什么时候创建新的CommandContext?
//1、CommandConfig中指定了不复用CommandContext
//2、当前线程上下文中不存在CommandConext
//3、当前线程上下文中的CommandConext已经抛出异常
if (!config.isContextReusePossible() || context == null || context.getException() != null) {
context = commandContextFactory.createCommandContext(command); }
else {
contextReused = true;
}
try {
//将前面获取到的CommandContext入栈
Context.setCommandContext(context);
Context.setProcessEngineConfiguration(processEngineConfiguration);
//执行下一个interceptor,在CommandInvoker中可以通过Context.getCommandContext()获取线程上下文中的CommandContext
return next.execute(config, command);
} catch (Exception e) {
//记录异常信息
context.exception(e);
} finally {
try {
//如果CommandContext不可复用,用完直接关闭
if (!contextReused) {
context.close();
}
} finally {
//出栈操作
Context.removeCommandContext();
Context.removeProcessEngineConfiguration();
Context.removeBpmnOverrideContext();
}
}
return null;
}
Activiti的框架可以在一个Command的执行过程中,调用另外一个Command,所以会出现是否需要复用CommandContext的选项,默认值为true。
流程的解析
在DeployCmd中,首先调用DeploymentEntityManager持久化存储DeploymentEntity对象:
commandContext.getDeploymentEntityManager().insertDeployment(deployment);
然后调用DeploymentManager部署流程(流程解析):
commandContext.getProcessEngineConfiguration().getDeploymentManager().deploy(deployment, deploymentSettings);
DeploymentEntityManager
DeploymentEntityManager的deploy方法中循环调用Deployer对象的deploy方法,Activiti默认的Deployer是BpmnDeployer。
另外DeploymentEntityManager中还缓存了解析好的流程定义对象和Bpmn模型对象。
Activiti持久化的是流程图xml文件,每次系统重新启动都要执行一次“deploy”操作,生成ProcessDefinitionEntity对象。
BpmnDeployer
BpmnDeployer的deploy方法中包含几个操作(代码缩略版):
public void deploy(DeploymentEntity deployment, Map<String, Object> deploymentSettings) {
...
BpmnParse bpmnParse = bpmnParser.createParse().sourceInputStream(inputStream).setSourceSystemId(resourceName).deployment(deployment).name(resourceName);
bpmnParse.execute();
for (ProcessDefinitionEntity processDefinition: bpmnParse.getProcessDefinitions()) {
if (deployment.isNew()) {
ProcessDefinitionEntity latestProcessDefinition = ...
if (latestProcessDefinition != null) {
processDefinitionVersion = latestProcessDefinition.getVersion() + 1;
}else{
processDefinitionVersion = 1;
}
processDefinition.setId(idGenerator.getNextId());
dbSqlSession.insert(processDefinition);
}
...
}
}
- 通过BpmnParser对象创建BpmnParse。
- 调用BpmnParse的execute方法,将inputStream中的流程图转化为ProcessDefinitionEntity。
- 持久化ProcessDefinitionEntity对象。
BpmnParse
在BpmnParse的execute中完成了xml文件到ProcessDefinitionEntity对象的转化:
public BpmnParse execute() {
//xml->bpmnModel
bpmnModel = converter.convertToBpmnModel(streamSource, validateSchema, enableSafeBpmnXml, encoding);
//bpmnModel-> ProcessDefinitionEntity
transformProcessDefinitions();
}
protected void transformProcessDefinitions() {
for (Process process : bpmnModel.getProcesses()) {
bpmnParserHandlers.parseElement(this, process);
}
}
在流程定义解析过程中,会涉及到两套模型:
- Bpmn模型(由BpmnXMLConverter完成转换)
- PVM模型(由BpmnParseHandlers完成转换)
Bpmn模型
PVM模型
Bpmn模型更偏向于xml节点的描述,PVM模型是运行时模型。Bpmn模型中的ServiceTask、StartEvent等会统一映射转换为PVM的ActivityImpl对象,ServiceTask和StartEvent等节点行为上的差别,体现在ActivityImpl对象持有的不同的ActivityBehavior上。
运行流程
创建流程实例
在demo中通过RuntimeService发起流程实例:
processEngine.getRuntimeService().startProcessInstanceByKey("hello");
在startProcessInstanceByKey方法中执行StartProcessInstanceCmd命令:
public class StartProcessInstanceCmd<T> implements Command<ProcessInstance>, Serializable {
...
public ProcessInstance execute(CommandContext commandContext) {
//获取流程定义
ProcessDefinitionEntity processDefinition = ...
//创建流程实例
ExecutionEntity processInstance = processDefinition.createProcessInstance(businessKey);
//开始流程
processInstance.start();
return processInstance;
}
...
}
在StartProcessInstanceCmd方中通过流程定义ProcessDefinitionEntity创建了流程实例 ExecutionEntity:
ExecutionEntity实现了一些重要接口:
- PVM相关的接口,赋予了ExecutionEntity流程驱动的能力,例如single、start方法。
- 实现VariableScope接口让ExecutionEntity可以持久上下文变量。
- ProcessInstance接口暴露了ExecutionEntity关联的ProcessDefinitionEntity的信息。
- PersistentObject接口代表ExecutionEntity对象是需要持久化。
在ExecutionEntity中维护类一个属性:activity。activity属性代表当前执行到哪个节点,在创建ExecutionEntity过程中会设置activity,使流程从某一个节点开始,默认是开始节点。
最后StartProcessInstanceCmd还调用ExecutionEntity的start方法开始驱动流程:
public void start() {
performOperation(AtomicOperation.PROCESS_START);
}
驱动流程
Activiti框架的流程运行于PVM模型之上,在流程运行时主要涉及到PVM中几个对象:ActivityImpl、TransitionImpl和ActivityBehavior。
- ActivityImpl:ActivityImpl是流程节点的抽象,ActivityImpl维护流程图中节点的连线,包括有哪些进线,有哪些出线。另外还包含节点同步/异步执行等信息。
- TransitionImpl:TransitionImpl包含source和target两个属性,连接了两个流程节点。
- ActivityBehavior:每一个ActivityImpl对象都拥有一个ActivityBehavior对象,ActivityBehavior代表节点的行为。
ActivityImpl、TransitionImpl和ActivityBehavior只是描述了流程的节点、迁移线和节点行为,真正要让ExecutionEntity流转起来,还需要AtomicOperation的驱动:
AtomicOperation PROCESS_START = new AtomicOperationProcessStart();
AtomicOperation PROCESS_START_INITIAL = new AtomicOperationProcessStartInitial();
AtomicOperation PROCESS_END = new AtomicOperationProcessEnd();
AtomicOperation ACTIVITY_START = new AtomicOperationActivityStart();
AtomicOperation ACTIVITY_EXECUTE = new AtomicOperationActivityExecute();
AtomicOperation ACTIVITY_END = new AtomicOperationActivityEnd();
AtomicOperation TRANSITION_NOTIFY_LISTENER_END = new AtomicOperationTransitionNotifyListenerEnd();
AtomicOperation TRANSITION_DESTROY_SCOPE = new AtomicOperationTransitionDestroyScope();
AtomicOperation TRANSITION_NOTIFY_LISTENER_TAKE = new AtomicOperationTransitionNotifyListenerTake();
AtomicOperation TRANSITION_CREATE_SCOPE = new AtomicOperationTransitionCreateScope();
AtomicOperation TRANSITION_NOTIFY_LISTENER_START = new AtomicOperationTransitionNotifyListenerStart();
AtomicOperation DELETE_CASCADE = new AtomicOperationDeleteCascade();
AtomicOperation DELETE_CASCADE_FIRE_ACTIVITY_END = new AtomicOperationDeleteCascadeFireActivityEnd();
在ExecutionEntity的start方法中,调用了PROCESS_START,PROCESS_START做了几件事:
- 获取流程定义级别定义的监听start事件的ExecutionListener,调用notify方法。
- 如果开启了事件功能,发布ActivitiEntityWithVariablesEvent和ActivitiProcessStartedEvent。
- 调用PROCESS_START_INITIAL。
PROCESS_START_INITIAL也实现了类似的功能:
- 获取初始节点上定义的监听start事件的ExecutionListener,调用notify方法。
- 调用ACTIVITY_EXECUTE。
在Demo流程执行中涉及的AtomicOperation的链路主要包括:
- ACTIVITY_EXECUTE:调用当前activity的behavior。
- TRANSITION_NOTIFY_LISTENER_END:某个activity节点执行完毕,调用节点上声明的监听end事件的ExecutionListener。
- TRANSITION_NOTIFY_LISTENER_TAKE:触发线上的ExecutionListener。
- TRANSITION_NOTIFY_LISTENER_START:某个activity节点即将开始执行,调用节点上的监听start事件的ExecutionListener。
以Demo流程中的ServiceTask节点helloworld为例,在执行ACTIVITY_EXECUTE时,会获取activity关联的behavior:
public class AtomicOperationActivityExecute implements AtomicOperation {
public void execute(InterpretableExecution execution) {
...
ActivityImpl activity = (ActivityImpl) execution.getActivity();
ActivityBehavior activityBehavior = activity.getActivityBehavior();
activityBehavior.execute(execution);
...
}
}
ServiceTask解析时关联的是ServiceTaskJavaDelegateActivityBehavior,execution方法:
public void execute(ActivityExecution execution) throws Exception {
//execution中调用了me.likeyao.activiti.demo.HelloWorld
execute((DelegateExecution) execution);
//离开当前节点
leave(execution);
}
在leave方法中调用了:
bpmnActivityBehavior.performDefaultOutgoingBehavior(execution);
performDefaultOutgoingBehavior方法会在当前activity的 出线中选择一条,使流程流向下一个节点。在Demo中只有一条线存在:
protected void performOutgoingBehavior(ActivityExecution execution,
boolean checkConditions, boolean throwExceptionIfExecutionStuck, List<ActivityExecution> reusableExecutions) {
if (transitionsToTake.size() == 1) {
execution.take(transitionsToTake.get(0));
}
}
最终take方法会将流程驱动权交还到AtomicOperation中:
public class ExecutionEntity{
...
public void take(PvmTransition transition, boolean fireActivityCompletionEvent) {
...
setActivity((ActivityImpl)transition.getSource());
setTransition((TransitionImpl) transition);
performOperation(AtomicOperation.TRANSITION_NOTIFY_LISTENER_END);
...
}
...
}
AtomicOperation的问题
按照AtomicOperation的驱动模式,只有当遇到UserTask等需要等待single信号的节点,调用才会返回。这意味着当调用RuntimeService启动一个流程实例时,要一直等到流程运行到一个UserTask节点调用才会返回,如果流程比较长耗时非常验证。
另一个问题是当流程图比较复杂,ExecutionListener数量比较多时,AtomicOperation之间的互相调用会导致调用栈非常深。
AtomicOperation驱动模式与ExecutionEntity、Behavior等绑定的比较紧密,暂时没有特别好的办法替换掉。
小结
本文主要介绍了Activiti框架的启动、部署、运行的主链路,并没有深入BPMN规范和Activit功能的具体实现,后续打算根据Activiti的用户手册,详细分析每个功能的使用和实现。
Activiti 核心 API
Activiti 核心 API
7大接口
- RepositoryService:提供一系列管理流程部署和流程定义的API。
- RuntimeService:在流程运行时对流程实例进行管理与控制。
- TaskService:对流程任务进行管理,例如任务提醒、任务完成和创建任务等。
- IdentityService:提供对流程角色数据进行管理的API,这些角色数据包括用户组、用户及它们之间的关系。
- ManagementService:提供对流程引擎进行管理和维护的服务。
- HistoryService:对流程的历史数据进行操作,包括查询、删除这些历史数据。
- FormService:表单服务。
核心API
.1: ProcessEngine
说明:
-
在Activiti中最核心的类,其他的类都是由他而来。
-
产生方式:
在前面看到了两种创建ProcessEngine(流程引擎)的方式,而这里要简化很多,调用ProcessEngines的getDefaultProceeEngine方法时会自动加载classpath下名为activiti.cfg.xml文件。
-
可以产生RepositoryService
-
可以产生RuntimeService
-
可以产生TaskService
各个Service的作用:
RepositoryService | 管理流程定义 |
---|---|
RuntimeService | 执行管理,包括启动、推进、删除流程实例等操作 |
TaskService | 任务管理 |
HistoryService | 历史管理(执行完的数据的管理) |
IdentityService | 组织机构管理 |
FormService | 一个可选服务,任务表单管理 |
ManagerService | 使用Activiti的定制环境中基本上不会用到。 它可以查询数据库的表和表的元数据。另外,它提供了查询和管理异步操作的功能。 |
.2:RepositoryService
是Activiti的仓库服务类。所谓的仓库指流程定义文档的两个文件:bpmn文件和流程图片。
-
产生方式
-
可以产生DeploymentBuilder,用来定义流程部署的相关参数
-
删除流程定义
.3:RuntimeService
是activiti的流程执行服务类。可以从这个服务类中获取很多关于流程执行相关的信息。
.4:TaskService
是activiti的任务服务类。可以从这个类中获取任务的信息。
.5:HistoryService
是activiti的查询历史信息的类。在一个流程执行完成后,这个对象为我们提供查询历史信息。
.6:ProcessDefinition
流程定义类。可以从这里获得资源文件等。
.7:ProcessInstance
代表流程定义的执行实例。如范冰冰请了一天的假,她就必须发出一个流程实例的申请。一个流程实例包括了所有的运行节点。我们可以利用这个对象来了解当前流程实例的进度等信息。流程实例就表示一个流程从开始到结束的最大的流程分支,即一个流程中流程实例只有一个。
.8:Execution
Activiti用这个对象去描述流程执行的每一个节点。在没有并发的情况下,Execution就是同ProcessInstance。流程按照流程定义的规则执行一次的过程,就可以表示执行对象Execution。
从源代码中可以看出ProcessInstance就是Execution。但在现实意义上有所区别:
在单线流程中,如上图的贷款流程,ProcessInstance与Execution是一致的。
这个例子有一个特点:wire money(汇钱)和archive(存档)是并发执行的。 这个时候,总线路代表ProcessInstance,而分线路中每个活动代表Execution。
总结:
-
一个流程中,执行对象可以存在多个,但是流程实例只能有一个。
-
当流程按照规则只执行一次的时候,那么流程实例就是执行对象。
Activiti 用户手册
Activiti 数据库表结构
——文档适用于 Activiti 5-6
Activiti 数据库表结构设计说明
Activiti 工作流总共包含 23
张数据表(现在是25张,新增了 ACT_EVT_LOG
和 ACT_PROCDEF_INFO
)
Activiti 7 的数据库表结构设计
# Activiti 7 的数据库表结构设计
ACT_EVT_LOG, ACT_GE_BYTEARRAY, ACT_GE_PROPERTY, ACT_HI_ACTINST, ACT_HI_ATTACHMENT, ACT_HI_COMMENT, ACT_HI_DETAIL, ACT_HI_IDENTITYLINK, ACT_HI_PROCINST, ACT_HI_TASKINST, ACT_HI_VARINST, ACT_PROCDEF_INFO, ACT_RE_DEPLOYMENT, ACT_RE_MODEL, ACT_RE_PROCDEF, ACT_RU_DEADLETTER_JOB, ACT_RU_EVENT_SUBSCR, ACT_RU_EXECUTION, ACT_RU_IDENTITYLINK, ACT_RU_INTEGRATION, ACT_RU_JOB, ACT_RU_SUSPENDED_JOB, ACT_RU_TASK, ACT_RU_TIMER_JOB, ACT_RU_VARIABLE
Activiti 各大版本表对比
以下只是对表名做了简单的对比,并没有涉及到数据库表字段及数据类型
编号 | 表名 | Activiti 5.23.0 | Acticiti 6.0.0 | Activiti 7.1.0.M6 |
---|---|---|---|---|
1 | ACT_EVT_LOG | ☑ | ☑ | ☑ |
2 | ACT_GE_BYTEARRAY | ☑ | ☑ | ☑ |
3 | ACT_GE_PROPERTY | ☑ | ☑ | ☑ |
4 | ACT_HI_ACTINST | ☑ | ☑ | ☑ |
5 | ACT_HI_ATTACHMENT | ☑ | ☑ | ☑ |
6 | ACT_HI_COMMENT | ☑ | ☑ | ☑ |
7 | ACT_HI_DETAIL | ☑ | ☑ | ☑ |
8 | ACT_HI_IDENTITYLINK | ☑ | ☑ | ☑ |
9 | ACT_HI_PROCINST | ☑ | ☑ | ☑ |
10 | ACT_HI_TASKINST | ☑ | ☑ | ☑ |
11 | ACT_HI_VARINST | ☑ | ☑ | ☑ |
12 | ACT_ID_GROUP | ☑ | ☑ | |
13 | ACT_ID_INFO | ☑ | ☑ | |
14 | ACT_ID_MEMBERSHIP | ☑ | ☑ | |
15 | ACT_ID_USER | ☑ | ☑ | |
16 | ACT_PROCDEF_INFO | ☑ | ☑ | ☑ |
17 | ACT_RE_DEPLOYMENT | ☑ | ☑ | ☑ |
18 | ACT_RE_MODEL | ☑ | ☑ | ☑ |
19 | ACT_RE_PROCDEF | ☑ | ☑ | ☑ |
20 | ACT_RU_DEADLETTER_JOB | ☑ | ☑ | |
21 | ACT_RU_EVENT_SUBSCR | ☑ | ☑ | ☑ |
22 | ACT_RU_EXECUTION | ☑ | ☑ | ☑ |
23 | ACT_RU_IDENTITYLINK | ☑ | ☑ | ☑ |
24 | ACT_RU_INTEGRATION | ☑ | ||
25 | ACT_RU_JOB | ☑ | ☑ | ☑ |
26 | ACT_RU_SUSPENDED_JOB | ☑ | ☑ | |
27 | ACT_RU_TASK | ☑ | ☑ | ☑ |
28 | ACT_RU_TIMER_JOB | ☑ | ☑ | |
29 | ACT_RU_VARIABLE | ☑ | ☑ | ☑ |
表名规则
Activiti 使用到的表都是 ACT_ 开头的。表名的第二部分用两个字母表明表的用途。
- ACT_GE_ (
GE
) 表示 general 全局通用数据及设置,各种情况都使用的数据。 - ACT_HI_ (
HI
) 表示 history 历史数据表,包含着程执行的历史相关数据,如结束的流程实例,变量,任务,等等 - ACT_ID_ (
ID
) 表示 identity 组织机构,用户记录,流程中使用到的用户和组。这些表包含标识的信息,如用户,用户组,等等。 - ACT_RE_ (
RE
) 表示 repository 存储,包含的是静态信息,如,流程定义,流程的资源(图片,规则等)。 - ACT_RU_ (
RU
) 表示 runtime 运行时,运行时的流程变量,用户任务,变量,职责(job)等运行时的数据。Activiti 只存储实例执行期间的运行时数据,当流程实例结束时,将删除这些记录。这就保证了这些运行时的表小且快。
25 张表详情
一般数据 (ACT_GE_)
通用数据表
表名 | 解释 |
---|---|
ACT_GE_BYTEARRAY | 二进制数据表,存储通用的流程定义和流程资源。 |
ACT_GE_PROPERTY | 系统相关属性,属性数据表存储整个流程引擎级别的数据,初始化表结构时,会默认插入三条记录。 |
流程历史记录 (ACT_HI_)
表名 | 解释 |
---|---|
ACT_HI_ACTINST | 历史节点表 |
ACT_HI_ATTACHMENT | 历史附件表 |
ACT_HI_COMMENT | 历史意见表 |
ACT_HI_DETAIL | 历史详情表,提供历史变量的查询 |
ACT_HI_IDENTITYLINK | 历史流程人员表 |
ACT_HI_PROCINST | 历史流程实例表 |
ACT_HI_TASKINST | 历史任务实例表 |
ACT_HI_VARINST | 历史变量表 |
用户用户组表 (ACT_ID_)
身份数据表
表名 | 解释 |
---|---|
ACT_ID_GROUP | 用户组信息表 |
ACT_ID_INFO | 用户扩展信息表 |
ACT_ID_MEMBERSHIP | 用户与用户组对应信息表 |
ACT_ID_USER | 用户信息表 |
流程定义表 (ACT_RE_)
流程存储数据表
表名 | 解释 |
---|---|
ACT_RE_DEPLOYMENT | 部署信息表 |
ACT_RE_MODEL | 流程设计模型部署表 |
ACT_RE_PROCDEF | 流程定义数据表 |
运行实例表 (ACT_RU_)
表名 | 解释 |
---|---|
ACT_RU_EVENT_SUBSCR | 运行时事件 throwEvent、catchEvent 时间监听信息表 |
ACT_RU_EXECUTION | 运行时流程执行实例 |
ACT_RU_IDENTITYLINK | 运行时流程人员表,主要存储任务节点与参与者的相关信息 |
ACT_RU_JOB | 运行时定时任务数据表 |
ACT_RU_TASK | 运行时任务节点表 |
ACT_RU_VARIABLE | 运行时流程变量数据表 |
其它
表名 | 解释 |
---|---|
ACT_EVT_LOG | 事件日志 |
ACT_PROCDEF_INFO | 流程定义的动态变更信息 |
一般数据 (ACT_GE_)
ACT_GE_BYTEARRAY 二进制数据表
二进制数据表,存储通用的流程定义和流程资源。(act_ge_bytearray)
保存流程定义图片和xml、Serializable(序列化)的变量,即保存所有二进制数据,特别注意类路径部署时候,不要把svn等隐藏文件或者其他与流程无关的文件也一起部署到该表中,会造成一些错误(可能导致流程定义无法删除)
用于保存与流程引擎相关的资源,只要调用了 Activiti 存储服务的 API ,涉及的资源均会被转换为 byte 数组保存到这个表中。在资源表中设计了一个 BYTES宇段,用来保存资源的内容,因此理论上其可以用于保存任何类型的资源(文件或者其他来源的输入流)。一般情况下, Activiti 使用这个表来保存字符串、流程文件的内容、流程图片内容
字段名称 | 字段描述 | 数据类型 | 主键 | 为空 | 取值说明 |
---|---|---|---|---|---|
ID_ | ID_ | nvarchar(64) | Y | 主键ID | |
REV_ | 乐观锁 | int | Y | Version(版本) | |
NAME_ | 名称 | nvarchar(255) | Y | 部署的文件名称,如:leave.bpmn.png,leave.bpmn20.xml | |
DEPLOYMENT_ID_ | 部署ID | nvarchar(64) | Y | 部署表ID | |
BYTES_ | 字节 | varbinary(max) | Y | 部署文件 | |
GENERATED_ | 是否是引擎生成 | tinyint | Y | 0为用户生成,1为activiti生成 |
字段 | 类型 | NULL | 备注 |
---|---|---|---|
ID_ | varchar(64) | NO | |
REV_ | int(11) | YES | 数据版本, Activiti为一些有可能会被频繁修改的数据表,加入该字段,用来表示该数据被操作的次数 |
NAME_ | varchar(255) | YES | 资源名称 |
DEPLOYMENT_ID_ | varchar(64) | YES | 一次部署可以添加多个资源,该字段与部署表ACT_RE_DEPLOYMENT的主键相关联 |
BYTES_ | longblob | YES | 资源内容,最大可存储4G数据 |
GENERATED_ | tinyint(4) | YES | 是否由Activiti自动产生的资源,0 |
ACT_GE_PROPERTY 属性数据表
属性数据表(act_ge_property)
属性数据表。存储整个流程引擎级别的数据。
Activiti 将全部的属性抽象为 key-value 对,每个属性都有名称和值, 使用
ACT GE PROPERTY 来保存这些属性
字段名称 | 字段描述 | 数据类型 | 主键 | 为空 | 取值说明 |
---|---|---|---|---|---|
NAME_ | 名称 | nvarchar(64) | √ | schema.versionschema.historynext.dbid | |
VALUE_ | 值 | nvarchar(300) | √ | 5.create(5.) | |
REV_ | 乐观锁 | int | √ | version |
字段 | 类型 | NULL | 备注 |
---|---|---|---|
NAME_ | varchar(64) | NO | 名称 |
VALUE_ | varchar(300) | YES | 值 |
REV_ | int(11) | YES | 数据的版本号 |
流程历史记录 (ACT_HI_)
历史数据表就好像流程引擎的日志表,操作过的流程元素将会被记录到历史表中。
ACT_HI_ACTINST 历史节点表
历史节点表(act_hi_actinst)
历史活动信息。这里记录流程流转过的所有节点,与HI_TASKINST不同的是,taskinst只记录usertask内容
字段名称 | 字段描述 | 数据类型 | 主键 | 为空 | 取值说明 |
---|---|---|---|---|---|
ID_ | ID_ | nvarchar(64) | √ | ||
PROC_DEF_ID_ | 流程定义ID | nvarchar(64) | |||
PROC_INST_ID_ | 流程实例ID | nvarchar(64) | |||
EXECUTION_ID_ | 执行实例ID | nvarchar(64) | |||
ACT_ID_ | 节点ID | nvarchar(225) | 节点定义ID | ||
TASK_ID_ | 任务实例ID | nvarchar(64) | √ | 任务实例ID 其他节点类型实例ID在这里为空 | |
CALL_PROC_INST_ID_ | 调用外部的流程实例ID | nvarchar(64) | √ | 调用外部流程的流程实例ID’ | |
ACT_NAME_ | 节点名称 | nvarchar(225) | √ | 节点定义名称 | |
ACT_TYPE_ | 节点类型 | nvarchar(225) | 如startEvent、userTask | ||
ASSIGNEE_ | 签收人 | nvarchar(64) | √ | 节点签收人 | |
START_TIME_ | 开始时间 | datetime | 2013-09-15 11:30:00 | ||
END_TIME_ | 结束时间 | datetime | √ | 2013-09-15 11:30:00 | |
DURATION_ | 耗时 | numeric(19,0) | √ | 毫秒值 |
字段 | 类型 | NULL | 备注 |
---|---|---|---|
ID_ | varchar(64) | NO | |
PROC_DEF_ID_ | varchar(64) | NO | |
PROC_INST_ID_ | varchar(64) | NO | |
EXECUTION_ID_ | varchar(64) | NO | |
ACT_ID_ | varchar(255) | NO | |
TASK_ID_ | varchar(64) | YES | |
CALL_PROC_INST_ID_ | varchar(64) | YES | |
ACT_NAME_ | varchar(255) | YES | |
ACT_TYPE_ | varchar(255) | NO | |
ASSIGNEE_ | varchar(255) | YES | |
START_TIME_ | datetime | NO | |
END_TIME_ | datetime | YES | |
DURATION_ | bigint(20) | YES | |
DELETE_REASON_ | varchar(4000) | YES | |
TENANT_ID_ | varchar(255) | YES |
ACT_HI_ATTACHMENTA 历史附件表
历史附件表( act_hi_attachment )
字段名称 | 字段描述 | 数据类型 | 主键 | 为空 | 取值说明 |
---|---|---|---|---|---|
ID_ | ID_ | nvarchar(64) | √ | 主键ID | |
REV_ | 乐观锁 | integer | √ | Version | |
USER_ID_ | 用户ID | nvarchar(255) | √ | 用户ID | |
NAME_ | 名称 | nvarchar(255) | √ | 附件名称 | |
DESCRIPTION_ | 描述 | nvarchar(4000) | √ | 描述 | |
TYPE_ | 类型 | nvarchar(255) | √ | 附件类型 | |
TASK_ID_ | 任务实例ID | nvarchar(64) | √ | 节点实例ID | |
PROC_INST_ID_ | 流程实例ID | nvarchar(64) | √ | 流程实例ID | |
URL_ | URL_ | nvarchar(4000) | √ | 附件地址 | |
CONTENT_ID_ | 字节表的ID | nvarchar(64) | √ | ACT_GE_BYTEARRAY的ID |
字段 | 类型 | NULL | 备注 |
---|---|---|---|
ID_ | varchar(64) | NO | |
REV_ | int(11) | YES | |
USER_ID_ | varchar(255) | YES | 附件对应的用户ID |
NAME_ | varchar(255) | YES | 附件名称 |
DESCRIPTION_ | varchar(4000) | YES | 附件描述 |
TYPE_ | varchar(255) | YES | 附件类型 |
TASK_ID_ | varchar(64) | YES | 附件对应的任务ID |
PROC_INST_ID_ | varchar(64) | YES | 对应的流程实例ID |
URL_ | varchar(4000) | YES | 链接到该附件的URL |
CONTENT_ID_ | varchar(64) | YES | 附件内容ID,附件内容会保存到资源表中,该字段记录资源数据ID |
TIME_ | datetime | YES | 数据产生的时间 |
ACT_HI_COMMENT 历史意见表
历史意见表( act_hi_comment )
表实际不只保存评论数据,它还会保存某些事件数据
字段名称 | 字段描述 | 数据类型 | 主键 | 为空 | 取值说明 |
---|---|---|---|---|---|
ID_ | ID_ | nvarchar(64) | √ | 主键ID | |
TYPE_ | 类型 | nvarchar(255) | √ | 类型:event(事件)comment(意见) | |
TIME_ | 时间 | datetime | 填写时间’ | ||
USER_ID_ | 用户ID | nvarchar(64) | √ | 填写人 | |
TASK_ID_ | 节点任务ID | nvarchar(64) | √ | 节点实例ID | |
PROC_INST_ID_ | 流程实例ID | nvarchar(255) | √ | 流程实例ID | |
ACTION_ | 行为类型 | nvarchar(64) | √ | 见备注1 | |
MESSAGE_ | 基本内容 | nvarchar(4000) | √ | 用于存放流程产生的信息,比如审批意见 | |
FULL_MSG_ | 全部内容 | varbinary(max) | √ | 附件地址 |
字段 | 类型 | NULL | 备注 |
---|---|---|---|
ID_ | varchar(64) | NO | |
TYPE_ | varchar(255) | YES | 评论的类型可以设值为event或comment表示事件记录数据或者评论数据 |
TIME_ | datetime | NO | 数据产生的时间 |
USER_ID_ | varchar(255) | YES | 产生评论数据的用户ID |
TASK_ID_ | varchar(64) | YES | 评论数据的任务ID |
PROC_INST_ID_ | varchar(64) | YES | 评论数据对应的流程实例ID |
ACTION_ | varchar(255) | YES | 该评论数据的操作标识 |
MESSAGE_ | varchar(4000) | YES | 该评论数据的信息 |
FULL_MSG_ | longblob | YES | 该字段同样记录评论数据的信息 |
ACT_HI_DETAIL 流程明细表
历史详情表( act_hi_detail )
流程中产生的变量详细,包括控制流程流转的变量,业务表单中填写的流程需要用到的变量等。
流程明细表(ACT HI DETAlL 会记录流程执行过程中的参数或者表单数据,由于在流程执行过程中,会产生大量这类数据,因此默认情况下, Activiti 不会保存流程明细数据,除非将流程引擎的历史数据( hist。可〉配置为 full
字段名称 | 字段描述 | 数据类型 | 主键 | 为空 | 取值说明 |
---|---|---|---|---|---|
ID_ | ID_ | nvarchar(64) | √ | 主键 | |
TYPE_ | 类型 | nvarchar(255) | 见备注2 | ||
PROC_INST_ID_ | 流程实例ID | nvarchar(64) | √ | 流程实例ID | |
EXECUTION_ID_ | 执行实例ID | nvarchar(64) | √ | 执行实例ID | |
TASK_ID_ | 任务实例ID | nvarchar(64) | √ | 任务实例ID | |
ACT_INST_ID_ | 节点实例ID | nvarchar(64) | √ | ACT_HI_ACTINST表的ID | |
NAME_ | 名称 | nvarchar(255) | 名称 | ||
VAR_TYPE_ | 参数类型 | nvarchar(255) | √ | 见备注3 | |
REV_ | 乐观锁 | int | √ | Version | |
TIME_ | 时间戳 | datetime | 创建时间 | ||
BYTEARRAY_ID_ | 字节表ID | nvarchar | √ | ACT_GE_BYTEARRAY表的ID | |
DOUBLE_ | DOUBLE_ | double precision | √ | 存储变量类型为Double | |
LONG_ | LONG_ | numeric | √ | 存储变量类型为long | |
TEXT_ | TEXT_ | nvarchar | √ | 存储变量值类型为String | |
TEXT2_ | TEXT2_ | nvarchar | √ | 此处存储的是JPA持久化对象时,才会有值。此值为对象ID |
字段 | 类型 | NULL | 备注 |
---|---|---|---|
ID_ | varchar(64) | NO | |
TYPE_ | varchar(255) | NO | |
PROC_INST_ID_ | varchar(64) | YES | |
EXECUTION_ID_ | varchar(64) | YES | |
TASK_ID_ | varchar(64) | YES | |
ACT_INST_ID_ | varchar(64) | YES | |
NAME_ | varchar(255) | NO | |
VAR_TYPE_ | varchar(255) | YES | |
REV_ | int(11) | YES | |
TIME_ | datetime | NO | |
BYTEARRAY_ID_ | varchar(64) | YES | |
DOUBLE_ | double | YES | |
LONG_ | bigint(20) | YES | |
TEXT_ | varchar(4000) | YES | |
TEXT2_ | varchar(4000) | YES |
ACT_HI_IDENTITYLINK 历史流程人员表
历史流程人员表( act_ru_identitylink )
任务参与者数据表。主要存储历史节点参与者的信息
字段名称 | 字段描述 | 数据类型 | 主键 | 为空 | 取值说明 |
---|---|---|---|---|---|
ID_ | ID_ | nvarchar(64) | √ | ID_ | |
GROUP_ID_ | 组ID | nvarchar(255) | √ | 组ID | |
TYPE_ | 类型 | nvarchar(255) | √ | 备注4 | |
USER_ID_ | 用户ID | nvarchar(255) | √ | 用户ID | |
TASK_ID_ | 节点实例ID | nvarchar(64) | √ | 节点实例ID | |
PROC_INST_ID_ | 流程实例ID | nvarchar(64) | √ | 流程实例ID |
ACT_HI_PROCINST 流程实例表
历史流程实例表(act_hi_procinst)
只要流程被启动,就会将流程实例的数据写入 ACT HI PROC ST 表中 。除了基本的流程字段外,与运行时数据表不同的是,历史流程实例表还会记录流程的开始活动ID,结束活动ID等信息
字段名称 | 字段描述 | 数据类型 | 主键 | 为空 | 取值说明 |
---|---|---|---|---|---|
ID_ | ID_ | nvarchar(64) | √ | 主键ID | |
PROC_INST_ID_ | 流程实例ID | nvarchar(64) | 流程实例ID | ||
BUSINESS_KEY_ | 业务主键 | nvarchar(255) | √ | 业务主键,业务表单的ID | |
PROC_DEF_ID_ | 流程定义ID | nvarchar(64) | 流程定义ID | ||
START_TIME_ | 开始时间 | datetime | 开始时间 | ||
END_TIME_ | 结束时间 | datetime | √ | 结束时间 | |
DURATION_ | 耗时 | Numeric(19) | √ | 耗时 | |
START_USER_ID_ | 起草人 | nvarchar(255) | √ | 起草人 | |
START_ACT_ID_ | 开始节点ID | nvarchar(255) | √ | 起草环节ID | |
END_ACT_ID_ | 结束节点ID | nvarchar(255) | √ | 结束环节ID | |
SUPER_PROCESS_INSTANCE_ID_ | 父流程实例ID | nvarchar(64) | √ | 父流程实例ID | |
DELETE_REASON_ | 删除原因 | nvarchar(4000) | √ | 删除原因 |
字段 | 类型 | NULL | 备注 |
---|---|---|---|
ID_ | varchar(64) | NO | |
PROC_INST_ID_ | varchar(64) | NO | |
BUSINESS_KEY_ | varchar(255) | YES | |
PROC_DEF_ID_ | varchar(64) | NO | |
START_TIME_ | datetime | NO | |
END_TIME_ | datetime | YES | |
DURATION_ | bigint(20) | YES | |
START_USER_ID_ | varchar(255) | YES | |
START_ACT_ID_ | varchar(255) | YES | 开始活动的 ID 一般是流程开始事件的ID,在流程文件中定义 |
END_ACT_ID_ | varchar(255) | YES | 流程最后一个活动的 ID一般是流程结束事件的ID,在流程文件中定义。 |
SUPER_PROCESS_INSTANCE_ID_ | varchar(64) | YES | |
DELETE_REASON_ | varchar(4000) | YES | 该流程实例被删除的原因 |
TENANT_ID_ | varchar(255) | YES | |
NAME_ | varchar(255) | YES |
ACT_HI_TASKINST 历史任务表
历史任务实例表( act_hi_taskinst )
字段名称 | 字段描述 | 数据类型 | 主键 | 为空 | 取值说明 |
---|---|---|---|---|---|
ID_ | ID_ | nvarchar(64) | √ | 主键ID | |
PROC_DEF_ID_ | 流程定义ID | nvarchar(64) | √ | 流程定义ID | |
TASK_DEF_KEY_ | 节点定义ID | nvarchar(255) | √ | 节点定义ID | |
PROC_INST_ID_ | 流程实例ID | nvarchar(64) | √ | 流程实例ID | |
EXECUTION_ID_ | 执行实例ID | nvarchar(64) | √ | 执行实例ID | |
NAME_ | 名称 | varchar(255) | √ | 名称 | |
PARENT_TASK_ID_ | 父节点实例ID | nvarchar(64) | √ | 父节点实例ID | |
DESCRIPTION_ | 描述 | nvarchar(400) | √ | 描述 | |
OWNER_ | 实际签收人 任务的拥有者 | nvarchar(255) | √ | 签收人(默认为空,只有在委托时才有值) | |
ASSIGNEE_ | 签收人或被委托 | nvarchar(255) | √ | 签收人或被委托 | |
START_TIME_ | 开始时间 | datetime | 开始时间 | ||
CLAIM_TIME_ | 提醒时间 | datetime | √ | 提醒时间 | |
END_TIME_ | 结束时间 | datetime | √ | 结束时间 | |
DURATION_ | 耗时 | numeric(19) | √ | 耗时 | |
DELETE_REASON_ | 删除原因 | nvarchar(4000) | √ | 删除原因(completed,deleted) | |
PRIORITY_ | 优先级别 | int | √ | 优先级别 | |
DUE_DATE_ | 过期时间 | datetime | √ | 过期时间,表明任务应在多长时间内完成 | |
FORM_KEY_ | 节点定义的formkey | nvarchar(255) | √ | desinger节点定义的form_key属性 |
字段 | 类型 | NULL | 备注 |
---|---|---|---|
ID_ | varchar(64) | NO | |
PROC_DEF_ID_ | varchar(64) | YES | |
TASK_DEF_KEY_ | varchar(255) | YES | |
PROC_INST_ID_ | varchar(64) | YES | |
EXECUTION_ID_ | varchar(64) | YES | |
NAME_ | varchar(255) | YES | |
PARENT_TASK_ID_ | varchar(64) | YES | |
DESCRIPTION_ | varchar(4000) | YES | |
OWNER_ | varchar(255) | YES | |
ASSIGNEE_ | varchar(255) | YES | |
START_TIME_ | datetime | NO | |
CLAIM_TIME_ | datetime | YES | |
END_TIME_ | datetime | YES | |
DURATION_ | bigint(20) | YES | |
DELETE_REASON_ | varchar(4000) | YES | |
PRIORITY_ | int(11) | YES | |
DUE_DATE_ | datetime | YES | |
FORM_KEY_ | varchar(255) | YES | |
CATEGORY_ | varchar(255) | YES | |
TENANT_ID_ | varchar(255) | YES |
ACT_HI_VARINST 历史变量表
历史变量表( act_hi_varinst )
字段名称 | 字段描述 | 数据类型 | 主键 | 为空 | 取值说明 |
---|---|---|---|---|---|
ID_ | ID_ | nvarchar(64) | √ | ID_ | |
PROC_INST_ID_ | 流程实例ID | nvarchar(64) | √ | 流程实例ID | |
EXECUTION_ID_ | 执行实例ID | nvarchar(255) | √ | 执行实例ID | |
TASK_ID_ | 任务实例ID | nvarchar(64) | √ | 任务实例ID | |
NAME_ | 名称 | nvarchar(64) | 参数名称(英文) | ||
VAR_TYPE_ | 参数类型 | varchar(255) | √ | 备注5 | |
REV_ | 乐观锁 | nvarchar(64) | √ | 乐观锁 Version | |
BYTEARRAY_ID_ | 字节表ID | nvarchar(400) | √ | ACT_GE_BYTEARRAY表的主键 | |
DOUBLE_ | DOUBLE_ | nvarchar(255) | √ | 存储DoubleType类型的数据 | |
LONG_ | LONG_ | nvarchar(255) | √ | 存储LongType类型的数据 | |
TEXT_ | TEXT_ | datetime | √ | 备注6 | |
TEXT2_ | TEXT2_ | datetime | √ | 此处存储的是JPA持久化对象时,才会有值。此值为对象ID |
用户用户组表 (ACT_ID_)
Activiti 整个身份数据模块,可以独立于流程引擎而存在 有关身份数据的几张表没有保存与流程相关的数据及关联。身份表名称以 ACT_ID 开头,表名中的 是单词 identity的缩写。
ACT_ID_GROUP 用户组信息表
用户组信息表( act_id_group )
字段名称 | 字段描述 | 数据类型 | 主键 | 为空 | 取值说明 |
---|---|---|---|---|---|
ID_ | ID_ | nvarchar(64) | √ | 主键ID | |
REV_ | 乐观锁 | int | √ | 乐观锁Version | |
NAME_ | 名称 | nvarchar(255) | √ | 组名称 | |
TYPE_ | 类型 | nvarchar(255) | √ | 类型 |
字段 | 类型 | NULL | 备注 |
---|---|---|---|
ID_ | varchar(64) | NO | |
REV_ | int(11) | YES | |
NAME_ | varchar(255) | YES | 用户组名称 |
TYPE_ | varchar(255) | YES | 用户组类型,类型不由activiti提供,但是在某些业务中, Activiti 会根据该字段的值进行查询,字段值由 Activiti 定如 Activiti webService |
ACT_ID_INFO 用户扩展信息表
用户扩展信息表( act_id_info )
Activiti 将用户、用户账号和用户信息分为三种数据,其中用户表保存用户的数据,而用
户账号和用户信息,则被保存到 ACT_ID_INFO 表中
字段名称 | 字段描述 | 数据类型 | 主键 | 为空 | 取值说明 |
---|---|---|---|---|---|
ID_ | ID_ | nvarchar(64) | √ | 主键ID | |
REV_ | 乐观锁 | int | √ | 乐观锁Version | |
USER_ID_ | 用户ID | nvarchar(64) | √ | ||
TYPE_ | 类型 | nvarchar(64) | √ | ||
KEY_ | nvarchar(255) | √ | |||
VALUE_ | nvarchar(255) | √ | |||
PASSWORD_ | Image | √ | |||
PARENT_ID_ | nvarchar(255) | √ |
字段 | 类型 | NULL | 备注 |
---|---|---|---|
ID_ | varchar(64) | NO | |
REV_ | int(11) | YES | |
USER_ID_ | varchar(64) | YES | 对应用户表的数据ID(没有强制做外键关联) |
TYPE_ | varchar(64) | YES | 信息类型,当前可以设置用户账号(account)、用户信息(userinfo)和NULL三种值 |
KEY_ | varchar(255) | YES | 数据的键。可以根据改键来查找用户信息的值 |
VALUE_ | varchar(255) | YES | 数据的值 |
PASSWORD_ | longblob | YES | 用户账号的密码字段 |
PARENT_ID_ | varchar(255) | YES | 该信息的父信息ID,如果一条数据设置了父信息ID,则表示该数据是用户账号(信息)的明细数据,例如一个账号有激活日期,那么激活日期就是该账号的明细数据,此处使用了自关联来实现。 |
ACT_ID_MEMBERSHIP 用户与分组对应信息表
用户与分组对应信息表( act_id_membership )
用来保存用户的分组信息。
一个用户组下有多个用户, 一个用户可以属于不同的用户组,那么这种多对多的关系,就
使用关系表来进行描述,关系表为 ACT ID ME IBERSHIP ,只有两个字段。需要注意的是, ACT_ID_MEMBERSHI的两个字段均做了外键约束 写入该表的数据,必须要有用户和用户组数据与之关联。
字段名称 | 字段描述 | 数据类型 | 主键 | 为空 | 取值说明 |
---|---|---|---|---|---|
USER_ID | 用户ID | nvarchar(64) | √ | ||
GROUP_ID | 用户组ID | nvarchar(64) | √ |
字段 | 类型 | NULL | 备注 |
---|---|---|---|
USER_ID_ | varchar(64) | NO | |
GROUP_ID_ | varchar(64) | NO |
ACT_ID_USER 用户信息表
用户信息表( act_id_user )
字段名称 | 字段描述 | 数据类型 | 主键 | 为空 | 取值说明 |
---|---|---|---|---|---|
ID_ | ID_ | nvarchar(64) | √ | 主键ID | |
REV_ | 乐观锁 | int | √ | 乐观锁Version | |
FIRST_ | 姓 | nvarchar(255) | √ | ||
LAST_ | 名 | nvarchar(255) | √ | ||
EMAIL_ | EMAIL_ | nvarchar(255) | √ | ||
PWD_ | 密码 | nvarchar(255) | √ | ||
PICTURE_ID_ | 图片ID | nvarchar(64) | √ |
字段 | 类型 | NULL | 备注 |
---|---|---|---|
ID_ | varchar(64) | NO | |
REV_ | int(11) | YES | |
FIRST_ | varchar(255) | YES | 人名 |
LAST_ | varchar(255) | YES | 姓式 |
EMAIL_ | varchar(255) | YES | 用户邮箱 |
PWD_ | varchar(255) | YES | 用户密码 |
PICTURE_ID_ | varchar(64) | YES | 用户图片,对应资源中的数据ID |
流程定义表 (ACT_RE_)
ACT_RE_DEPLOYMENT 流程部署表
部署信息表( act_re_deployment )
部署流程定义时需要被持久化保存下来的信息。
在 Activiti 中,一次部署可以添加多个资源,资源会被保存到资源表中(act_ge_bytearray),而对于部署,则部署信息会被保存到部署表中
字段名称 | 字段描述 | 数据类型 | 主键 | 为空 | 取值说明 |
---|---|---|---|---|---|
ID_ | ID_ | nvarchar(64) | √ | 主键ID | |
NAME_ | 部署名称 | nvarchar(255) | √ | 部署文件名 | |
CATEGORY_ | 分类 | nvarchar(255) | √ | 类别 | |
DEPLOY_TIME_ | 部署时间 | datetime | √ | 部署时间 |
字段 | 类型 | NULL | 备注 |
---|---|---|---|
ID_ | varchar(64) | NO | |
NAME_ | varchar(255) | YES | 部署的名称 |
CATEGORY_ | varchar(255) | YES | |
KEY_ | varchar(255) | YES | |
TENANT_ID_ | varchar(255) | YES | |
DEPLOY_TIME_ | timestamp | YES | 部署的时间 |
ENGINE_VERSION_ | varchar(255) | YES |
ACT_RE_MODEL 流程设计模型部署表
流程设计模型部署表( act_re_model )
流程设计器设计流程后,保存数据到该表。
字段名称 | 字段描述 | 数据类型 | 主键 | 为空 | 取值说明 |
---|---|---|---|---|---|
ID_ | ID_ | nvarchar(64) | √ | ID_ | |
REV_ | 乐观锁 | int | √ | 乐观锁 | |
NAME_ | 名称 | nvarchar(255) | √ | 名称 | |
KEY_ | KEY_ | nvarchar(255) | √ | 分类 | |
CATEGORY_ | 分类 | nvarchar(255) | √ | 分类 | |
CREATE_TIME_ | 创建时间 | datetime | √ | 创建时间 | |
LAST_UPDATE_TIME_ | 最新修改时间 | datetime | √ | 最新修改时间 | |
VERSION_ | 版本 | int | √ | 版本 | |
META_INFO_ | META_INFO_ | nvarchar(255) | √ | 以json格式保存流程定义的信息 | |
DEPLOYMENT_ID_ | 部署ID | nvarchar(255) | √ | 部署ID | |
EDITOR_SOURCE_VALUE_ID_ | datetime | √ | |||
EDITOR_SOURCE_EXTRA_VALUE_ID_ | datetime | √ |
ACT_RE_PROCDEF 流程定义表
流程定义数据表( act_re_procdef )
业务流程定义数据表。此表和 ACT_RE_DEPLOYMENT 是多对一的关系,即,一个部署的bar包里可能包含多个流程定义文件,每个流程定义文件都会有一条记录在 ACT_REPROCDEF 表内,每个流程定义的数据,都会对于 ACT_GE_BYTEARRAY 表内的一个资源文件和 PNG 图片文件。和 ACT_GE_BYTEARRAY 的关联是通过程序用ACT_GE_BYTEARRAY.NAME 与 ACT_RE_PROCDEF.NAME 完成的,在数据库表结构中没有体现。
Activiti 在部署添加资源时,如果发布部署的文件是流程文件( .bpnn 或者.BPMN 20.xml),
则除了会解析这些流程文件,将内容保存到资源表外,还会解析流程文件的内容,形成特定的流程定义数据,写入流程定义表( ACT_RE_PROCDEF )中
字段名称 | 字段描述 | 数据类型 | 主键 | 为空 | 取值说明 |
---|---|---|---|---|---|
ID_ | ID_ | nvarchar(64) | √ | ID_ | |
REV_ | 乐观锁 | int | √ | 乐观锁 | |
CATEGORY_ | 分类 | nvarchar(255) | √ | 流程定义的Namespace就是类别 | |
NAME_ | 名称 | nvarchar(255) | √ | 名称 | |
KEY_ | 定义的KEY | nvarchar(255) | 流程定义ID | ||
VERSION_ | 版本 | int | 版本 | ||
DEPLOYMENT_ID_ | 部署表ID | nvarchar(64) | √ | 部署表ID | |
RESOURCE_NAME_ | bpmn文件名称 | nvarchar(4000) | √ | 流程bpmn文件名称 | |
DGRM_RESOURCE_NAME_ | png图片名称 | nvarchar(4000) | √ | 流程图片名称 | |
DESCRIPTION_ | 描述 | nvarchar(4000) | √ | 描述 | |
HAS_START_FORM_KEY_ | 是否存在开始节点formKey | tinyint | √ | start节点是否存在formKey 0否 1是 | |
SUSPENSION_STATE_ | 是否挂起 | tinyint | √ | 1 激活 2挂起 |
字段 | 类型 | NULL | 备注 |
---|---|---|---|
ID_ | varchar(64) | NO | |
REV_ | int(11) | YES | |
CATEGORY_ | varchar(255) | YES | 流程定义的分类 |
NAME_ | varchar(255) | YES | 流程定义名称 |
KEY_ | varchar(255) | NO | 流程定义的key |
VERSION_ | int(11) | NO | |
DEPLOYMENT_ID_ | varchar(64) | YES | 流程定义对应的部署数据ID |
RESOURCE_NAME_ | varchar(4000) | YES | 流程定义对应的资源名称 |
DGRM_RESOURCE_NAME_ | varchar(4000) | YES | 流程定义对应的流程图资源名称 |
DESCRIPTION_ | varchar(4000) | YES | |
HAS_START_FORM_KEY_ | tinyint(4) | YES | |
HAS_GRAPHICAL_NOTATION_ | tinyint(4) | YES | |
SUSPENSION_STATE_ | int(11) | YES | 表示流程定义的状态是激活还是终止,1:激活,2:终止,如果流程定义状态是终止状态则不能启动该流程 |
TENANT_ID_ | varchar(255) | YES | |
ENGINE_VERSION_ | varchar(255) | YES |
运行实例表 (ACT_RU_)
运行时数据表用来保存流程在运行过程中所产生的数据,例如流程实例、执行流、任务等
运行时数据表的名称以 ACT_RU 开头,“RU ”是单词 runtime 的缩写。
ACT_RU_EVENT_SUBSCR 事件描述表
事件描述表( act_ru_event_subscr )
如果流程到达某类事件节点, Activiti 会往ACT_RUN_EVENT_SUBSCR表中加入事件描述数据,这些事件描述数据将会决定流程事件的触发。
字段名称 | 字段描述 | 数据类型 | 主键 | 为空 | 取值说明 |
---|---|---|---|---|---|
ID_ | 事件ID | nvarchar(64) | √ | 事件ID | |
REV_ | 版本 | int | √ | 乐观锁Version | |
EVENT_TYPE_ | 事件类型 | nvarchar(255) | 事件类型 | ||
EVENT_NAME_ | 事件名称 | nvarchar(255) | √ | 事件名称 | |
EXECUTION_ID_ | 执行实例ID | nvarchar(64) | √ | 执行实例ID | |
PROC_INST_ID_ | 流程实例ID | nvarchar(64) | √ | 流程实例ID | |
ACTIVITY_ID_ | 活动实例ID | nvarchar(64) | √ | 活动实例ID | |
CONFIGURATION_ | 配置 | nvarchar(255) | √ | 配置 | |
CREATED_ | 是否创建 | datetime | 默认值 当前系统时间戳CURRENT_TIMESTAMP |
字段 | 类型 | NULL | 备注 |
---|---|---|---|
ID_ | varchar(64) | NO | |
REV_ | int(11) | YES | |
EVENT_TYPE_ | varchar(255) | NO | 事件类型,不同事件会产生不同类型的事件描述,并不是所有的事件都会产生时间描述 |
EVENT_NAME_ | varchar(255) | YES | 事件名称,在流程文件中定义 |
EXECUTION_ID_ | varchar(64) | YES | 事件所在的执行流ID |
PROC_INST_ID_ | varchar(64) | YES | 时间所在的流程实例ID |
ACTIVITY_ID_ | varchar(64) | YES | 具体事件的ID,在流程文件中定义 |
CONFIGURATION_ | varchar(255) | YES | 事件的配置属性,该字段中有可能存放流程定义ID,执行流ID,或者其他数据 |
CREATED_ | timestamp | NO | |
PROC_DEF_ID_ | varchar(64) | YES | |
TENANT_ID_ | varchar(255) | YES |
ACT_RU_EXECUTION 运行时流程执行实例表
运行时流程执行实例表( act_ru_execution )
流程启动后,会产生一个流程实例,同时会产生相应的执行流,流程实例和执行流数据均
被保存在 ACT_RU_EXECUTION 表中,如果一个流程实例只有一条执行流,那么该表中只产
生一条数据,该数据既表示执行流,也表示流程实例。
字段名称 | 字段描述 | 数据类型 | 主键 | 为空 | 取值说明 |
---|---|---|---|---|---|
ID_ | ID_ | nvarchar(64) | √ | ID_ | |
REV_ | 乐观锁 | int | √ | 乐观锁 | |
PROC_INST_ID_ | 流程实例ID | nvarchar(64) | 流程实例ID | ||
BUSINESS_KEY_ | 业务主键ID | nvarchar(255) | √ | 业务主键ID | |
PARENT_ID_ | 父节点实例ID | nvarchar(64) | √ | 父节点实例ID | |
PROC_DEF_ID_ | 流程定义ID | nvarchar(64) | √ | 流程定义ID | |
SUPER_EXEC_ | SUPER_EXEC_ | nvarchar(64) | √ | SUPER_EXEC_ | |
ACT_ID_ | 节点实例ID | nvarchar(255) | √ | 节点实例ID即ACT_HI_ACTINST中ID | |
IS_ACTIVE_ | 是否存活 | tinyint | √ | 是否存活 | |
IS_CONCURRENT_ | 是否并行 | tinyint | √ | 是否为并行(true/false) | |
IS_SCOPE_ | IS_SCOPE_ | tinyint | √ | IS_SCOPE_ | |
IS_EVENT_SCOPE_ | IS_EVENT_SCOPE_ | tinyint | √ | IS_EVENT_SCOPE_ | |
SUSPENSION_STATE_ | 是否挂起 | tinyint | √ | 挂起状态 1激活 2挂起 | |
CACHED_ENT_STATE_ | int | √ |
字段 | 类型 | NULL | 备注 |
---|---|---|---|
ID_ | varchar(64) | NO | |
REV_ | int(11) | YES | |
PROC_INST_ID_ | varchar(64) | YES | 流程实例ID,一个流程实例可能产生多个执行流,该字段表示执行流所属的流程实例 |
BUSINESS_KEY_ | varchar(255) | YES | 启动流程时指定的业务主键 |
PARENT_ID_ | varchar(64) | YES | 父执行流的ID,一个流程实例可能会产生多个执行流,该字段表示父执行ID |
PROC_DEF_ID_ | varchar(64) | YES | 流程定义数据ID |
SUPER_EXEC_ | varchar(64) | YES | |
ROOT_PROC_INST_ID_ | varchar(64) | YES | |
ACT_ID_ | varchar(255) | YES | 当前执行流行为的ID,ID在流程文件中定义 |
IS_ACTIVE_ | tinyint(4) | YES | 该执行流是否活跃的标识 |
IS_CONCURRENT_ | tinyint(4) | YES | 执行流是否正在并行 |
IS_SCOPE_ | tinyint(4) | YES | |
IS_EVENT_SCOPE_ | tinyint(4) | YES | |
IS_MI_ROOT_ | tinyint(4) | YES | |
SUSPENSION_STATE_ | int(11) | YES | 标识流程的中断状态 |
CACHED_ENT_STATE_ | int(11) | YES | |
TENANT_ID_ | varchar(255) | YES | |
NAME_ | varchar(255) | YES | |
START_TIME_ | datetime | YES | |
START_USER_ID_ | varchar(255) | YES | |
LOCK_TIME_ | timestamp | YES | |
IS_COUNT_ENABLED_ | tinyint(4) | YES | |
EVT_SUBSCR_COUNT_ | int(11) | YES | |
TASK_COUNT_ | int(11) | YES | |
JOB_COUNT_ | int(11) | YES | |
TIMER_JOB_COUNT_ | int(11) | YES | |
SUSP_JOB_COUNT_ | int(11) | YES | |
DEADLETTER_JOB_COUNT_ | int(11) | YES | |
VAR_COUNT_ | int(11) | YES | |
ID_LINK_COUNT_ | int(11) | YES |
ACT_RU_IDENTITYLINK 运行时流程人员表
运行时流程人员表( act_ru_identitylink )
任务参与者数据表。主要存储当前节点参与者的信息。
流程与身份关系,用户或者用户组与流程数据之间的关系
字段名称 | 字段描述 | 数据类型 | 主键 | 为空 | 取值说明 |
---|---|---|---|---|---|
ID_ | ID_ | nvarchar(64) | √ | ID_ | |
REV_ | 乐观锁 | int | √ | 乐观锁 | |
GROUP_ID_ | 组ID | nvarchar(64) | √ | 组ID | |
TYPE_ | 类型 | nvarchar(255) | √ | 备注7 | |
USER_ID_ | 用户ID | nvarchar(64) | √ | 用户ID | |
TASK_ID_ | 节点实例ID | nvarchar(64) | √ | 节点实例ID | |
PROC_INST_ID_ | 流程实例ID | nvarchar(64) | √ | 流程实例ID | |
PROC_DEF_ID_ | 流程定义ID | nvarchar(255) | √ | 流程定义ID |
字段 | 类型 | NULL | 备注 |
---|---|---|---|
ID_ | varchar(64) | NO | |
REV_ | int(11) | YES | |
GROUP_ID_ | varchar(255) | YES | 该关系数据中的用户组ID |
TYPE_ | varchar(255) | YES | 该关系数据的类型,assignee(指派人或组),candidate(候选人或组),owner(拥有人) |
USER_ID_ | varchar(255) | YES | 关系数据中的用户ID |
TASK_ID_ | varchar(64) | YES | 关系数据中的任务ID |
PROC_INST_ID_ | varchar(64) | YES | 关系数据中的流程实例ID |
PROC_DEF_ID_ | varchar(64) | YES | 关系数据中的流程定义ID |
ACT_RU_JOB 运行时定时任务数据表
运行时定时任务数据表( act_ru_job )
字段名称 | 字段描述 | 数据类型 | 主键 | 为空 | 取值说明 |
---|---|---|---|---|---|
ID_ | 标识 | nvarchar(64) | √ | 标识 | |
REV_ | 版本 | int | √ | 版本 | |
TYPE_ | 类型 | nvarchar(255) | 类型 | ||
LOCK_EXP_TIME_ | 锁定释放时间 | datetime | √ | 锁定释放时间 | |
LOCK_OWNER_ | 挂起者 | nvarchar(255) | √ | 挂起者 | |
EXCLUSIVE_ | bit | √ | |||
EXECUTION_ID_ | 执行实例ID | nvarchar(64) | √ | 执行实例ID | |
PROCESS_INSTANCE_ID_ | 流程实例ID | nvarchar(64) | √ | 流程实例ID | |
PROC_DEF_ID_ | 流程定义ID | nvarchar(64) | √ | 流程定义ID | |
RETRIES_ | int | √ | |||
EXCEPTION_STACK_ID_ | 异常信息ID | nvarchar(64) | √ | 异常信息ID | |
EXCEPTION_MSG_ | 异常信息 | nvarchar(4000) | √ | 异常信息 | |
DUEDATE_ | 到期时间 | datetime | √ | 到期时间 | |
REPEAT_ | 重复 | nvarchar(255) | √ | 重复 | |
HANDLER_TYPE_ | 处理类型 | nvarchar(255) | √ | 处理类型 | |
HANDLER_CFG_ | nvarchar(4000) | √ | 标识 |
字段 | 类型 | NULL | 备注 |
---|---|---|---|
ID_ | varchar(64) | NO | |
REV_ | int(11) | YES | |
TYPE_ | varchar(255) | NO | |
LOCK_EXP_TIME_ | timestamp | YES | |
LOCK_OWNER_ | varchar(255) | YES | |
EXCLUSIVE_ | tinyint(1) | YES | |
EXECUTION_ID_ | varchar(64) | YES | |
PROCESS_INSTANCE_ID_ | varchar(64) | YES | |
PROC_DEF_ID_ | varchar(64) | YES | |
RETRIES_ | int(11) | YES | |
EXCEPTION_STACK_ID_ | varchar(64) | YES | |
EXCEPTION_MSG_ | varchar(4000) | YES | |
DUEDATE_ | timestamp | YES | |
REPEAT_ | varchar(255) | YES | |
HANDLER_TYPE_ | varchar(255) | YES | |
HANDLER_CFG_ | varchar(4000) | YES | |
TENANT_ID_ | varchar(255) | YES |
ACT_RU_TASK 运行时任务节点表
运行时任务节点表( act_ru_task )
流程在运行过程中所产生的任务数据保存在 ACT_RU_TASK 表中
字段名称 | 字段描述 | 数据类型 | 主键 | 为空 | 取值说明 |
---|---|---|---|---|---|
ID_ | ID_ | nvarchar(64) | √ | ID_ | |
REV_ | 乐观锁 | int | √ | 乐观锁 | |
EXECUTION_ID_ | 执行实例ID | nvarchar(64) | √ | 执行实例ID | |
PROC_INST_ID_ | 流程实例ID | nvarchar(64) | √ | 流程实例ID | |
PROC_DEF_ID_ | 流程定义ID | nvarchar(64) | √ | 流程定义ID | |
NAME_ | 节点定义名称 | nvarchar(255) | √ | 节点定义名称 | |
PARENT_TASK_ID_ | 父节点实例ID | nvarchar(64) | √ | 父节点实例ID | |
DESCRIPTION_ | 节点定义描述 | nvarchar(4000) | √ | 节点定义描述 | |
TASK_DEF_KEY_ | 节点定义的KEY | nvarchar(255) | √ | 任务定义的ID | |
OWNER_ | 实际签收人 | nvarchar(255) | √ | 拥有者(一般情况下为空,只有在委托时才有值) | |
ASSIGNEE_ | 签收人或委托人 | nvarchar(255) | √ | 签收人或委托人 | |
DELEGATION_ | 委托类型 | nvarchar(64) | √ | 备注8 | |
PRIORITY_ | 优先级别 | int | √ | 优先级别,默认为:50 | |
CREATE_TIME_ | 创建时间 | datetime | √ | 创建时间 | |
DUE_DATE_ | 过期时间 | datetime | √ | 耗时 | |
SUSPENSION_STATE_ | 是否挂起 | int | √ | 1代表激活 2代表挂起 |
字段 | 类型 | NULL | 备注 |
---|---|---|---|
ID_ | varchar(64) | NO | |
REV_ | int(11) | YES | |
EXECUTION_ID_ | varchar(64) | YES | 任务所在的执行流ID |
PROC_INST_ID_ | varchar(64) | YES | 对应的流程实例ID |
PROC_DEF_ID_ | varchar(64) | YES | 对应流程定义数据ID |
NAME_ | varchar(255) | YES | 任务名称,在流程文件中定义 |
PARENT_TASK_ID_ | varchar(64) | YES | |
DESCRIPTION_ | varchar(4000) | YES | 任务描述,在流程文件中配置 |
TASK_DEF_KEY_ | varchar(255) | YES | 任务定义的ID值,在流程文件中定义 |
OWNER_ | varchar(255) | YES | 任务拥有人,没有做外键关联 |
ASSIGNEE_ | varchar(255) | YES | 被指派执行该任务的人,没有做外键关联 |
DELEGATION_ | varchar(64) | YES | |
PRIORITY_ | int(11) | YES | 任务优先级数值 |
CREATE_TIME_ | timestamp | YES | |
DUE_DATE_ | datetime | YES | 任务预定日期 |
CATEGORY_ | varchar(255) | YES | |
SUSPENSION_STATE_ | int(11) | YES | |
TENANT_ID_ | varchar(255) | YES | |
FORM_KEY_ | varchar(255) | YES | |
CLAIM_TIME_ | datetime | YES |
ACT_RU_VARIABLE 运行时流程变量数据表
运行时流程变量数据表( act_ru_variable )
Activiti 提供了ACT_RU_VAEIABLE表来存放流程中的参数,这类参数包括流程实例参数、执行流参数和任务参数,参数有可能会有多种类型
字段名称 | 字段描述 | 数据类型 | 主键 | 为空 | 取值说明 |
---|---|---|---|---|---|
ID_ | ID_ | nvarchar(64) | √ | 主键标识 | |
REV_ | 乐观锁 | int | √ | 乐观锁 | |
TYPE_ | 类型 | nvarchar(255) | 备注9 | ||
NAME_ | 名称 | nvarchar(255) | 变量名称 | ||
EXECUTION_ID_ | 执行实例ID | nvarchar(64) | √ | 执行的ID | |
PROC_INST_ID_ | 流程实例ID | nvarchar(64) | √ | 流程实例ID | |
TASK_ID_ | 节点实例ID | nvarchar(64) | √ | 节点实例ID(Local) | |
BYTEARRAY_ID_ | 字节表ID | nvarchar(64) | √ | 字节表的ID(ACT_GE_BYTEARRAY) | |
DOUBLE_ | DOUBLE_ | float | √ | 存储变量类型为Double | |
LONG_ | LONG_ | numeric(19) | √ | 存储变量类型为long | |
TEXT_ | TEXT_ | nvarchar(4000) | √ | ‘存储变量值类型为String 如此处存储持久化对象时,值jpa对象的class | |
TEXT2_ | TEXT2_ | nvarchar(4000) | √ | 此处存储的是JPA持久化对象时,才会有值。此值为对象ID |
字段 | 类型 | NULL | 备注 |
---|---|---|---|
ID_ | varchar(64) | NO | |
REV_ | int(11) | YES | |
TYPE_ | varchar(255) | NO | 参数类型,该字段值可以为:boolean、bytes、serializable、date、double、integer、jpa-entity、long、null、short、string,这些字段值均为activiti提供,还可以通过扩展来自定义参数类型 |
NAME_ | varchar(255) | NO | 参数名称 |
EXECUTION_ID_ | varchar(64) | YES | 该参数对应的执行ID,NULL |
PROC_INST_ID_ | varchar(64) | YES | 该参数对应的流程实例ID,NULL |
TASK_ID_ | varchar(64) | YES | 如果该参数是任务参数,就需要设置任务ID |
BYTEARRAY_ID_ | varchar(64) | YES | 如果参数值是序列化对象,那么可以将该对象作为资源保存到资源表中,该字段保存资源表中的数据ID |
DOUBLE_ | double | YES | 参数类型为double的话则值会保存在该字段中 |
LONG_ | bigint(20) | YES | 参数类型为long的话则值会保存在该字段中 |
TEXT_ | varchar(4000) | YES | 保存文本类型的参数 |
TEXT2_ | varchar(4000) | YES |
工作数据表(以下表结构暂时未整理)
ACT_RU_DEADLETTER 无法执行工作表
字段 | 类型 | NULL | 备注 |
---|---|---|---|
ID_ | varchar(64) | NO | |
REV_ | int(11) | YES | |
TYPE_ | varchar(255) | NO | |
EXCLUSIVE_ | tinyint(1) | YES | |
EXECUTION_ID_ | varchar(64) | YES | |
PROCESS_INSTANCE_ID_ | varchar(64) | YES | |
PROC_DEF_ID_ | varchar(64) | YES | |
EXCEPTION_STACK_ID_ | varchar(64) | YES | |
EXCEPTION_MSG_ | varchar(4000) | YES | |
DUEDATE_ | timestamp | YES | |
REPEAT_ | varchar(255) | YES | |
HANDLER_TYPE_ | varchar(255) | YES | |
HANDLER_CFG_ | varchar(4000) | YES | |
TENANT_ID_ | varchar(255) | YES |
ACT_RU_SUSPENDED 中断工作表
字段 | 类型 | NULL | 备注 |
---|---|---|---|
ID_ | varchar(64) | NO | |
REV_ | int(11) | YES | |
TYPE_ | varchar(255) | NO | |
EXCLUSIVE_ | tinyint(1) | YES | |
EXECUTION_ID_ | varchar(64) | YES | |
PROCESS_INSTANCE_ID_ | varchar(64) | YES | |
PROC_DEF_ID_ | varchar(64) | YES | |
RETRIES_ | int(11) | YES | |
EXCEPTION_STACK_ID_ | varchar(64) | YES | |
EXCEPTION_MSG_ | varchar(4000) | YES | |
DUEDATE_ | timestamp | YES | |
REPEAT_ | varchar(255) | YES | |
HANDLER_TYPE_ | varchar(255) | YES | |
HANDLER_CFG_ | varchar(4000) | YES | |
TENANT_ID_ | varchar(255) | YES |
ACT_RU_TIMER_JOB 定时器工作表
字段 | 类型 | NULL | 备注 |
---|---|---|---|
ID_ | varchar(64) | NO | |
REV_ | int(11) | YES | |
TYPE_ | varchar(255) | NO | |
LOCK_EXP_TIME_ | timestamp | YES | |
LOCK_OWNER_ | varchar(255) | YES | |
EXCLUSIVE_ | tinyint(1) | YES | |
EXECUTION_ID_ | varchar(64) | YES | |
PROCESS_INSTANCE_ID_ | varchar(64) | YES | |
PROC_DEF_ID_ | varchar(64) | YES | |
RETRIES_ | int(11) | YES | |
EXCEPTION_STACK_ID_ | varchar(64) | YES | |
EXCEPTION_MSG_ | varchar(4000) | YES | |
DUEDATE_ | timestamp | YES | |
REPEAT_ | varchar(255) | YES | |
HANDLER_TYPE_ | varchar(255) | YES | |
HANDLER_CFG_ | varchar(4000) | YES | |
TENANT_ID_ | varchar(255) | YES |
其他
ACT_EVT_LOG 事件日志表
Activiti 事件日志表( act_evt_log )
字段名称 | 字段描述 | 数据类型 | 为空 | 取值说明 |
---|---|---|---|---|
LOG_NR_ | 主键 | bigint(20) | NO | 自增长 |
TYPE_ | 类型 | varchar(64) | YES | 类型 |
PROC_DEF_ID_ | 流程定义ID | varchar(64) | YES | 流程定义ID |
PROC_INST_ID_ | 流程实例ID | varchar(64) | YES | 流程实例ID |
EXECUTION_ID_ | 执行实例ID | varchar(64) | YES | 执行实例ID |
TASK_ID_ | 节点实例ID | varchar(64) | YES | 节点实例ID |
TIME_STAMP_ | 时间戳 | timestamp(3) | NO | 时间戳 |
USER_ID_ | 用户ID | varchar(255) | YES | 用户ID |
DATA_ | 一些额外参数 | longblob | YES | 例如 IP地址 等。 |
LOCK_OWNER_ | 挂起者 | varchar(255) | YES | 暂时没有用到 |
LOCK_TIME_ | 挂起时间 | timestamp(3) | YES | 暂时没有用到 |
IS_PROCESSED_ | 是否处理过了 | tinyint(4) | YES | 暂时没有用到 |
ACT_PROCDEF_INFO 流程定义扩展表
关联ACTGE_BYTEARRAY与PROC_DEF_ID表。
字段名称 | 字段描述 | 数据类型 | 为空 | 取值说明 |
---|---|---|---|---|
ID_ | 主键 | varchar(64) | NO | 主键ID |
PROC_DEF_ID_ | 流程定义ID | varchar(64) | NO | 流程定义ID |
REV_ | 乐观锁 | int(11) | YES | 默认值 NULL,version版本 |
INFO_JSON_ID_ | 主键 | varchar(64) | YES | ACT_GE_BYTEARRAY的ID |
DMN规则引擎数据表
决策部署表,act_dmn_deployment
保存决策数据,类似于流程定义部署,每一次部署,可以添加多份决策文件,向部署表中写入一条部署数据。
只启动流程引擎,并不会创建规则引擎表。
字段 | 注释 |
---|---|
NAME_ | 部署名称 |
CATEGORY_ | 部署的目录名称 |
PARENT_DEPLOYMENT_ID_ | 父部署ID |
决策表,act_dmn_decision_table
可以先将决策看做流程定义,决策文件中保存着决策表,部署时会解析决策文件中的决策模型并将其保存到act_dmn_decision_table中。
字段 | 注释 |
---|---|
KEY_ | 决策业务主键 |
DEPLOYMENT_ID_ | 所属的部署数据ID |
部署资源表,act_dmn_deployment_resource
规则引擎相关的资源,例如决策文件、图片等,被保存在act_dmn_deployment_resource表中,该表类似于流程引擎的资源表。
字段 | 注释 |
---|---|
NAME_ | 资源名称 |
DEPLOYMENT_ID_ | 所属的部署数据ID |
RESOURCE_BYTES_ | 资源内容,longblob类型。 |
结语
Activiti6.0的表相较于Activiti5有所不同,但核心的内容实质上还是一样,数据库这一块没有太大差别,对于这么多表,大体过一遍有个印象即可,Activiti6.0的重点仍在核心API。
Activiti 依赖事务监听器(上)
Activiti 依赖事务监听器
Activiti5.x
Activiti5.x版本中可以通过监听器辅助自身的业务操作,比如自定义一个执行监听器,则只需要定义一个类,然后实现org.activiti.engine.delegate.ExecutionListener接口即可。任务监听器需要实现org.activiti.engine.delegate.TaskListene。当然也可以自定义表达式以及委托表达式的方式实现。
当监听器被引擎触发的时候,会自动触发所有不同类型的听众(自定义监听器、内置监听器、历史监听器等)。但是自定义的监听器如果出现异常或者错误,那么这些监听器的结果并不依赖事务,通俗一点的描述就是事务回滚之后监听器会重复的执行,这就完全不对了。用下面的例子对其进行说明:
上图的流程文档如下所示:
<process id="bookflight" name="Book Flight" isExecutable="true">
<startEvent id="start-event-1" />
<sequenceFlow id="flow1" sourceRef="start-event-1" targetRef="book-flight" />
<serviceTask id="book-flight" name="Book flight" activiti:delegateExpression="${bookFlightBean}" activiti:async="true">
<extensionElements>
<activiti:executionListener event="end" delegateExpression="${emailBean}"/>
</extensionElements>
</serviceTask>
<sequenceFlow id="flow2" sourceRef="book-flight" targetRef="charge-credit-card">
</sequenceFlow>
<serviceTask id="charge-credit-card" name="Charge credit card" activiti:delegateExpression="${chargeCCBean}">
</serviceTask>
<sequenceFlow id="flow3" sourceRef="charge-credit-card" targetRef="do-something-else" />
<serviceTask id="do-something-else" name="Do something else" activiti:delegateExpression="${doSomethingBean}" activiti:async="true">
</serviceTask>
<sequenceFlow id="flow4" sourceRef="do-something-else" targetRef="end-event-1" />
<endEvent id="end-event-1" />
</process>
假设我们想发送电子邮件预订航班时,信用卡被指控成功。这当然可以以不同的方式来完成的。但是对于本例的缘故我们会通过实现执行侦听器和配置它的‘结束’事件的书飞行活动。
部署并启动上述的流程文档,活动成功。配置的执行侦听器会实现触发和发送电子邮件。
然后收取信用卡的活动执行。这导致一个例外。事务回滚和“书飞行”和“收取信用卡”将再次执行。这意味着我们执行侦听器也将再次执行。
这只是一个简单的例子。并且有许多用例中,您想要侦听器每次执行。但在有些情况下,比如在上面的示例中需要有可能让监听器基于整个事务的结果。比如事务成功,则监听器中的业务逻辑触发,事务失败则监听器中的业务逻辑不应该被触发。
Activiti 6.x
Activiti6中已经提供了事务监听器,以下代码片段显示了一个适应上面的例子,现在与事务相关的执行侦听器配置。
<serviceTask id="book-flight" name="Book flight" activiti:delegateExpression="${bookFlightBean}" activiti:async="true">
<extensionElements>
<activiti:executionListener event="end" delegateExpression="${emailBean}" onTransaction="committed" />
</extensionElements>
</serviceTask>
上述代码中,可以通过设置executionListener元素中的onTransaction属性进行事务状态的的定义,即事务的状态监听器的执行以来事务的状态。onTransaction属性可以有如下三个值:
· Committed(提交)
· rolled-back(回滚)
· before-commit(提交前)
注意:Activiti5.X设计器不支持设置onTransaction属性的设置。
新接口
依赖事务执行监听器
依赖事务监听器必须实现一个不同的接口而不是“普通”执行侦听器。TransactionDependentExecutionListener接口提供了一种方法,需要自行实现。该接口的定义如下所示:
public interface TransactionDependentExecutionListener extends BaseExecutionListener {
String ON_TRANSACTION_BEFORE_COMMIT = "before-commit";
String ON_TRANSACTION_COMMITTED = "committed";
String ON_TRANSACTION_ROLLED_BACK = "rolled-back";
void notify(String processInstanceId, String executionId, FlowElement flowElement,
MapexecutionVariables, MapcustomPropertiesMap);
}
依赖事务任务监听器
对于依赖事务任务监听器TransactionDependentTaskListener接口的定义如下:
public interface TransactionDependentTaskListener extends BaseTaskListener {
String ON_TRANSACTION_COMMITTING = "before-commit";
String ON_TRANSACTION_COMMITTED = "committed";
String ON_TRANSACTION_ROLLED_BACK = "rolled-back";
void notify(String processInstanceId, String executionId, Task task, MapexecutionVariables, MapcustomPropertiesMap);
}
对于最后一个输入参数customPropertiesMap,他是可选的。可以提供一个configureable属性解析器。(参考下文的ReceiveTask示例)。
Activiti 依赖事务监听器(下)
Activiti 依赖事务监听器
Activiti依赖事务监听器(上)讲解了Activiti依赖事务监听器的概念,接下来看一下该如何使用TransactionDependentExecutionListener。
当引擎解析BPMN XML模型的Java模型的“常规”执行/任务听众会转换为ActivitiListeners事务相关的监听器。如果引擎操作的执行期间,遇到ActivitiListener要检查它是否一个事务依赖。如果是这样,它不执行它那一刻,就像它与其他听众,它计划为以后处理。
这种延迟处理需要拍摄快照依此当前的执行状态,因为事情可以改变实际执行侦听器之前。甚至更重要的是,执行本身执行侦听器时不再可用。
(关于这一点涉及到了源码的讲解,后续章节会详细的讲解)。
让我们看一下如何使用该接口。
首先定义一个流程文档,该流程文档对应的图如下所示:
上图对应的流程文档XML内容如下所示:
<process id="transaction-dependent-listeners">
<startEvent id="start">
<extensionElements>
<activiti:executionListener delegateExpression="${myActivityLogger}" event="start" />
</extensionElements>
</startEvent>
<sequenceFlow id="flow1" sourceRef="start" targetRef="script-task-1"/>
<scriptTask id="script-task-1" name="Script Task One" activiti:async="true" scriptFormat="groovy">
<extensionElements>
<activiti:executionListener delegateExpression="${myActivityLogger}" event="start" />
<activiti:executionListener delegateExpression="${myMessageProducer}" event="start" onTransaction="committed" />
</extensionElements>
<script>
println 'script task one; start new transaction'
</script>
</scriptTask>
<sequenceFlow id="flow2" sourceRef="script-task-1" targetRef="receive-task-1"/>
<receiveTask id="receive-task-1" name="Wait">
<extensionElements>
<activiti:executionListener delegateExpression="${myActivityLogger}" event="start" />
</extensionElements>
</receiveTask>
<sequenceFlow id="flow3" sourceRef="receive-task-1" targetRef="script-task-2"/>
<scriptTask id="script-task-2" name="Script Task Two" activiti:async="true" scriptFormat="groovy">
<extensionElements>
<activiti:executionListener delegateExpression="${myActivityLogger}" event="start" />
</extensionElements>
<script>
println 'script task two; start new transaction'
</script>
</scriptTask>
<sequenceFlow id="flow4" sourceRef="script-task-2" targetRef="end"/>
<endEvent id="end">
<extensionElements>
<activiti:executionListener delegateExpression="${myActivityLogger}" event="start" />
</extensionElements>
</endEvent>
</process>
当Activiti遇到ReceiveTask执行进入等待状态。这意味着流程实例逗留在在ACT_RU_EXECUTION表中。
假设在上面的示例脚本任务一个的将JMS队列上的消息。和消费者的消息将会发送一个信号流程实例(继续从等待状态)。如果发送信号发生的非常快,等待状态尚未就可能依然存在。这种情况是例外,但它可能发生。
下面看一下JMS的配置:
生产者:
/**
* @author www.shareniu.com
*/
@Component("myMessageProducer")
public class MyMessageProducer implements TransactionDependentExecutionListener {
private static final Logger logger = LoggerFactory.getLogger(MyMessageProducer.class);
@Autowired
private JmsTemplate jmsTemplate;
public void notify(String processInstanceId, String executionId, FlowElement currentFlowElement, MapexecutionVariables, MapcustomPropertiesMap) {
logger.debug("Sending message <{}> to queue", executionId);
jmsTemplate.convertAndSend("receive_task_signal", executionId);
}
}
消费者:
/**
* @author www.shareniu.com
*/
@Component
public class MyMessageConsumer {
private static final Logger logger = LoggerFactory.getLogger(MyMessageConsumer.class);
@Autowired
private RuntimeService runtimeService;
@JmsListener(destination = "receive_task_signal", containerFactory = "myFactory")
public void receiveMessage(String executionId) {
logger.debug("Received message: <" + executionId + ">");
logger.debug("Signaling execution with id: <" + executionId + ">");
runtimeService.trigger(executionId);
}
}
在上面的示例脚本任务一个只是一个占位符标记的开始一个事务。这同样适用于“脚本任务两个”。的脚本任务一个和接收任务“等待”是相同的事务的一部分。这意味着尽管myMessageProducer的侦听器配置的脚本任务一个侦听器将在“等待”是坚持执行。
运行上述的代码,执行如下的脚本即可。
mvn spring-boot:run
控制台的输出如下:
2017-02-07 15:18:52.229 DEBUG 1243900 --- [cTaskExecutor-1] o.a.demo.listener.MyActivityLogger : Current activity id:
script task one; start new transaction
2017-02-07 15:18:52.653 DEBUG 1243900 --- [cTaskExecutor-1] o.a.demo.listener.MyActivityLogger : Current activity id:
2017-02-07 15:18:52.657 DEBUG 1243900 --- [cTaskExecutor-1] o.a.demo.listener.MyMessageProducer : Sending message <5> to queue
2017-02-07 15:18:52.689 DEBUG 1243900 --- [enerContainer-1] org.activiti.demo.jms.MyMessageConsumer : Received message: <5>
2017-02-07 15:18:52.689 DEBUG 1243900 --- [enerContainer-1] org.activiti.demo.jms.MyMessageConsumer : Signaling execution with id: <5>
2017-02-07 15:18:52.696 DEBUG 1243900 --- [cTaskExecutor-2] o.a.demo.listener.MyActivityLogger : Current activity id:
script task two; start new transaction
2017-02-07 15:18:52.703 DEBUG 1243900 --- [cTaskExecutor-2] o.a.demo.listener.MyActivityLogger : Current activity id:
Activiti6 特性
了解 Activiti6 新增特性
本文重点分析 Activiti6
新增的一些特性,从而可以更好的了解 Activiti6
的走向。
Activiti6最大的变化点就是对代码进行了重构,该版本修复以往的Bug并不多,但内部实现相对来说变化比较大。其突出的变化如下所示:
-
新增两款新引擎,Form引擎和DMN引擎(动态引擎)。其中DMN引擎允许开发人员创建自己的决策表。可以通过变量和定义的规则方式从决策表中计算结果。这些决策表的数据可以被rule task调用,决策表与流程实例是完全隔离的,相互之间不需要知道对方的存在。Form引擎可以通过Activiti6 UI界面进行配置,通俗一点的理解就是Activiti6将Form表单独立出来了。Form表单信息可以以JSON格式进行定义和使用。Activiti6 UI 默认包括新的规则引擎和表单引擎。
-
新增ad-hoc子流程。可以参考文章(ad-hoc子流程使用)。
-
作业执行器被重构。Activiti6版本仅保留了Activiti5版本中的异步作业执行器(async executor)。定时作业被划分了四个不同的表:executable jobs, timer jobs, suspended jobs 和deadletter jobs。引擎可以更快的执行作业。定时器作业在新的版本存储于单独的表中,一个线程会定时轮训需要执行的作业,快到期的作业会被添加到suspended jobs表中。重试的作业已经被干掉了,需要重试的作业会被添加到deadletter jobs表中。这样的重构意义主要是为了提高查询效率,可以执行的作业可以很快的被查询出来。
-
作业执行器通过消息队列的方式进行,关于这一点可以参考随后的文章。
-
瞬态变量的引入。瞬态变量不会存储到 Activiti 变量表中,但仍为单一的事务持续时间执行。比如可以在REST服务之间进行调用的时候使用,或者使用于Java service task。
-
引入了事务依赖监听器,可以参考Activiti 依赖事务监听器(上)的讲解。
-
Activiti 6 UI 程序中,添加了DMN编辑器。
-
对于多实例节点而言,添加了终止多实例节点的相关方法。这个特性允许开发人员使用API结束多实例所有节点的执行。
-
优化补偿活动行为和在子流程中的使用。
-
在运行流程实例以及执行实例中添加了开始时间以及启动流程实例的人字段。在ru_task中增加了任务的认领时间(claim time)。
-
妥善解决数据库架构 (oracle/postgres) 的使用。
-
修复历史数据捕获。
-
大量重构 Activiti 6 UI 应用程序,例如应用程序中定义现在部署作为正常的活动部署,没有为其单独的应用程序定义表。
-
改进Activiti 6 QA中的问题。
Activiti 6.0 工作流入门
Activiti 6.0 工作流入门学习
工作流介绍
工作流: 是对工作流程及其各操作步骤之间业务规则的抽象、概括描述
工作流建模: 即将工作流程中的工作如何前后组织在一起的逻辑和规则,在计算机中以恰当的模型表达并对其实施计算
要解决的问题: 是为实某个业务目标,利用 计算机在多个参与者之间按某种预定规则自动传递文档、信息或者任务
- ProcessEnigne 流程引擎
- RepositoryService 流程仓库Service,可以管理流程仓库例如部署删除读取流程资源
- RuntimeService 运行时Service可以处理所有运行状态的流程实例流程控制(开始,暂停,挂起等)
- TaskService 任务Service用于管理、查询任务,例如签收、办理、指派等
- IdentitiServicec 身份Service可以管理查询用户、组之间的关系
- FormService 表单Service用于读取和流程、任务相关的表单数据
- HistoryService 历史Service用于查询所有的历史数据
- ManagementService 引擎管理Service,和具体业务无关,主要查询引擎配置,数据库作业
- DynamicBpmService 动态bpm服务
数据模型设计
数据表分类 | 描述 |
---|---|
ACT_GE_* | 通用数据表 |
ACT_RE_* | 流程定义存储表 |
ACT_ID_* | 身份信息表 |
ACT_RU_* | 运行时数据表 |
ACT_HI_* | 历史数据表 |
BPM2.0元素
- 流对象(FlowObject)
- 链接对象(ConnectingObject)
- 数据(Data)
- 泳道(Swimlanes)
- 描述对象(Artifacts)
审批流程模型化
购物工作流程模型化
部署Activiti
准备环境:
- Activiti User Guide
- Quick Start Guide
- Java Docs
- Activiti软件包activiti-6.0.0.zip
- java环境1.8
- tomcat
-
压缩activiti-6.0.0.zip找到wars把里面的activiti-admin.war,activiti-app.war复制到tomcat的webapps下面并启动tomcat
-
浏览器打开访问地址http://127.0.0.1:8080/activiti-app/账号amdin密码test
流程设计体验
创建用户
创建了三个用户
创建流程
画流程图并指定user
选择对应的用户
创建app
选择创建的流程保存
流程执行
切换userdev账号启动流程
切换到usertl账号进行审批
切换到userhr账号审批
登录管理员后台查看
登录http://127.0.0.1:8080/activiti-admin用管理员账号登录账号密码都是admin
修改端口号跟activiti-app项目的端口号一致这里是8080
Activiti 6.0 源码分析 helloword
Activiti 6.0 源码分析 helloword 学习
获取源码
- 然后从自己目录下把项目克隆到本地
- 切换分支>git checkout -b study6 activiti-6.0.0
- 编译>mvn clean test-compile
- 导入到编辑器
Activiti6.0模块介绍
- module/activiti-engine 核心模块
- module/activiti-spring Spring集成模块
- module/activiti-sping-boot SpringBoot集成模块
- module/activiti-rest 对外提供rest api模块
- module/activiti-form-engine 表单引擎模块
- module/activiti-ldap 集成ldap用户的模块
基于源码activiti-app运行
启动cativiti-app
cd modules/activiti-ui/activiti-app
mvn clean tomcat7:run
打开浏览器访问http://127.0.0.1:9999/activiti-app/
activiti-ui
activiti-app 集成发布的war工程
activiti-app-conf UI独立于业务外的配置
activiti-app-logic UI的业务逻辑
activiti-app-rest 提供接口的rest api
Activiti6.0初体验helloword
通过activiti-app画好流程图并设置属性
- id:startEvent | name:开始
- id:submitForm | name:填写审批信息
- id:decideSubmit | name:提交OR取消
- id:endEventCancel | name:取消
- id:tl_approve | name:主管审批
- id:decideTLApprove | name:主管审批校验
- id:hr_approve | name:人事审批
- id:decideHRApprove | name:人事审批校验
- id:endEvent |name:结束
设置form表单信息
填写审批信息表单
主管审批表单
hr审批表单
设置流转条件
提交or取消:
${submitType=="y"||submitType=="Y"}
${submitType=="n"||submitType=="N"}
主管审批校验:
${tlApprove=="y"||tlApprove=="Y"}
${tlApprove=="n"||tlApprove=="N"}
人事审批校验:
${hrApprove=="y"||hrApprove=="Y"}
${hrApprove=="n"||hrApprove=="N"}
导出工作流xml文件创建demoActiviti项目
- 把导入文件放入项目里
- 引入jar包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.guosh.activiti</groupId>
<artifactId>guosh-activiti</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--activiti核心模块-->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
<version>6.0.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--日志-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.11</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
<!--h2内存数据库-->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.3.176</version>
</dependency>
</dependencies>
</project>
- 流程demo
public class DemoMain {
private static final Logger logger= LoggerFactory.getLogger(DemoMain.class);
public static void main(String[] args) throws ParseException {
logger.info("启动程序");
//创建流程引擎(基于内存数据库的流程引擎对象)
ProcessEngine processEngine = getProcessEngine();
//部署流程定义文件
ProcessDefinition processDefinition = getProcessDefinition(processEngine);
//启动流程
ProcessInstance processInstance = getProcessInstance(processEngine, processDefinition);
//处理流程任务
processTask(processEngine, processInstance);
logger.info("结束程序");
}
private static void processTask(ProcessEngine processEngine, ProcessInstance processInstance) throws ParseException {
Scanner scanner = new Scanner(System.in);
//流程不为空并且流程没有结束
while (processInstance!=null && !processInstance.isEnded()){
//任务Service用于管理、查询任务,例如签收、办理、指派等
TaskService taskService = processEngine.getTaskService();
List<Task> list = taskService.createTaskQuery().list();
for (Task task:list) {
logger.info("待处理任务 [{}]",task.getName());
Map<String, Object> variables = getStringObjectMap(processEngine, scanner, task);
//提交
taskService.complete(task.getId(),variables);
//获取流程对象最新状态
processInstance=processEngine.getRuntimeService().createProcessInstanceQuery()
.processInstanceId(processInstance.getId()).singleResult();
}
logger.info("待处理任务数量 [{}]",list.size());
}
scanner.close();
}
private static Map<String, Object> getStringObjectMap(ProcessEngine processEngine, Scanner scanner, Task task) throws ParseException {
//表单Service用于读取和流程、任务相关的表单数据
FormService formService = processEngine.getFormService();
//获取任务表单
TaskFormData taskFormData = formService.getTaskFormData(task.getId());
//获取属性集合
List<FormProperty> formProperties = taskFormData.getFormProperties();
//存储要提交的表单
Map<String, Object> variables=new HashMap<String, Object>();
String line=null;
for (FormProperty property:formProperties){
//如果是string类型
if(StringFormType.class.isInstance(property.getType())){
logger.info("请输入 {}",property.getName());
line=scanner.nextLine();
logger.info("您输入的内容是 [{}]",line);
variables.put(property.getId(),line);
}else if(DateFormType.class.isInstance(property.getType())){
logger.info("请输入 {} 格式 (yyyy-MM-dd)",property.getName());
line=scanner.nextLine();
SimpleDateFormat dateFormat= new SimpleDateFormat("yyyy-MM-dd");
Date date = dateFormat.parse(line);
variables.put(property.getId(),date);
}else{
logger.info("类型不支持");
}
}
return variables;
}
private static ProcessInstance getProcessInstance(ProcessEngine processEngine, ProcessDefinition processDefinition) {
//运行时Service可以处理所有运行状态的流程实例流程控制(开始,暂停,挂起等)
RuntimeService runtimeService = processEngine.getRuntimeService();
ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinition.getId());
logger.info("启动流程[{}]",processInstance.getProcessDefinitionKey());
return processInstance;
}
private static ProcessDefinition getProcessDefinition(ProcessEngine processEngine) {
//流程仓库Service,可以管理流程仓库例如部署删除读取流程资源
RepositoryService repositoryService = processEngine.getRepositoryService();
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("second_approve.bpmn20.xml")
.deploy();
//部署id
String deploymentId = deployment.getId();
//流程对应对象
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.deploymentId(deploymentId)
.singleResult();
logger.info("流程名称 [{}],流程ID [{}],流程KEY [{}]",processDefinition.getName(),processDefinition.getId(),processDefinition.getKey());
return processDefinition;
}
private static ProcessEngine getProcessEngine() {
ProcessEngineConfiguration cfg = ProcessEngineConfiguration.createStandaloneInMemProcessEngineConfiguration();
//构造流程引擎
ProcessEngine processEngine = cfg.buildProcessEngine();
String name = processEngine.getName();
String version = processEngine.VERSION;
logger.info("流程引擎名称 [{}],版本 [{}]",name,version);
return processEngine;
}
}
Activiti 框架原理
本文基于一个简单的Demo流程介绍了Activiti框架启动、部署、运行过程。
Demo准备
流程图文件:
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
<process id="hello" name="hello" isExecutable="true">
<!-- 流程开始节点 -->
<startEvent id="start" name="Start" ></startEvent>
<!-- serviceTask:执行me.likeyao.activiti.demo.HelloWorld的execute方法,打印hello world -->
<serviceTask id="helloworld" name="helloworld" activiti:class="me.likeyao.activiti.demo.HelloWorld"/>
<!-- 流程结束节点 -->
<endEvent id="end" name="End"></endEvent>
<!-- 流程迁移线:开始节点到serviceTask节点 -->
<sequenceFlow id="sid-1" sourceRef="start" targetRef="helloworld"></sequenceFlow>
<!-- 流程迁移线:serviceTask节点到结束节点 -->
<sequenceFlow id="sid-3" sourceRef="helloworld" targetRef="end"></sequenceFlow>
</process>
</definitions>
代码:
public class App {
public static void main(String[] args) {
//创建流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//部署流程图
processEngine.getRepositoryService().createDeployment().addClasspathResource("hello.bpmn20.xml").deploy();
//发起流程
processEngine.getRuntimeService().startProcessInstanceByKey("hello");
}
}
public class HelloWorld implements JavaDelegate{
public void execute(DelegateExecution execution) throws Exception {
System.out.println("Hello world!");
}
}
Demo实现的功能是发起一个流程,执行到流程的serviceTask节点时,打印Hello world!,然后流程结束。
源码版本:5.22.0
框架初始化
ProcessEngine类图
ProcessEngine
ProcessEngine是Activiti框架的门面,ProcessEngine本身不提供任何功能,通过getXXXService方法可以获取到对应的Service对象执行操作。Demo中涉及到的两个Service:
- RepositoryService:流程定义和流程部署相关功能。
- RuntimeService:流程实例相关功能(发起流程、获取流程实例变量)。
ProcessEngineConfiguration
ProcessEngineConfiguration负责Activiti框架的属性配置、初始化工作,初始化入口是buildProcessEngine方法,所有Activiti框架运行时需要用到的组件基本都在这里初始化:
public ProcessEngine buildProcessEngine() {
init();
return new ProcessEngineImpl(this);
}
protected void init() {
initConfigurators();
configuratorsBeforeInit();
initProcessDiagramGenerator();
initHistoryLevel();
initExpressionManager();
initDataSource();
initVariableTypes();
initBeans();
initFormEngines();
initFormTypes();
initScriptingEngines();
initClock();
initBusinessCalendarManager();
initCommandContextFactory();
initTransactionContextFactory();
initCommandExecutors();
initServices();
initIdGenerator();
initDeployers();
initJobHandlers();
initJobExecutor();
initAsyncExecutor();
initTransactionFactory();
initSqlSessionFactory();
initSessionFactories();
initJpa();
initDelegateInterceptor();
initEventHandlers();
initFailedJobCommandFactory();
initEventDispatcher();
initProcessValidator();
initDatabaseEventLogging();
configuratorsAfterInit();
}
这里有一个扩展点:ProcessEngineConfigurator。
public interface ProcessEngineConfigurator {
//组件初始化前
void beforeInit(ProcessEngineConfigurationImpl processEngineConfiguration);
//组件初始化后
void configure(ProcessEngineConfigurationImpl processEngineConfiguration);
//优先级
int getPriority();
}
在init初始化方法中,initConfigurators方法通过ServiceLoader加载ProcessEngineConfigurator。随后在configuratorsBeforeInit和configuratorsAfterInit方法中分别调用ProcessEngineConfigurator的beforeInit和configure方法,使用户可以在ProcessEngineConfiguration初始化前后编程式的修改属性,替换Activiti默认组件。
流程部署
流程部署实现的功能是将xml格式的流程图,转化为Activiti框架运行时依赖的流程定义对象。
RepositoryService
Demo中通过以下代码部署了一个流程:
processEngine.getRepositoryService().createDeployment().addClasspathResource("hello.bpmn20.xml").deploy();
createDeployment方法中创建了DeploymentBuilder对象,DeploymentBuilder对象负责读取指定路径的流程图xml文件的内容(byte数组),并缓存在DeploymentEntity对象中:
public DeploymentBuilder addInputStream(String resourceName, InputStream inputStream) {
...
byte[] bytes = IoUtil.readInputStream(inputStream, resourceName);
ResourceEntity resource = new ResourceEntity();
resource.setName(resourceName);
resource.setBytes(bytes);
deployment.addResource(resource);
return this;
}
最终DeploymentBuilder的deploy方法会调用RepositoryService的deploy方法,完成流程部署:
public Deployment deploy() {
return repositoryService.deploy(this);
}
CommandExecutor
在RepositoryService的deploy方法中,使用了CommandExecutor对象:
public Deployment deploy(DeploymentBuilderImpl deploymentBuilder) {
return commandExecutor.execute(new DeployCmd<Deployment>(deploymentBuilder));
}
在Activiti中,大部分操作都以Command模式实现,例如部署流程图的DeployCmd。CommandExecutor封装了一系列的CommandInterceptor,在内部形成CommandInterceptor链,在命令执行前后做了拦截。Activiti框架提供了一些
CommandInterceptor实现:
名称 | 作用 |
---|---|
CommandContextInterceptor | 用于生成命令执行的上下文(CommandContext)。 |
LogInterceptor | 开启日志Debug级别后,打印日志。 |
JtaTransactionInterceptor | 开启Jta事务 |
引入activiti-spring包,通过SpringTransactionInterceptor引入Spring的事务支持。
CommandExecutor在ProcessEngineConfigurationImpl的initCommandExecutors方法中初始化:
protected void initCommandExecutors() {
initDefaultCommandConfig();
initSchemaCommandConfig();
initCommandInvoker();
initCommandInterceptors();
initCommandExecutor();
}
可以设置ProcessEngineConfigurationImpl的customPreCommandInterceptors和customPostCommandInterceptors属性,添加自定义的CommandInterceptor:
protected void initCommandInterceptors() {
if (commandInterceptors==null) {
commandInterceptors = new ArrayList<CommandInterceptor>();
if (customPreCommandInterceptors!=null) {
commandInterceptors.addAll(customPreCommandInterceptors);
}
commandInterceptors.addAll(getDefaultCommandInterceptors());
if (customPostCommandInterceptors!=null) {
commandInterceptors.addAll(customPostCommandInterceptors);
}
commandInterceptors.add(commandInvoker);
}
}
这里的pre和post是指Activiti框架getDefaultCommandInterceptors()的前后。
CommandInvoker是CommandInterceptor链的最后一个对象,负责调用Command:
public class CommandInvoker extends AbstractCommandInterceptor {
@Override
public <T> T execute(CommandConfig config, Command<T> command) {
return command.execute(Context.getCommandContext());
}
}
CommandContext
CommandContext是Activit框架Command执行的上下文,主要包含各种SessionFactory:
sessionFactories = processEngineConfiguration.getSessionFactories();
SessionFactory负责生成Session,Session是Activiti操作持久化对象的统一接口:
名称 | 作用 |
---|---|
ProcessDefinitionEntityManager | 流程定义相关读写操作。 |
ExecutionEntityManager | 流程实例相关读写操作。 |
DefaultHistoryManager | 历史记录相关读写操作 |
CommandContext的生命周期
CommandConext在CommandContextInterceptor中创建,在finally代码块中销毁:
public <T> T execute(CommandConfig config, Command<T> command) {
//首先尝试从线程上下文的栈中获取CommandContext
CommandContext context = Context.getCommandContext();
boolean contextReused = false;
//什么时候创建新的CommandContext?
//1、CommandConfig中指定了不复用CommandContext
//2、当前线程上下文中不存在CommandConext
//3、当前线程上下文中的CommandConext已经抛出异常
if (!config.isContextReusePossible() || context == null || context.getException() != null) {
context = commandContextFactory.createCommandContext(command); }
else {
contextReused = true;
}
try {
//将前面获取到的CommandContext入栈
Context.setCommandContext(context);
Context.setProcessEngineConfiguration(processEngineConfiguration);
//执行下一个interceptor,在CommandInvoker中可以通过Context.getCommandContext()获取线程上下文中的CommandContext
return next.execute(config, command);
} catch (Exception e) {
//记录异常信息
context.exception(e);
} finally {
try {
//如果CommandContext不可复用,用完直接关闭
if (!contextReused) {
context.close();
}
} finally {
//出栈操作
Context.removeCommandContext();
Context.removeProcessEngineConfiguration();
Context.removeBpmnOverrideContext();
}
}
return null;
}
Activiti的框架可以在一个Command的执行过程中,调用另外一个Command,所以会出现是否需要复用CommandContext的选项,默认值为true。
流程的解析
在DeployCmd中,首先调用DeploymentEntityManager持久化存储DeploymentEntity对象:
commandContext.getDeploymentEntityManager().insertDeployment(deployment);
然后调用DeploymentManager部署流程(流程解析):
commandContext.getProcessEngineConfiguration().getDeploymentManager().deploy(deployment, deploymentSettings);
DeploymentEntityManager
DeploymentEntityManager的deploy方法中循环调用Deployer对象的deploy方法,Activiti默认的Deployer是BpmnDeployer。
另外DeploymentEntityManager中还缓存了解析好的流程定义对象和Bpmn模型对象。
Activiti持久化的是流程图xml文件,每次系统重新启动都要执行一次“deploy”操作,生成ProcessDefinitionEntity对象。
BpmnDeployer
BpmnDeployer的deploy方法中包含几个操作(代码缩略版):
public void deploy(DeploymentEntity deployment, Map<String, Object> deploymentSettings) {
...
BpmnParse bpmnParse = bpmnParser.createParse().sourceInputStream(inputStream).setSourceSystemId(resourceName).deployment(deployment).name(resourceName);
bpmnParse.execute();
for (ProcessDefinitionEntity processDefinition: bpmnParse.getProcessDefinitions()) {
if (deployment.isNew()) {
ProcessDefinitionEntity latestProcessDefinition = ...
if (latestProcessDefinition != null) {
processDefinitionVersion = latestProcessDefinition.getVersion() + 1;
}else{
processDefinitionVersion = 1;
}
processDefinition.setId(idGenerator.getNextId());
dbSqlSession.insert(processDefinition);
}
...
}
}
- 通过BpmnParser对象创建BpmnParse。
- 调用BpmnParse的execute方法,将inputStream中的流程图转化为ProcessDefinitionEntity。
- 持久化ProcessDefinitionEntity对象。
BpmnParse
在BpmnParse的execute中完成了xml文件到ProcessDefinitionEntity对象的转化:
public BpmnParse execute() {
//xml->bpmnModel
bpmnModel = converter.convertToBpmnModel(streamSource, validateSchema, enableSafeBpmnXml, encoding);
//bpmnModel-> ProcessDefinitionEntity
transformProcessDefinitions();
}
protected void transformProcessDefinitions() {
for (Process process : bpmnModel.getProcesses()) {
bpmnParserHandlers.parseElement(this, process);
}
}
在流程定义解析过程中,会涉及到两套模型:
- Bpmn模型(由BpmnXMLConverter完成转换)
- PVM模型(由BpmnParseHandlers完成转换)
Bpmn模型
PVM模型
Bpmn模型更偏向于xml节点的描述,PVM模型是运行时模型。Bpmn模型中的ServiceTask、StartEvent等会统一映射转换为PVM的ActivityImpl对象,ServiceTask和StartEvent等节点行为上的差别,体现在ActivityImpl对象持有的不同的ActivityBehavior上。
运行流程
创建流程实例
在demo中通过RuntimeService发起流程实例:
processEngine.getRuntimeService().startProcessInstanceByKey("hello");
在startProcessInstanceByKey方法中执行StartProcessInstanceCmd命令:
public class StartProcessInstanceCmd<T> implements Command<ProcessInstance>, Serializable {
...
public ProcessInstance execute(CommandContext commandContext) {
//获取流程定义
ProcessDefinitionEntity processDefinition = ...
//创建流程实例
ExecutionEntity processInstance = processDefinition.createProcessInstance(businessKey);
//开始流程
processInstance.start();
return processInstance;
}
...
}
在StartProcessInstanceCmd方中通过流程定义ProcessDefinitionEntity创建了流程实例 ExecutionEntity:
ExecutionEntity实现了一些重要接口:
- PVM相关的接口,赋予了ExecutionEntity流程驱动的能力,例如single、start方法。
- 实现VariableScope接口让ExecutionEntity可以持久上下文变量。
- ProcessInstance接口暴露了ExecutionEntity关联的ProcessDefinitionEntity的信息。
- PersistentObject接口代表ExecutionEntity对象是需要持久化。
在ExecutionEntity中维护类一个属性:activity。activity属性代表当前执行到哪个节点,在创建ExecutionEntity过程中会设置activity,使流程从某一个节点开始,默认是开始节点。
最后StartProcessInstanceCmd还调用ExecutionEntity的start方法开始驱动流程:
public void start() {
performOperation(AtomicOperation.PROCESS_START);
}
驱动流程
Activiti框架的流程运行于PVM模型之上,在流程运行时主要涉及到PVM中几个对象:ActivityImpl、TransitionImpl和ActivityBehavior。
- ActivityImpl:ActivityImpl是流程节点的抽象,ActivityImpl维护流程图中节点的连线,包括有哪些进线,有哪些出线。另外还包含节点同步/异步执行等信息。
- TransitionImpl:TransitionImpl包含source和target两个属性,连接了两个流程节点。
- ActivityBehavior:每一个ActivityImpl对象都拥有一个ActivityBehavior对象,ActivityBehavior代表节点的行为。
ActivityImpl、TransitionImpl和ActivityBehavior只是描述了流程的节点、迁移线和节点行为,真正要让ExecutionEntity流转起来,还需要AtomicOperation的驱动:
AtomicOperation PROCESS_START = new AtomicOperationProcessStart();
AtomicOperation PROCESS_START_INITIAL = new AtomicOperationProcessStartInitial();
AtomicOperation PROCESS_END = new AtomicOperationProcessEnd();
AtomicOperation ACTIVITY_START = new AtomicOperationActivityStart();
AtomicOperation ACTIVITY_EXECUTE = new AtomicOperationActivityExecute();
AtomicOperation ACTIVITY_END = new AtomicOperationActivityEnd();
AtomicOperation TRANSITION_NOTIFY_LISTENER_END = new AtomicOperationTransitionNotifyListenerEnd();
AtomicOperation TRANSITION_DESTROY_SCOPE = new AtomicOperationTransitionDestroyScope();
AtomicOperation TRANSITION_NOTIFY_LISTENER_TAKE = new AtomicOperationTransitionNotifyListenerTake();
AtomicOperation TRANSITION_CREATE_SCOPE = new AtomicOperationTransitionCreateScope();
AtomicOperation TRANSITION_NOTIFY_LISTENER_START = new AtomicOperationTransitionNotifyListenerStart();
AtomicOperation DELETE_CASCADE = new AtomicOperationDeleteCascade();
AtomicOperation DELETE_CASCADE_FIRE_ACTIVITY_END = new AtomicOperationDeleteCascadeFireActivityEnd();
在ExecutionEntity的start方法中,调用了PROCESS_START,PROCESS_START做了几件事:
- 获取流程定义级别定义的监听start事件的ExecutionListener,调用notify方法。
- 如果开启了事件功能,发布ActivitiEntityWithVariablesEvent和ActivitiProcessStartedEvent。
- 调用PROCESS_START_INITIAL。
PROCESS_START_INITIAL也实现了类似的功能:
- 获取初始节点上定义的监听start事件的ExecutionListener,调用notify方法。
- 调用ACTIVITY_EXECUTE。
在Demo流程执行中涉及的AtomicOperation的链路主要包括:
- ACTIVITY_EXECUTE:调用当前activity的behavior。
- TRANSITION_NOTIFY_LISTENER_END:某个activity节点执行完毕,调用节点上声明的监听end事件的ExecutionListener。
- TRANSITION_NOTIFY_LISTENER_TAKE:触发线上的ExecutionListener。
- TRANSITION_NOTIFY_LISTENER_START:某个activity节点即将开始执行,调用节点上的监听start事件的ExecutionListener。
以Demo流程中的ServiceTask节点helloworld为例,在执行ACTIVITY_EXECUTE时,会获取activity关联的behavior:
public class AtomicOperationActivityExecute implements AtomicOperation {
public void execute(InterpretableExecution execution) {
...
ActivityImpl activity = (ActivityImpl) execution.getActivity();
ActivityBehavior activityBehavior = activity.getActivityBehavior();
activityBehavior.execute(execution);
...
}
}
ServiceTask解析时关联的是ServiceTaskJavaDelegateActivityBehavior,execution方法:
public void execute(ActivityExecution execution) throws Exception {
//execution中调用了me.likeyao.activiti.demo.HelloWorld
execute((DelegateExecution) execution);
//离开当前节点
leave(execution);
}
在leave方法中调用了:
bpmnActivityBehavior.performDefaultOutgoingBehavior(execution);
performDefaultOutgoingBehavior方法会在当前activity的 出线中选择一条,使流程流向下一个节点。在Demo中只有一条线存在:
protected void performOutgoingBehavior(ActivityExecution execution,
boolean checkConditions, boolean throwExceptionIfExecutionStuck, List<ActivityExecution> reusableExecutions) {
if (transitionsToTake.size() == 1) {
execution.take(transitionsToTake.get(0));
}
}
最终take方法会将流程驱动权交还到AtomicOperation中:
public class ExecutionEntity{
...
public void take(PvmTransition transition, boolean fireActivityCompletionEvent) {
...
setActivity((ActivityImpl)transition.getSource());
setTransition((TransitionImpl) transition);
performOperation(AtomicOperation.TRANSITION_NOTIFY_LISTENER_END);
...
}
...
}
AtomicOperation的问题
按照AtomicOperation的驱动模式,只有当遇到UserTask等需要等待single信号的节点,调用才会返回。这意味着当调用RuntimeService启动一个流程实例时,要一直等到流程运行到一个UserTask节点调用才会返回,如果流程比较长耗时非常验证。
另一个问题是当流程图比较复杂,ExecutionListener数量比较多时,AtomicOperation之间的互相调用会导致调用栈非常深。
AtomicOperation驱动模式与ExecutionEntity、Behavior等绑定的比较紧密,暂时没有特别好的办法替换掉。
小结
本文主要介绍了Activiti框架的启动、部署、运行的主链路,并没有深入BPMN规范和Activit功能的具体实现,后续打算根据Activiti的用户手册,详细分析每个功能的使用和实现。