赞
踩
近期因项目需求学习了一下Activiti工作流引擎的相关内容,便于后续复现与归档,希望今年接下来的项目可以进展顺利。这里整理一下学习过程中的相关材料,并结合了自己的项目实践经验教您快速上手Activiti工作流引擎。
工作流(Workflow)就是通过计算机对业务流程自动化执行管理,主要目的在于使在多个参与者之间按照某种预定义的规则自动进行传递文档、信息或任务的过程,从而实现某个预期的业务目标,或者促使此目标的实现。
Alfresco软件在2010年5月17日宣布Activiti业务流程管理(BPM)开源项目的正式启动,其首席架构师由业务流程管理BPM的专家 Tom Baeyens担任。
Activiti是一个工作流引擎,可以将业务系统中复杂的业务流程抽取出来,使用专门的建模语言BPMN2.0进行定义,业务流程按照预先定义的流程进行执行,实现了系统的流程由activiti进行管理,减少业务系统由于流程变更进行系统升级改造的工作量,从而提高系统的健壮性,同时也减少了系统开发维护成本。
BPM(Business Process Management),即业务流程管理,是一种规范化的构造端到端的业务流程,以持续的提高组织业务效率。常见商业管理教育如EMBA、MBA等均将BPM包含在内。
BPM软件就是根据企业中业务环境的变化,推进人与人之间、人与系统之间以及系统与系统之间的整合及调整的经营方法与解决方案的IT工具。通过BPM软件对企业内部及外部的业务流程的整个生命周期进行建模、自动化、管理监控和优化,使企业成本降低,利润得以大幅提升。
BPM软件在企业中应用领域广泛,凡是有业务流程的地方都可以BPM软件进行管理,比如企业人事办公管理、采购流程管理、公文审批流程管理、财务管理等。
BPMN(Business Process Model AndNotation)- 业务流程模型和符号 是由BPMI(BusinessProcess Management Initiative)开发的一套标准的业务流程建模符号,使用BPMN提供的符号可以创建业务流程。 2004年5月发布了BPMN1.0规范.BPMI于2005年9月并入OMG(The Object Management Group对象管理组织)组织。OMG于2011年1月发布BPMN2.0的最终版本。
BPMN 是目前被各 BPM 厂商广泛接受的 BPM 标准。Activiti 就是使用 BPMN 2.0 进行流程建模、流程执行管理,它包括很多的建模符号,比如:
BPMN2.0的基本符号主要包含:
(1)事件 Event
(2)活动 Activity
活动是工作或任务的一个通用术语。一个活动可以是一个任务,还可以是一个当前流程的子处理流程; 其次还可以为活动指定不同的类型。常见活动如下:
(3)网关 GateWay
网关用来处理决策,有几种常用网关需要了解:
排他网关 (x)
——只有一条路径会被选择。流程执行到该网关时,按照输出流的顺序逐个计算,当条件的计算结果为true时,继续执行当前网关的输出流;
如果多条线路计算结果都是 true,则会执行第一个值为 true 的线路。如果所有网关计算结果没有true,则引擎会抛出异常。
排他网关需要和条件顺序流结合使用,default 属性指定默认顺序流,当所有的条件不满足时会执行默认顺序流。
并行网关 (+)
——所有路径会被同时选择
拆分 —— 并行执行所有输出顺序流,为每一条顺序流创建一个并行执行线路。
合并 —— 所有从并行网关拆分并执行完成的线路均在此等候,直到所有的线路都执行完成才继续向下执行。
包容网关 (+)
—— 可以同时执行多条线路,也可以在网关上设置条件
拆分 —— 计算每条线路上的表达式,当表达式计算结果为true时,创建一个并行线路并继续执行
合并 —— 所有从并行网关拆分并执行完成的线路均在此等候,直到所有的线路都执行完成才继续向下执行。
事件网关 (+)
—— 专门为中间捕获事件设置的,允许设置多个输出流指向多个不同的中间捕获事件。当流程执行到事件网关后,流程处于等待状态,需要等待抛出事件才能将等待状态转换为活动状态。
(4)流向 Flow
流是连接两个流程节点的连线。常见的流向包含以下几种:
上面了解Activiti基础概念之后,实际应用中需要调用Activiti的工具类,下面来了解Activiti的类关系,如图所示:
通过ProcessEngineConfiguration
可以创建工作流引擎ProceccEngine
,常用的两种方法如下:
(1)StandaloneProcessEngineConfiguration
使用StandaloneProcessEngineConfiguration来创建ProcessEngine,Activiti会自己处理事务,配置方式通常是在activiti.cfg.xml
中定义一个id为processEngineConfiguration 的bean,后文实践过程中我使用的就是这种方式:
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="jdbc:mysql://localhost:3306/activiti?useSSL=false"></property>
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="username" value="root"></property>
<property name="password" value="19940403"></property>
</bean>
<bean id="processEngineConfiguration"
class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<property name="dataSource" ref="dataSource"></property>
</bean>
(2)SpringProcessEngineConfiguration
通过org.activiti.spring.SpringProcessEngineConfiguration
与Spring整合,创建spring与activiti的整合配置文件activity-spring.cfg.xml
(名称可修改)
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd "> <!-- 工作流引擎配置bean --> <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration"> <!-- 数据源 --> <property name="dataSource" ref="dataSource" /> <!-- 使用spring事务管理器 --> <property name="transactionManager" ref="transactionManager" /> <!-- 数据库策略 --> <property name="databaseSchemaUpdate" value="drop-create" /> <!-- activiti的定时任务关闭 --> <property name="jobExecutorActivate" value="false" /> </bean> <!-- 流程引擎 --> <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean"> <property name="processEngineConfiguration" ref="processEngineConfiguration" /> </bean> <!-- 资源服务service --> <bean id="repositoryService" factory-bean="processEngine" factory-method="getRepositoryService" /> <!-- 流程运行service --> <bean id="runtimeService" factory-bean="processEngine" factory-method="getRuntimeService" /> <!-- 任务管理service --> <bean id="taskService" factory-bean="processEngine" factory-method="getTaskService" /> <!-- 历史管理service --> <bean id="historyService" factory-bean="processEngine" factory-method="getHistoryService" /> <!-- 用户管理service --> <bean id="identityService" factory-bean="processEngine" factory-method="getIdentityService" /> <!-- 引擎管理service --> <bean id="managementService" factory-bean="processEngine" factory-method="getManagementService" /> <!-- 数据源 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/activiti" /> <property name="username" value="root" /> <property name="password" value="19940403" /> <property name="maxActive" value="3" /> <property name="maxIdle" value="1" /> </bean> <!-- 事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 通知 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes></tx:attributes> <!-- 传播行为 --> <tx:method name="save*" propagation="REQUIRED" /> <tx:method name="insert*" propagation="REQUIRED" /> <tx:method name="delete*" propagation="REQUIRED" /> <tx:method name="update*" propagation="REQUIRED" /> <tx:method name="find*" propagation="SUPPORTS" read-only="true" /> <tx:method name="get*" propagation="SUPPORTS" read-only="true" /> </tx:attributes> </tx:advice> <!-- 切面,根据具体项目修改切点配置 --> <aop:config proxy-target-class="true"> <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.itheima.ihrm.service.impl.*.(..))"* /> </aop:config> </beans>
配置完成后可以创建processEngineConfiguration实例,有两种方式:
ProcessEngineConfiguration configuration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml")
上边的代码要求activiti.cfg.xml中必须有一个processEngineConfiguration的bean
也可以使用下边的方法,更改bean 的名字:
ProcessEngineConfiguration.createProcessEngineConfigurationFromResource(String resource, String beanName);
工作流引擎相当于一个门面接口,使用了外观设计模式。通过ProcessEngineConfiguration
创建processEngine
,把service
服务的七大接口在本类中都实例化了,并单独为每一个服务接口提供了一个实例化的方法,通过它可以获取所有的service
服务接口对象。
将activiti.cfg.xml
文件名及路径固定,且activiti.cfg.xml文件中有 processEngineConfiguration的配置, 可以使用如下代码创建processEngine:
//直接使用工具类 ProcessEngines,使用classpath下的activiti.cfg.xml中的配置创建processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
System.out.println(processEngine);
或者使用一般创建方式:
//先构建ProcessEngineConfiguration
ProcessEngineConfiguration configuration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
//通过ProcessEngineConfiguration创建ProcessEngine,此时会创建数据库
ProcessEngine processEngine = configuration.buildProcessEngine();
Service是工作流引擎提供用于进行工作流部署、执行、管理的服务接口,通过使用这些接口可以操作服务对应的数据库表,总览如下:
service名称 | service作用 |
---|---|
RepositoryService | activiti的资源管理类,提供了管理和控制流程发布包和流程定义的操作。除了部署流程定义以外还可以查询引擎中的发布包和流程定义,暂停或激活发布包,对应全部和特定流程定义,获得多种资源,像是包含在发布包里的文件, 或引擎自动生成的流程图。 |
RuntimeService | Activiti的流程运行管理类,可以从这个服务类中获取很多关于流程执行相关的信息。 |
TaskService | Activiti的任务管理类,可以从这个类中获取任务的信息。 |
HistoryService | Activiti的历史管理类,可以查询历史信息,执行流程时,引擎会保存很多数据(根据配置),比如流程实例启动时间,任务的参与者, 完成任务的时间,每个流程实例的执行路径,等等。 这个服务主要通过查询功能来获得这些数据。 |
ManagerService | Activiti的引擎管理类,提供了对 Activiti 流程引擎的管理和维护功能,这些功能不在工作流驱动的应用程序中使用,主要用于 Activiti 系统的日常维护。 |
通过ProcessEngine
创建Service
,方式如下:
RuntimeService runtimeService = processEngine.getRuntimeService();
RepositoryService repositoryService = processEngine.getRepositoryService();
TaskService taskService = processEngine.getTaskService();
目前的项目开发过程中还未涉及到Activiti源码底层的阅读与学习工作,后续如有需要会对这里进一步更新相关内容。
开发环境:
Jdk1.8或以上版本
,Mysql 5及以上的版本
,IDEA
Activiti下载地址:http://activiti.org/download.html
GitHub:https://github.com/Activiti/Activiti 这里下载的是activity-dev。
p.s.这里源码看着太复杂了,后续未使用,网上看了一些教程从零开始搭了一个简易demo版,后文对其进行详细介绍,需要下载的请自行跳转链接:
在IDEA的File菜单
中找到子菜单”Settings”
,选择左侧的“plugins”
菜单,如下图所示:
网上都说可以搜索到actiBPM插件,但我这里没有找到,于是下载了Activiti BPMN Visualizer
,应该是同样功能类型的插件,点击Install安装。
之后就可以使用这款插件进行.bpmn文件的可视化与设计,例如:
Activiti 在运行时需要数据库的支持,支持的数据库有:H2, Mysql, Oracle, Postgres, Mssql, Db2,我这里使用Mysql 8.2.0
。Activiti 使用25张表,把流程定义节点内容读取到数据库表中,以供后续使用。
方式1、使用activiti框架提供的建表语句
在database目录下找到相应数据库的建表语句,执行即可。
方式2、使用activiti框架的自动建表功能(推荐)
完整的依赖内容如下:
<?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>groupId</groupId> <artifactId>ActivitiDemo</artifactId> <version>1.0-SNAPSHOT</version> <properties> <slf4j.version>1.6.6</slf4j.version> <log4j.version>1.2.12</log4j.version> <activiti.version>6.0.0</activiti.version> </properties> <dependencies> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-spring-boot-starter-basic</artifactId> <version>${activiti.version}</version> </dependency> <!-- mysql驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.28</version> </dependency> <!-- mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.5</version> </dependency> <!-- 链接池 --> <!--<dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.4</version> </dependency>--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.2</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies> </project>
activiti.cfg.xml
,注意:默认方式目录和文件名不能修改,因为Activiti的源码中已经设置,到固定的目录读取固定文件名的文件。在 activiti.cfg.xml 中进行配置,bean的名字叫processEngineConfiguration
,名字不可修改,用来创建 ProcessEngine
,在创建 ProcessEngine
时会执行数据库的操作:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/contex http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="url" value="jdbc:mysql://localhost:3306/activiti?useSSL=false"></property> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="username" value="root"></property> <property name="password" value="19940403"></property> </bean> <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration"> <property name="dataSource" ref="dataSource"></property> </bean> </beans>
创建一个测试类,调用activiti的工具类,生成acitivti需要的数据库表。
代码如下:
package Activiti; import org.activiti.engine.ProcessEngine; import org.activiti.engine.ProcessEngineConfiguration; import org.junit.Test; public class CreateTablesDemo { /** * 生成 activiti的数据库表 */ @Test public void testCreateDbTable() { ProcessEngineConfiguration processEngineConfiguration = ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration(); processEngineConfiguration.setJdbcDriver("com.mysql.jdbc.Driver"); processEngineConfiguration.setJdbcUrl("jdbc:mysql://localhost:3306/activiti"); processEngineConfiguration.setJdbcUsername("root"); processEngineConfiguration.setJdbcPassword("19940403"); /** * true:如果不存在表就创建,存在就直接使用。(默认使用该策略,真实项目不推荐使用,如果目前使用较低版本,贸然引入了更高的版本后, * 上线之后,就会导致activiti数据库环境升级版本,可能造成线上部分表无法使用。顺便一提:activiti的版本只能向上升级,无法降级处理) * false:流程引擎启动的时候,不会创建表,如果不存在就报错,存在就直接使用。(建议使用) * create-drop:每次都是创建表,然后删除表,需要手动调用引擎类的close方法(更不建议使用) */ processEngineConfiguration.setDatabaseSchemaUpdate("true"); // processEngineConfiguration.setDatabaseSchemaUpdate("false"); // processEngineConfiguration.setDatabaseSchemaUpdate("create-drop"); ProcessEngine buildProcessEngine = processEngineConfiguration.buildProcessEngine(); // buildProcessEngine.close(); System.out.println(buildProcessEngine); } }
这里记录遇到的一些问题:
(1) java.sql.SQLException: Unknown system variable 'query_cache_size'
错误原因在于数据库驱动程序与数据库版本不对应,将pom.xml中的驱动版本替换成自己版本驱动,重启即可。
(2) Cause: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
错误原因在于数据库连接参数不正确,确认账户密码是否正确,还有URL中的IP是否正确,端口是否正确,端口是否放行。
(3)Host XXX is not allowed to connect to this MySQL Server
错误原因在于Mysql配置了不支持远程连接,在cmd命令行窗口中,输入mysql -u root -p
,输入密码后连接数据库,依次执行如下命令:
use mysql;
select host from user where user = 'root';
将host设置为通配符%,执行如下代码:
update user set host = '%' where user = 'root';
执行flush privileges
使配置立即生效。
执行完成后查看数据库, 创建了 25 张表,结果如下:
看到刚才创建的表发现Activiti 的表都以 ACT_
开头。
第二部分是表示表的用途的两个字母标识,用途也和服务的 API 对应:
ACT_RE :'RE’表示 repository
。
这个前缀的表包含了流程定义和流程静态资源 (图片,规则,等等)。
ACT_RU:'RU’表示 runtime
。
这些运行时的表,包含流程实例,任务,变量,异步任务,等运行中的数据。 Activiti 只在流程实例执行过程中保存这些数据, 在流程结束时就会删除这些记录。 这样运行时表可以一直很小速度很快。
ACT_HI:'HI’表示 history
。
这些表包含历史数据,比如历史流程实例, 变量,任务等等。
ACT_GE : GE 表示 general
。
通用数据, 用于不同场景下 。
一般数据 (ACT_GE_
) 2张:
表名 | 解释 |
---|---|
ACT_GE_BYTEARRAY | 二进制数据表,存储通用的流程定义和流程资源。 |
ACT_GE_PROPERTY | 系统相关属性,属性数据表存储整个流程引擎级别的数据,初始化表结构时,会默认插入三条记录。 |
流程历史记录 (ACT_HI_
)8张:
表名 | 解释 |
---|---|
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_EVT_LOG | 事件日志表 |
用户用户组表 (ACT_ID_
) 4张:
表名 | 解释 |
---|---|
ACT_ID_GROUP | 用户组信息表 |
ACT_ID_INFO | 用户扩展信息表 |
ACT_ID_MEMBERSHIP | 用户与用户组对应信息表 |
ACT_ID_USER | 用户信息表 |
流程定义表 (ACT_RE_
) 4张:
表名 | 解释 |
---|---|
ACT_RE_DEPLOYMENT | 部署信息表 |
ACT_RE_MODEL | 流程设计模型部署表 |
ACT_RE_PROCDEF | 流程定义数据表 |
ACT_PROCDEF_INFO | 流程定义的动态变更信息表 |
运行实例表 (ACT_RU_
) 6张:
表名 | 解释 |
---|---|
ACT_RU_EVENT_SUBSCR | 运行时事件 throwEvent、catchEvent 时间监听信息表 |
ACT_RU_EXECUTION | 运行时流程执行实例 |
ACT_RU_IDENTITYLINK | 运行时流程人员表,主要存储任务节点与参与者的相关信息 |
ACT_RU_JOB | 运行时定时任务数据表 |
ACT_RU_TASK | 运行时任务节点表 |
ACT_RU_VARIABLE | 运行时流程变量数据表 |
其它2张:
表名 | 解释 |
---|---|
ACT_EVT_LOG | 事件日志 |
ACT_PROCDEF_INFO | 流程定义的动态变更信息 |
流程定义是线下按照BPMN2.0标准
去描述业务流程,如果B/S架构开发可在前端页面对业务流程进行建模,并将生成的文件上传至服务端进行处理,如果纯服务端开发通常使用IDEA中的Designer设计器
插件绘制流程,并会生成两个文件:.bpmn
和.png
。
BPMN 2.0的根节点是definitions节点
。这个元素中,可以定义多个流程定义(建议每个文件只包含一个流程定义, 可以简化开发过程中的维护难度)。 注意definitions元素最少也要包含xmlns
和 targetNamespace
的声明。 targetNamespace可以是任意值,它用来对流程实例进行分类。
这里我是用设计器绘制了一个简单的业务流程Demo,如图所示:
之后鼠标右键选择Save to PNG
,保存.png
文件至resources
目录下。
将上面在设计器中定义的流程部署到activiti数据库中,就是流程定义部署,有两种部署方式:
(1)单个文件部署方式
分别将bpmn文件和png图片文件部署,执行代码如下:
package Activiti; import org.activiti.engine.ProcessEngine; import org.activiti.engine.ProcessEngines; import org.activiti.engine.RepositoryService; import org.activiti.engine.repository.Deployment; import org.junit.Test; public class ProcessDesignDemo { /** * 部署流程定义 */ @Test public void testDeployment(){ // 1、创建ProcessEngine ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); // 2、得到RepositoryService实例 RepositoryService repositoryService = processEngine.getRepositoryService(); // 3、使用RepositoryService进行部署 Deployment deployment = repositoryService.createDeployment() .addClasspathResource("Demo.bpmn20.xml") // 添加bpmn资源 .addClasspathResource("Demo.png") // 添加png资源 .name("计算任务流程") .deploy(); // 4、输出部署信息 System.out.println("流程部署id:" + deployment.getId()); System.out.println("流程部署名称:" + deployment.getName()); } }
执行此操作后activiti会将指定的bpm文件和图片文件保存在activiti数据库。
(2)压缩包部署方式
将两个资源文件压缩成zip包,执行代码如下:
@Test public void deployProcessByZip() { // 定义zip输入流 InputStream inputStream = this .getClass() .getClassLoader() .getResourceAsStream( "Demo.zip"); ZipInputStream zipInputStream = new ZipInputStream(inputStream); ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); // 获取repositoryService RepositoryService repositoryService = processEngine.getRepositoryService(); // 流程部署 Deployment deployment = repositoryService.createDeployment() .addZipInputStream(zipInputStream) .deploy(); System.out.println("流程部署id:" + deployment.getId()); System.out.println("流程部署名称:" + deployment.getName()); }
流程定义部署后操作Activiti的3张表如下:
act_re_deployment 流程定义部署表,每部署一次增加一条记录
act_re_procdef 流程定义表,部署每个新的流程定义都会在这张表中增加一条记录
act_ge_bytearray 流程资源表
SELECT * FROM act_re_deployment #流程定义部署表,记录流程部署信息
SELECT * FROM act_re_procdef #流程定义表,记录流程定义信息
注意 KEY
这个字段是用来唯一识别不同流程的关键字
SELECT * FROM act_ge_bytearray #资源表
act_re_deployment
和act_re_procdef
一对多关系,一次部署在流程部署表生成一条记录,但一次部署可以部署多个流程定义,每个流程定义在流程定义表生成一条记录。每一个流程定义在act_ge_bytearray
会存在两个资源记录,bpmn和png。
建议一次部署一个流程,这样部署表和流程定义表是一对一关系,方便读取流程部署及流程定义信息。
流程定义部署后就可以通过工作流管理业务流程了,针对该流程,启动一个流程实例表示发起一次流程业务,这就相当于java类与java对象的关系,类定义好后需要new创建一个对象使用,当然可以new多个对象。可以执行代码如下:
package Activiti; import org.activiti.engine.ProcessEngine; import org.activiti.engine.ProcessEngines; import org.activiti.engine.RuntimeService; import org.activiti.engine.runtime.ProcessInstance; import org.junit.Test; public class ProcessInstanceDemo { /** * 启动流程实例 */ @Test public void testStartProcess(){ // 1、创建ProcessEngine ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); // 2、获取RunTimeService RuntimeService runtimeService = processEngine.getRuntimeService(); // 3、根据流程定义Id启动流程 ProcessInstance processInstance = runtimeService .startProcessInstanceByKey("Demo"); // 输出内容 System.out.println("流程定义id:" + processInstance.getProcessDefinitionId()); System.out.println("流程实例id:" + processInstance.getId()); System.out.println("当前活动Id:" + processInstance.getActivityId()); } }
输出内容如下:
操作的数据库表如下:
act_hi_actinst
: 流程实例执行历史
act_hi_identitylink
:流程的参与用户历史信息
act_hi_procinst
: 流程实例历史信息
act_hi_taskinst
: 流程任务历史信息
act_ru_execution
流程执行信息
act_ru_identitylink
:流程的参与用户信息
act_ru_task
:任务信息
流程启动后,任务的负责人就可以查询自己当前需要处理的任务,查询出来的任务都是该用户的待办任务。
package Activiti; import org.activiti.engine.ProcessEngine; import org.activiti.engine.ProcessEngines; import org.activiti.engine.RuntimeService; import org.activiti.engine.TaskService; import org.activiti.engine.runtime.ProcessInstance; import org.activiti.engine.task.Task; import org.junit.Test; import java.util.List; public class TaskInstanceDemo { /** * 查询当前个人待执行的任务 */ @Test public void testFindPersonalTaskList() { // 任务负责人 String assignee = "负责人1"; ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); // 创建TaskService TaskService taskService = processEngine.getTaskService(); // 根据流程key 和 任务负责人 查询任务 List<Task> list = taskService.createTaskQuery() .processDefinitionKey("Demo") //流程Key .taskAssignee(assignee)//只查询该任务负责人的任务 .list(); for (Task task : list) { System.out.println("流程实例id:" + task.getProcessInstanceId()); System.out.println("任务id:" + task.getId()); System.out.println("任务负责人:" + task.getAssignee()); System.out.println("任务名称:" + task.getName()); } } }
输出结果如下:
任务负责人查询待办任务,选择任务进行处理,完成任务。
/** * 完成个人任务 */ @Test public void completTask(){ // 获取引擎 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); // 获取taskService TaskService taskService = processEngine.getTaskService(); // 根据流程key 和 任务的负责人 查询任务 // 返回一个任务对象 Task task = taskService.createTaskQuery() .processDefinitionKey("Demo") //流程Key .taskAssignee("负责人1") //要查询的负责人 .singleResult(); // 完成任务,参数:任务id taskService.complete(task.getId()); }
此时act_hi_taskinst
任务实例表单中会新增一条待完成的任务,同时上一条任务的记录会添加结束时间的时间信息。
三个负责人都执行任务结束后,观察库表会发现act_hi_procinst
会添加流程结束时间的时间信息,act_hi_taskinst
中该流程的所有任务都有完整记录,同时act_ru_task
与act_ru_execution
中的运行实例记录会清空。
查询流程相关信息,包含流程定义,流程部署,流程定义版本,具体代码如下:
/** * 查询流程定义 */ @Test public void queryProcessDefinition(){ // 获取引擎 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); // repositoryService RepositoryService repositoryService = processEngine.getRepositoryService(); // 得到ProcessDefinitionQuery 对象 ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery(); // 查询出当前所有的流程定义 // 条件:processDefinitionKey // orderByProcessDefinitionVersion 按照版本排序 // desc倒叙 // list 返回集合 List<ProcessDefinition> definitionList = processDefinitionQuery.processDefinitionKey("Demo") .orderByProcessDefinitionVersion() .desc() .list(); // 输出流程定义信息 for (ProcessDefinition processDefinition : definitionList) { System.out.println("流程定义 id="+processDefinition.getId()); System.out.println("流程定义 name="+processDefinition.getName()); System.out.println("流程定义 key="+processDefinition.getKey()); System.out.println("流程定义 Version="+processDefinition.getVersion()); System.out.println("流程部署ID ="+processDefinition.getDeploymentId()); } }
输出结果如图,之所以部署了这么多次是前面测试压缩文件部署的时候多测了几回,嘿嘿
对已定义的流程进行删除,执行代码如下:
/** * 删除流程定义 */ @Test public void deleteDeployment() { // 流程部署id String deploymentId = "7501"; ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); // 通过流程引擎获取repositoryService RepositoryService repositoryService = processEngine .getRepositoryService(); //删除流程定义,如果该流程定义已有流程实例启动则删除时出错 repositoryService.deleteDeployment(deploymentId); //设置true 级联删除流程定义,即使该流程有流程实例启动也可以删除,设置为false非级别删除方式,如果流程 //repositoryService.deleteDeployment(deploymentId, true); }
这里有几点注意事项:
1)使用repositoryService
删除流程定义,历史表信息不会被删除
2)如果该流程定义下没有正在运行的流程,则可以用普通删除,如果该流程定义下存在已经运行的流程,使用普通删除报错,可用级联删除方法将流程及相关记录全部删除:
先删除没有完成流程节点,最后就可以完全删除流程定义信息。
项目开发中级联删除操作一般只开放给超级管理员使用。
上文流程定义部署时资源文件已经上传到数据库,如果其他用户想要查看这些资源文件,可以从数据库中把资源文件下载到本地。
使用Activiti的api来实现,首先引入commons-io依赖包:
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
通过流程定义对象获取流程定义资源,获取bpmn和png
/** * 流程资源下载 */ @Test public void queryBpmnFile() throws IOException { // 1、得到引擎 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); // 2、获取repositoryService RepositoryService repositoryService = processEngine.getRepositoryService(); // 3、得到查询器:ProcessDefinitionQuery,设置查询条件,得到想要的流程定义 ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery() .processDefinitionKey("Demo") .singleResult(); // 4、通过流程定义信息,得到部署ID String deploymentId = processDefinition.getDeploymentId(); // 5、通过repositoryService的方法,实现读取图片信息和bpmn信息 // png图片的流 InputStream pngInput = repositoryService.getResourceAsStream(deploymentId, processDefinition.getDiagramResourceName()); // bpmn文件的流 InputStream bpmnInput = repositoryService.getResourceAsStream(deploymentId, processDefinition.getResourceName()); // 6、构造OutputStream流 File file_png = new File("D:\\Demo.png"); File file_bpmn = new File("D:\\Demo.bpmn"); FileOutputStream bpmnOut = new FileOutputStream(file_bpmn); FileOutputStream pngOut = new FileOutputStream(file_png); // 7、输入流,输出流的转换 IOUtils.copy(pngInput,pngOut); IOUtils.copy(bpmnInput,bpmnOut); // 8、关闭流 pngOut.close(); bpmnOut.close(); pngInput.close(); bpmnInput.close(); }
其中deploymentId
为流程部署ID, resource_name
为act_ge_bytearray表中NAME_列的值,使用repositoryService
的getDeploymentResourceNames()
方法可以获取指定部署下得所有文件的名称,使用repositoryService的getResourceAsStream()
方法传入部署ID和资源图片名称可以获取部署下指定名称文件的输入流。
即使流程定义已经删除了,流程执行的历史信息依然保存在Activiti的act_hi_*
相关的表中。所以还是可以查询流程执行的历史信息,可以通过HistoryService
提供的接口来查看相关的历史记录。
/** * 查看历史信息 */ @Test public void findHistoryInfo(){ // 获取引擎 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); // 获取HistoryService HistoryService historyService = processEngine.getHistoryService(); // 获取 actinst表的查询对象 HistoricActivityInstanceQuery instanceQuery = historyService.createHistoricActivityInstanceQuery(); // 查询 actinst表,条件:根据 InstanceId 查询 // instanceQuery.processInstanceId("2501"); // 查询 actinst表,条件:根据 DefinitionId 查询 instanceQuery.processDefinitionId("Demo:4:12504"); // 增加排序操作,orderByHistoricActivityInstanceStartTime 根据开始时间排序 asc 升序 instanceQuery.orderByHistoricActivityInstanceStartTime().asc(); // 查询所有内容 List<HistoricActivityInstance> activityInstanceList = instanceQuery.list(); // 输出 for (HistoricActivityInstance hi : activityInstanceList) { System.out.println(hi.getActivityId()); System.out.println(hi.getActivityName()); System.out.println(hi.getProcessDefinitionId()); System.out.println(hi.getProcessInstanceId()); System.out.println("<==========================>"); } }
查询结果如下所示:
到这里,以上都完美掌握以后就成功入门Activiti工作流引擎啦,可以根据实际项目需求进行相关需求的开发工作,但像很多复杂的应用例如组任务,网关等等还没有涉及,后续如有对更深入的进阶功能进行学习再进行博文更新。
今天雪下的超级大然而并不能快乐玩雪的乔木小姐
2022.03.18
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。