为什么要配置本地网络服务
描述:此服务应用于局域网内,访问他人的Oracle数据库
注意:
还是使用:NetConfigurationAssistant工具,单击打开
3.选择本地网络服务名配置,点击下一步
4.选择添加,点击下一步
5.输入服务器名称(SID),点击下一步
6.选择TCP协议,点击下一步
7.输入你要访问Oracle的IP地址和端口号(可以使用默认端口),我在家里,没有局域网,所以我填的是自己的ip地址,你们填的时候,一定要输入你要访问的IP地址,不要输入自己的IP
8.选择是进行测试,点击下一步
9.选择更改登录——输入用户名和密码——确定——下一步
12.选择完成,点击下一步
13.选择完成,配置完成
14.使用第三方工具登录Oracle数据库,点击OK
15.登录成功
16.查看刚才配置信息是否写入到tnsnames.ora文件当中
完整的Oracle数据库通常由两部分组成:Oracle数据库和数据库实例。
\1) 数据库是一系列物理文件的集合(数据文件,控制文件,联机日志,参数文件等);
\2) Oracle数据库实例则是一组Oracle后台进程/线程以及在服务器分配的共享内存区。
在启动Oracle数据库服务器时,实际上是在服务器的内存中创建一个Oracle实例(即在服务器内存中分配共享内存并创建相关的后台内存),然后由这个Oracle数据库实例来访问和控制磁盘中的数据文件。Oracle有一个很大的内存快,成为全局区(SGA)。
数据库是数据集合。Oracle是一种数据库管理系统,是一种关系型的数据库管理系统。
通常情况了我们称的“数据库”,并不仅指物理的数据集合,他包含物理数据、数据库管理系统。也即物理数据、内存、操作系统进程的组合体。
我们在安装Oracle数据库时,会让我们选择安装启动数据库(即默认的全局数据库)如下图:
全局数据库名:就是一个数据库的标识,在安装时就要想好,以后一般不修改,修改起来也麻烦,因为数据库一旦安装,数据库名就写进了控制文件,数据库表,很多地方都会用到这个数据库名。
启动数据库:也叫全局数据库,是数据库系统的入口,它会内置一些高级权限的用户如SYS,SYSTEM等。我们用这些高级权限账号登陆就可以在数据库实例中创建表空间,用户,表了。
查询当前数据库名:
select name from v$database;
用Oracle官方描述:实例是访问Oracle数据库所需的一部分计算机内存和辅助处理后台进程,是由进程和这些进程所使用的内存(SGA)所构成一个集合。
其实就是用来访问和使用数据库的一块进程,它只存在于内存中。就像Java中new出来的实例对象一样。
我们访问Oracle都是访问一个实例,但这个实例如果关联了数据库文件,就是可以访问的,如果没有,就会得到实例不可用的错误。
实例名指的是用于响应某个数据库操作的数据库管理系统的名称。她同时也叫SID。实例名是由参数instance_name决定的。
查询当前数据库实例名:
select instance_name from v$instance;
数据库实例名(instance_name)用于对外部连接。在操作系统中要取得与数据库的联系,必须使用数据库实例名。比如我们作开发,要连接数据库,就得连接数据库实例名:
jdbc:oracle:thin:@localhost:1521:orcl(orcl就为数据库实例名)
一个数据库可以有多个实例,在作数据库服务集群的时候可以用到。
Oracle数据库是通过表空间来存储物理表的,一个数据库实例可以有N个表空间,一个表空间下可以有N张表。
有了数据库,就可以创建表空间。
表空间(tablespace)是数据库的逻辑划分,每个数据库至少有一个表空间(称作SYSTEM表空间)。为了便于管理和提高运行效率,可以使用一些附加表空间来划分用户和应用程序。例如:USER表空间供一般用户使用,RBS表空间供回滚段使用。一个表空间只能属于一个数据库。
创建表空间语法:
Create TableSpace 表空间名称
DataFile 表空间数据文件路径
Size 表空间初始大小
Autoextend on
如:
create tablespace db_test
datafile 'D:\oracle\product\10.2.0\userdata\db_test.dbf'
size 50m
autoextend on;
查看已经创建好的表空间:
select default_tablespace, temporary_tablespace, d.username
from dba_users d
Oracle数据库建好后,要想在数据库里建表,必须先为数据库建立用户,并为用户指定表空间。
上面我们建好了数据库和表空间,接下来建用户:
创建新用户:
CREATE USER 用户名
IDENTIFIED BY 密码
DEFAULT TABLESPACE 表空间(默认USERS)
TEMPORARY TABLESPACE 临时表空间(默认TEMP)
如:
CREATE USER utest
IDENTIFIED BY utestpwd
DEFAULT TABLESPACE db_test
TEMPORARY TABLESPACE temp;(这里临时表空间不能使用我们创建的db_test,不知为何?)
有了用户,要想使用用户账号管理自己的表空间,还得给它分权限:
GRANT CONNECT TO utest;
GRANT RESOURCE TO utest;
GRANT dba TO utest; --dba为最高级权限,可以创建数据库,表等。
查看数据库用户:
select * from dba_users;
有了数据库,表空间和用户,就可以用自定义的用户在自己的表空间创建表了。有了表,我们可以开发了。
**数据库:
**Oracle数据库是数据的物理存储。这就包括(数据文件ORA或者DBF、控制文件、联机日志、参数文件)。其实Oracle数据库的概念和其它数据库不一样,这里的数据库是一个操作系统只有一个库。可以看作是Oracle就只有一个大数据库。
**实例:
**一个Oracle实例(Oracle Instance)有一系列的后台进程(Backguound Processes)和内存结构(Memory Structures)组成。一个数据库可以有n个实例。
**用户:
**用户是在实例下建立的。不同实例可以建相同名字的用户。
**表空间:
**表空间是一个用来管理数据存储逻辑概念,表空间只是和数据文件(ORA或者DBF文件)发生关系,数据文件是物理的,一个表空间可以包含多个数据文件,而一个数据文件只能隶属一个表空间。
**数据文件(dbf、ora):
** 数据文件是数据库的物理存储单位。数据库的数据是存储在表空间中的,真 正是在某一个或者多个数据文件中。而一个表空间可以由一个或多个数据文件组成,一个数据文件只能属于一个表空间。一旦数据文件被加入到某个表空间后,就不能删除这个文件,如果要删除某个数据文件,只能删除其所属于的表空间才行。
注:
表的数据,是有用户放入某一个表空间的,而这个表空间会随机把这些表数据放到一个或者多个数据文件中。由于oracle的数据库不是普通的概念,oracle是有用户和表空间对数据进行管理和存放的。但是表不是有表空间去查询的,而是由用户去查的。因为不同用户可以在同一个表空间建立同一个名字的表!这里区分就是用户了!
理解1:
Oracle数据库可以创建多个实例,每个实例可以创建多个表空间,每个表空间下可以创建多个用户(同时用户也属于表空间对应的实例)和数据库文件,用户可以创建多个表(每个表随机存储在一个或多个数据库文件中),如下图:
理解2:
理解1MS有误。
实例下有和,授权访问,是管理的,经授权在中创建,随机存储到不同的中。如下图所示:
操作1:
安装Oracle后会有默认的实例,即ORCL。一般不创建多个实例,在默认实例下创建表空间和用户等。
1,运行CMD进入DOS界面,首先输入:sqlplus,回车;再输入:sys/sys as sysdba,回车,即进入“SQL〉”操作状态。
2,输入:CREATE TABLESPACE 表空间名称 LOGGING DATAFILE ‘c:\表空间数据文件.ora’ SIZE 500M autoextend on next 200m maxsize 2048m;,表空间创建完成。
3,输入:CREATE USER 用户名称 PROFILE default IDENTIFIED BY 用户密码 DEFAULT TABLESPACE 授权访问的表空间名称 TEMPORARY TABLESPACE temp ACCOUNT UNLOCK;,用户创建完成,并授权用户访问某表空间。
操作2:
创建表和插入数据,并查询浏览插入的数据。
1,创建表,输入:
create table 表名(
字段名称1 字段类型,
字段名称2 字段类型,
字段名称3 字段类型
);,回车。
2,插入数据,输入:insert into 表名(字段1,字段2,字段3) VALUES(值1,值2,值3);,回车。
3,查询数据,输入:select * from 表名;,回车。
在Oracle中,结合逻辑存储与物理存储的概念,我们可以这样来理解数据库、表空间、SCHEMA、数据文件这些概念:
数据库是一个大圈,里面圈着的是表空间,表空间里面是数据文件,那么schema是什么呢?schema是一个逻辑概念,是一个集合,但schema并不是一个对象,oracle也并没有提供创建schema的语法。
一般而言,一个用户就对应一个schema,该用户的schema名等于用户名,并作为该用户缺省schema,用户是不能创建schema的,schema在创建用户的时候创建,并可以指定用户的各种表空间(这点与PostgreSQL是不同,PostgreSQL是可以创建schema并指派给某个用户)。当前连接到数据库上的用户创建的所有数据库对象默认都属于这个schema(即在不指明schema的情况下),比如若用户scott连接到数据库,然后create table test(id int not null)创建表,那么这个表被创建在了scott这个schema中;但若这样create kanon.table test(id int not null)的话,这个表被创建在了kanon这个schema中,当然前提是权限允许。
创建用户的方法是这样的:
create user 用户名 identified by 密码
default tablespace 表空间名
temporary tablespace 表空间名
quota 限额 (建议创建的时候指明表空间名)
由此来看,schema是一个逻辑概念。
但一定要注意一点:schema好像并不是在创建user时就创建的,而是在该用户创建了第一个对象之后才将schema真正创建的,只有user下存在对象,他对应的schema才会存在,如果user下不存在任何对象了,schema也就不存在了;
数据库
在oracle中,数据库是由表空间来组成的,而表空间里面是具体的物理文件—数据文件。我们可以创建数据库并为其指定各种表空间
表空间
这是个逻辑概念,本质上是一个或者多个数据文件的集合。
数据文件:
具体存储数据的物理文件,是一个物理概念。
一个数据文件只能属于一个表空间,一个表空间可以包含一个或多个数据文件。一个数据库由多个表空间组成,一个表空间只能属于一个数据库。
若还不理解,下面是我从网上摘的一个比喻,很形象的解释了什么是Database,什么是Schema,什么是Table,什么是列,什么是行,什么是User,不妨一看。
“我们可以把Database看作是一个大仓库,仓库分了很多很多的房间,Schema就是其中的房间,一个Schema代表一个房间,Table可以看作是每个Schema中的床,Table(床)被放入每个房间中,不能放置在房间之外,那岂不是晚上睡觉无家可归了,然后床上可以放置很多物品,就好比 Table上可以放置很多列和行一样,数据库中存储数据的基本单元是Table,现实中每个仓库放置物品的基本单位就是床, User就是每个Schema的主人,(所以Schema包含的是Object,而不是User),user和schema是一一对应的,每个user在没有特别指定下只能使用自己schema(房间)的东西,如果一个user想使用其他schema(房间)的东西,那就要看那个schema(房间)的user(主人)有没有给你这个权限了,或者看这个仓库的老大(DBA)有没有给你这个权限了。换句话说,如果你是某个仓库的主人,那么这个仓库的使用权和仓库中的所有东西都是你的(包括房间),你有完全的操作权,可以扔掉不用的东西从每个房间,也可以放置一些有用的东西到某一个房间,你还可以给每个User分配具体的权限,也就是他到某一个房间能做些什么,是只能看(Read-Only),还是可以像主人一样有所有的控制权(R/W),这个就要看这个User所对应的角色Role了。”—摘自网络
-- 新建一个Command Window 在提示符下输入一下命令,回车执行就可以了
-- 创建表空间
create tablespace dbspace datafile 'D:\oracle\product\10.2.0\oradata\orcl\dbspace.dbf' size 400M autoextend on next 10m maxsize unlimited;
-- 删除表空间其中1) DATAFILE: 表空间数据文件存放路径2)
DROP TABLESPACE dbspace INCLUDING CONTENTS AND DATAFILES;
SIZE: 起初设置为200M3) UNIFORM: 指定区尺寸为128k,如不指定,区尺寸默认为64k 4) 空间名称histdb 与 数据文件名称 histdb.dbf 不要求相同,可随意命名.5) AUTOEXTEND ON/OFF 表示启动/停止自动扩展表空间6)
alter database datafile ' D:\oracle\product\10.2.0\oradata\orcl\histdb.dbf ' resize 500m;
//手动修改数据文件大小为下列命令用于为表空间建立用户,将用户的默认表空间设置为刚建立的表空间
create user test1 identified by test1 default tablespace dbspace; alter database default tablespace dbspace;
create user test identified by test; select username, default_tablespace defspace from dba_users where username='TEST';
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 1、普通的时间转换
String string = new SimpleDateFormat("yyyy-MM-dd").format(new Date()).toString();
System.out.println(string); // 2022-07-19
System.out.println("-------------------------------");
// 2、日历类的时间操作
Calendar calendar = Calendar.getInstance();
System.out.println(calendar.get(Calendar.YEAR)); // 2022
System.out.println(calendar.get(Calendar.MONTH) + 1); // 7
System.out.println(calendar.get(Calendar.DATE)); // 19
// 当前时间: 2022-07-19 10:07:41
System.out.println("当前时间: " + dateFormat.format(calendar.getTime()));
// 获取当前时间前3秒
calendar.add(Calendar.SECOND, -3);// 3秒之前的时间
// 当前时间3秒之前: 2022-07-19 10:07:38
System.out.println("当前时间3秒之前: " + dateFormat.format(calendar.getTime()));
// 获取当前时间前3分钟
calendar.add(Calendar.MINUTE, -3);// 3分钟之前的时间
// 当前时间3分钟之前: 2022-07-19 10:04:38
System.out.println("当前时间3分钟之前: " + dateFormat.format(calendar.getTime()));
// 获取当前时间后3秒
calendar.add(Calendar.SECOND, 3);// 3秒之后的时间
// 当前时间3秒之后: 2022-07-19 10:04:41
System.out.println("当前时间3秒之后: " + dateFormat.format(calendar.getTime()));
// 获取当前时间后3分钟
calendar.add(Calendar.MINUTE, 3);// 3分钟之后的时间
// 当前时间3分钟之后: 2022-07-19 10:07:41
System.out.println("当前时间3分钟之后: " + dateFormat.format(calendar.getTime()));
// 获取当前时间
Date date1=new Date();
Calendar calendar1 = new GregorianCalendar();
calendar1.setTime(date1);
// 把日期往后增加一天,整数 往后推,负数往前移动
calendar1.add(calendar1.DATE, 1);
// 这个时间就是日期往后推一天的结果
Date time = calendar1.getTime();
System.out.println(time); // Wed Jul 20 10:07:41 CST 2022
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
List<String> newList = list.subList(0, 2);
System.out.println("取前两条数据:"+ newList); // 取前两条数据:[a, b]
参考链接:https://blog.csdn.net/weixin_44162337/article/details/107828366?spm=1001.2014.3001.5506
// 1.根据id更新
User user = new User();
user.setUserId(1);
user.setAge(29);
userMapper.updateById(user);
// 2.条件构造器作为参数进行更新
// 把名字为rhb的用户年龄更新为18,其他属性不变
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("name","rhb");
User user = new User();
user.setAge(18);
userMapper.update(user, updateWrapper);
// 假设只更新一个字段在使用updateWrapper 的构造器中也需要构造一个实体对象,
// 这样比较麻烦。可以使用updateWrapper的set方法
// 只更新一个属性,把名字为rhb的用户年龄更新为18,其他属性不变
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("name","rhb").set("age", 18);
userMapper.update(null, updateWrapper);
// 3.lambda构造器
LambdaUpdateWrapper<User> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
lambdaUpdateWrapper.eq(User::getName, "rhb").set(User::getAge, 18);
Integer rows = userMapper.update(null, lambdaUpdateWrapper);
————————————————
版权声明:本文为CSDN博主「伊宇紫」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/LDY1016/article/details/100121146
最近的项目中涉及到使用RestTemplate 调用请求方式为delete和put的接口,但是RestTemplate提供的delete()和put()方法都没有返回值,但是我要调用的接口是有返回值的,网上的资料很多都是写的调用exchange()方法来实现,但是基本上都没有给出完整实例,导致我在参考他们的代码的时候会出现参数无法传递的问题,目前我已经解决该问题,现将我的解决方法分享出来.
我同样是使用exchange()方法来实现,但是url有讲究,需要像使用exchange方法调用get请求一样,使用占位符
1、 delete请求实例,请求方式使用 HttpMethod.DELETE
/**
* 删除用户
* @param id
* @return
*/
public String delete(Long id) {
StringBuffer url = new StringBuffer(baseUrl).append("/user/delete/{id}");
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("id", id);
ResponseEntity<String > response = restTemplate.exchange(url.toString(),
HttpMethod.DELETE, null, String .class, paramMap);
String result = response.getBody();
return result;
}
2、put请求实例,请求方式使用 HttpMethod.PUT
/**
* 更新用户基础信息
* @param userInfoDTO
* @return
*/
public String edit(UserInfoDTO userInfoDTO) {
StringBuffer url = new StringBuffer(baseUrl)
.append("/user/edit?tmp=1")
.append("&id={id}")
.append("&userName={userName}")
.append("&nickName={nickName}")
.append("&realName={realName}")
.append("&sex={sex}")
.append("&birthday={birthday}");
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("userId", userInfoDTO.getId());
paramMap.put("userName", userInfoDTO.getUserName());
paramMap.put("nickName", userInfoDTO.getNickName());
paramMap.put("realName", userInfoDTO.getRealName());
paramMap.put("sex", userInfoDTO.getSex());
paramMap.put("birthday", userInfoDTO.getBirthday());
ResponseEntity<String > response = restTemplate.exchange(url.toString(),
HttpMethod.PUT, null, String .class, paramMap);
String result = response.getBody();
return result;
}
最近使用Spring 的 RestTemplate 工具类请求接口的时候发现参数传递的一个坑,也就是当我们把参数封装在Map里面的时候,Map 的类型选择。 使用RestTemplate post请求的时候主要可以通过三种方式实现:
1、调用postForObject方法 2、使用postForEntity方法 3、调用exchange方法
postForObject和postForEntity方法的区别主要在于可以在postForEntity方法中设置header的属性,当需要指定header的属性值的时候,使用postForEntity方法。exchange方法和postForEntity类似,但是更灵活,exchange还可以调用get、put、delete请求。使用这三种方法调用post请求传递参数,当接口是表单接收参数的时候,Map不能定义为以下两种类型(url使用占位符进行参数传递时除外),HashMap是以请求体传递,MultiValueMap是表单传递!
Map<String, Object> paramMap = new HashMap<String, Object>();
Map<String, Object> paramMap = new LinkedHashMap<String, Object>();
经过测试,我发现这两种map里面的参数都不能被后台接收到,这个问题困扰我两天,终于,当我把Map类型换成LinkedMultiValueMap后,参数成功传递到后台
MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<String, Object>();
经过测试,正确的传参方式如下:
public static void main(String[] args) {
RestTemplate template = new RestTemplate();
String url = "http://192.168.2.40:8081/channel/channelHourData/getHourNewUserData";
// 封装参数,千万不要替换为Map与HashMap,否则参数无法传递
MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<String, Object>();
paramMap.add("dt", "20180416");
// 1、使用postForObject请求接口
String result = template.postForObject(url, paramMap, String.class);
System.out.println("result1==================" + result);
// 2、使用postForEntity请求接口
HttpHeaders headers = new HttpHeaders();
HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<MultiValueMap<String, Object>>(paramMap,headers);
ResponseEntity<String> response2 = template.postForEntity(url, httpEntity, String.class);
System.out.println("result2====================" + response2.getBody());
// 3、使用exchange请求接口
ResponseEntity<String> response3 = template.exchange(url, HttpMethod.POST, httpEntity, String.class);
System.out.println("result3====================" + response3.getBody());
}
GET方式传参说明
如果是get请求,又想要把参数封装到map里面进行传递的话,Map需要使用HashMap,且url需要使用占位符,如下:
public static void main(String[] args) {
RestTemplate restTemplate2 = new RestTemplate();
String url = "http://127.0.0.1:8081/interact/getData?dt={dt}&ht={ht}";
// 封装参数,这里是HashMap
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("dt", "20181116");
paramMap.put("ht", "10");
//1、使用getForObject请求接口
String result1 = template.getForObject(url, String.class, paramMap);
System.out.println("result1====================" + result1);
//2、使用exchange请求接口
HttpHeaders headers = new HttpHeaders();
headers.set("id", "lidy");
HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<MultiValueMap<String, Object>>(null,headers);
ResponseEntity<String> response2 = template.exchange(url, HttpMethod.GET, httpEntity, String.class,paramMap);
System.out.println("result2====================" + response2.getBody());
}
ps:post请求也可以通过占位符的方式进行传参(类似get),但是看起来不优雅,推荐使用文中的方式
参考链接:https://blog.csdn.net/weixin_38997187/article/details/107352518?spm=1001.2014.3001.5506
compile(默认选项):表示为当前依赖参与项目的编译、测试和运行阶段,scope的默认选项。
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
dependency>
test :表示为当前依赖只参与测试阶段。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
runtime :表示为当前依赖只参与运行阶段,一般这种类库都是接口与实现相分离的类库,如mysql的驱动包。
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<scope>runtimescope>
dependency>
privided :表示为当前依赖在打包过程中,不需要打进去,需要由运行的环境来提供,比如tomcat或者基础类库,如a依赖于b和c,bc中都包含gson依赖(privided),则a中不会依赖bc中的gson,需由a自行引入gson依赖。
<dependency>
<groupId>com.google.code.gsongroupId>
<artifactId>gsonartifactId>
<scope>providedscope>
dependency>
system :表示为当前依赖不从maven仓库获取,从本地系统获取,结合systempath使用,常用于无法从maven仓库获取的包。
<dependency>
<groupId>com.supermapgroupId>
<artifactId>dataartifactId>
<version>1.0version>
<scope>systemscope>
<systemPath>
${project.basedir}/src/main/resources/supermap/com.supermap.data.jar
systemPath>
dependency>
**import **:表示为当前项目依赖为多继承关系,常用于项目中自定义父工程,需要注意的是只能用在dependencyManagement里面,且仅用于type=pom的dependency。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>${spring-boot.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
参考链接:https://blog.csdn.net/weixin_38997187/article/details/107363821?spm=1001.2014.3001.5506
optional可选依赖,表示当一个项目中的依赖添加上optional=true的选项后,子项目中将不会包含这个依赖。常用于解决jar包冲突问题,和exclusion排除jar有异曲同工之妙,假设a项目中引入了以下依赖:
<project>
<groupId>com.xxx.testgroupId>
<artifactId>test-aartifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>pompackaging>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-configuration-processorartifactId>
dependency>
project>
b项目引入了a项目后发现spring-boot-configuration-processor和当前项目存在版本冲突问题,解决方式:
一、b项目中使用exclusion排除
<dependency>
<groupId>com.xxx.testgroupId>
<artifactId>test-aartifactId>
<version>0.0.1-SNAPSHOTversion>
<exclusions>
<exclusion>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-configuration-processorartifactId>
exclusion>
exclusions>
dependency>
二、a项目中使用optional选项
<project>
<groupId>com.xxx.testgroupId>
<artifactId>test-aartifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>pompackaging>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-configuration-processorartifactId>
<optional>trueoptional>
dependency>
project>
参考链接:https://blog.csdn.net/weixin_38997187/article/details/107359593
作用
maven中dependencyManagement常用于解决子项目按需加载问题。
案列
1.test-parent项目,包含模块utils、common、auth,通常写法结构如下:
<project>
<modelVersion>4.0.0modelVersion>
<groupId>com.xxx.testgroupId>
<artifactId>test-parentartifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>pompackaging>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.3.RELEASEversion>
parent>
<dependencies>
<dependency>
<groupId>com.xxx.testgroupId>
<artifactId>test-utilsartifactId>
<version>0.0.1-SNAPSHOTversion>
dependency>
<dependency>
<groupId>com.xxx.testgroupId>
<artifactId>test-authartifactId>
<version>0.0.1-SNAPSHOTversion>
dependency>
<dependency>
<groupId>com.xxx.testgroupId>
<artifactId>test-commonartifactId>
<version>0.0.1-SNAPSHOTversion>
dependency>
dependencies>
project>
2.假设b项目将test-parent作为父项目,b将加载test-parent中的utils、common、auth模块
<project>
<modelVersion>4.0.0modelVersion>
<groupId>com.xxx.testgroupId>
<artifactId>test-bartifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>jarpackaging>
<parent>
<groupId>com.xxx.testgroupId>
<artifactId>test-parentartifactId>
<version>2.1.3.RELEASEversion>
parent>
<dependencies>
其它依赖
dependencies>
project>
3.此时c项目将test-parent作为父项目,只需加载common模块,那么1这种写法显然不可行,因为我们只需要加载parent中的common模块,此时dependencyManagement的作用就体现出来了,改写写法1如下:
<project>
<modelVersion>4.0.0modelVersion>
<groupId>com.xxx.testgroupId>
<artifactId>test-parentartifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>pompackaging>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.3.RELEASEversion>
parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.xxx.testgroupId>
<artifactId>test-utilsartifactId>
<version>0.0.1-SNAPSHOTversion>
dependency>
<dependency>
<groupId>com.xxx.testgroupId>
<artifactId>test-authartifactId>
<version>0.0.1-SNAPSHOTversion>
dependency>
<dependency>
<groupId>com.xxx.testgroupId>
<artifactId>test-commonartifactId>
<version>0.0.1-SNAPSHOTversion>
dependency>
dependencies>
dependencyManagement>
project>
4.c项目中写法如下,只需要引入相应的模块,版本号会根据parent中指定的版本号加载。
<project>
<modelVersion>4.0.0modelVersion>
<groupId>com.xxx.testgroupId>
<artifactId>test-bartifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>jarpackaging>
<parent>
<groupId>com.xxx.testgroupId>
<artifactId>test-parentartifactId>
<version>2.1.3.RELEASEversion>
parent>
<dependency>
<groupId>com.xxx.testgroupId>
<artifactId>test-commonartifactId>
dependency>
project>
————————————————
版权声明:本文为CSDN博主「紫金小飞侠」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yangshengwei230612/article/details/103964905
HttpClient
HttpClient 是Apache Jakarta Common 下的子项目,可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,方便在日常项目开发中,调用第三方接口数据。
HttpClient的主要功能
环境说明:JDK1.8、SpringBoot(2.2.2)
在pom.xml中引入HttpClient的依赖和SpringBoot的基本依赖配置(web,jpa,fastjson,mysql)。
<dependency>
<groupId>org.apache.httpcomponentsgroupId>
<artifactId>httpclientartifactId>
<version>4.5.3version>
dependency>
httpclient使用示例主要步骤
【步骤】:
1)创建一个httpclient对象,注意以下版本问题说明
HttpClient4.0版本前:
HttpClient httpClient = new DefaultHttpClient();
4.0版本后:
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
2)创建一个httpGet对象
HttpGet request = new HttpGet(uri);
3)执行请求调用httpclient的execute(),传入httpGet对象,返回CloseableHttpResponse response = httpClient.execute(request, HttpClientContext.create());
4)取得响应结果并处理
5)关闭HttpClient
注意:主动设置编码,防止响应出现乱码
/**GET有参:*/
@Autowired
private UserServiceImple userServiceImple;
@Test
public void getByUsername(){
List<User> users = userServiceImple.findByUserName("王三");
System.out.println(JSON.toJSONString(users));
}
@Test
public void doGetTestWayOne() {
// 获得Http客户端(可以理解为:你得先有一个浏览器;注意:实际上HttpClient与浏览器是不一样的)
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
// 创建Get请求
HttpGet httpGet = new HttpGet("http://localhost:8080/user/2");
// 响应模型
CloseableHttpResponse response = null;
try {
// 配置信息
RequestConfig requestConfig = RequestConfig.custom()
// 设置连接超时时间(单位毫秒)
.setConnectTimeout(5000)
// 设置请求超时时间(单位毫秒)
.setConnectionRequestTimeout(5000)
// socket读写超时时间(单位毫秒)
.setSocketTimeout(5000)
// 设置是否允许重定向(默认为true)
.setRedirectsEnabled(true).build();
// 将上面的配置信息 运用到这个Get请求里
httpGet.setConfig(requestConfig);
// 由客户端执行(发送)Get请求
response = httpClient.execute(httpGet);
// 从响应模型中获取响应实体
HttpEntity responseEntity = response.getEntity();
System.out.println("响应状态为:" + response.getStatusLine());
if (responseEntity != null) {
System.out.println("响应内容长度为:" + responseEntity.getContentLength());
//主动设置编码,防止相应出现乱码
System.out.println("响应内容为:" + EntityUtils.toString(responseEntity, StandardCharsets.UTF_8));
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
// 释放资源
if (httpClient != null) {
httpClient.close();
}
if (response != null) {
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* POST---有参测试(对象参数)
* @date
*/
@Test
public void dopostHaveObjectParam() {
// 获得Http客户端(可以理解为:你得先有一个浏览器;注意:实际上HttpClient与浏览器是不一样的)
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
// 创建Post请求
HttpPost httpPost = new HttpPost("http://localhost:8080/user");
User user = new User();
user.setUserName("张山");
user.setPassword("Ss@1234");
// 我这里利用阿里的fastjson,将Object转换为json字符串;
// (需要导入com.alibaba.fastjson.JSON包)
String jsonString = JSON.toJSONString(user);
// 主动设置编码,防止出现乱码
StringEntity entity = new StringEntity(jsonString, "UTF-8");
// post请求是将参数放在请求体里面传过去的;这里将entity放入post请求体中
httpPost.setEntity(entity);
httpPost.setHeader("Content-Type", "application/json;charset=utf8");
// 响应模型
CloseableHttpResponse response = null;
try {
// 由客户端执行(发送)Post请求
response = httpClient.execute(httpPost);
// 从响应模型中获取响应实体
HttpEntity responseEntity = response.getEntity();
System.out.println("响应状态为:" + response.getStatusLine());
if (responseEntity != null) {
System.out.println("响应内容长度为:" + responseEntity.getContentLength());
//主动设置编码,防止相应出现乱码
System.out.println("响应内容为:" + EntityUtils.toString(responseEntity, StandardCharsets.UTF_8));
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
// 释放资源
if (httpClient != null) {
httpClient.close();
}
if (response != null) {
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* Put---有参测试(对象参数)
* @date
*/
@Test
public void doPutObjectParam() {
// 获得Http客户端(可以理解为:你得先有一个浏览器;注意:实际上HttpClient与浏览器是不一样的)
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
// 创建Put请求
HttpPost httpPut = new HttpPost("http://localhost:8080/user");
User user = new User();
user.setId((long) 2);
user.setUserName("张山");
user.setPassword("Ss@1234");
// 我这里利用阿里的fastjson,将Object转换为json字符串;
// (需要导入com.alibaba.fastjson.JSON包)
String jsonString = JSON.toJSONString(user);
// 主动设置编码,防止出现乱码
StringEntity entity = new StringEntity(jsonString, "UTF-8");
// post请求是将参数放在请求体里面传过去的;这里将entity放入Put请求体中
httpPut.setEntity(entity);
httpPut.setHeader("Content-Type", "application/json;charset=utf8");
// 响应模型
CloseableHttpResponse response = null;
try {
// 由客户端执行(发送)Put请求
response = httpClient.execute(httpPut);
// 从响应模型中获取响应实体
HttpEntity responseEntity = response.getEntity();
System.out.println("响应状态为:" + response.getStatusLine());
if (responseEntity != null) {
System.out.println("响应内容长度为:" + responseEntity.getContentLength());
//主动设置编码,防止相应出现乱码
System.out.println("响应内容为:" + EntityUtils.toString(responseEntity, StandardCharsets.UTF_8));
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
// 释放资源
if (httpClient != null) {
httpClient.close();
}
if (response != null) {
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* DeleteTest
*/
@Test
public void doDeleteTest() {
// 获得Http客户端(可以理解为:你得先有一个浏览器;注意:实际上HttpClient与浏览器是不一样的)
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
// 创建Delete请求
HttpDelete httpDelete = new HttpDelete("http://localhost:8080/user/41");
// 响应模型
CloseableHttpResponse response = null;
try {
// 配置信息
RequestConfig requestConfig = RequestConfig.custom()
// 设置连接超时时间(单位毫秒)
.setConnectTimeout(5000)
// 设置请求超时时间(单位毫秒)
.setConnectionRequestTimeout(5000)
// socket读写超时时间(单位毫秒)
.setSocketTimeout(5000)
// 设置是否允许重定向(默认为true)
.setRedirectsEnabled(true).build();
// 将上面的配置信息 运用到这个Delete请求里
httpDelete.setConfig(requestConfig);
// 由客户端执行(发送)Delete请求
response = httpClient.execute(httpDelete);
// 从响应模型中获取响应实体
HttpEntity responseEntity = response.getEntity();
System.out.println("响应状态为:" + response.getStatusLine());
if (responseEntity != null) {
System.out.println("响应内容长度为:" + responseEntity.getContentLength());
//主动设置编码,防止相应出现乱码
System.out.println("响应内容为:" + EntityUtils.toString(responseEntity, StandardCharsets.UTF_8));
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
// 释放资源
if (httpClient != null) {
httpClient.close();
}
if (response != null) {
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
————————————————
版权声明:本文为CSDN博主「贾西贝Xx」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_41968486/article/details/108627843
node版本更新可以下载最新的安装包,然后对原路径的进行覆盖,下面详细说一下:
1.更新nodeJS:
win+r 打开命令行,在命令行中输入:where node ,找到node的安装位置。
2.查看自己的node版本,在命令行输入:node -v
3.接下来,在浏览器中打开 node官网:https://nodejs.org/zh-cn/
,下载LTS版本的node安装包,如下图:下载红框里的那个
4.下载最新版本到本地,安装包是msi类型的,然后双击,解压,选择的安装路径为你之前安装node的路径即可,然后其他都是默认,直到安装完成。
5.安装完成后之后在命令行中输入node -v
,查看是否升级到指定版本。
1.更新npm可以直接用指令就可以,在CMD命令行输入命令
2.同样更新后,输入 npm -v
,就可以查看npm版本
————————————————
版权声明:本文为CSDN博主「Alone_in_」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Alone_in_/article/details/105972368
一、前言
在写Sql 的时候遇到个问题,两个整数 int 类型相除,我希望保留两位小数。
二、问题解决
1、知识普及:
select 10/3
-->3
select 10%3
-->1
select 11/4
-->2
select 11%4
-->3
/ 符号是取整,% 符号是取余。在上面的例子中,清晰的展示了它们的作用。需要注意的是 / 取整符号是直接截断的,11除于4结果应该是2.75,取整后就是2,由此可看出它是不会四舍五入的。
2、步入正题:
方法有三种:cast、convert 和 round。这里用10除于3举例。
比较常用的就是CAST函数了,这里就是先将 int类型的 10 转换成了 float ,再去除于3,得出的结果就有无限小数,第一种写法比较特别,刚开始我也没看懂,怎么会多了一个点?!直到看到第二种写法才恍然大悟,只是它将小数位省略不写而已。结果是一样的。
-- decimal(8,2) 8指定指定小数点左边和右边个数之和可以存储的十进制数字的最大个数,最大精度8。
-- 2指定小数点右边可以存储的十进制数字的最大个数。但是小数位数必须是从 0 到 8之间的值。默认小数位数是 0。
select CAST(10*1./3 as decimal(8,2))
-->3.33
select CAST(10*1.0/3 as decimal(8,3))
-->3.333
select CAST(1000*1.0/3 as decimal(7,3))
-->结果为33333.333,超过了7位数,报错
这种功能上和cast 类似,就是写法不同而已。
select CONVERT(decimal(8,2),10*1./3)
-->3.33
select CONVERT(decimal(8,2),10*1.0/3)
-->3.33
最后就是ROUND函数了,保留是保留了,但保留的是两位有效的小数,后面的很多个零就是根据默认类型自动填充的。
select ROUND(10*1./3,2)
-->3.330000
select ROUND(10*1.0/3,2)
-->3.330000
要去掉的话也方便,再丢回到前两个函数中转换一下即可,如:
select CONVERT(decimal(10,2),ROUND(10*1.00/3,2))
-->3.33
这方法看似多此一举,但也有他的用处。CAST函数和CONVERT函数都是自动四舍五入的,而ROUND函数有一个参数,设置非0的数后,就会变成直接截断。这里拿11除于4来举例会比较清晰,如下:
select ROUND(11*1.0/4,1)
-->2.800000
select ROUND(11*1.0/4,1,6)
-->2.700000
参考链接:https://yueludanfeng.blog.csdn.net/article/details/90750248?spm=1001.2014.3001.5506
语法
实例
select to_date('2022-07-28','yyyy-MM-dd') - to_date('2022-07-01','yyyy-MM-dd') toDate
select generate_series(0, to_date('2022-07-28','yyyy-MM-dd') - to_date('2022-07-01','yyyy-MM-dd') )
SELECT to_date('2022-07-01','yyyy-MM-dd') + generate_series(0, to_date('2022-07-28','yyyy-MM-dd') - to_date('2022-07-01','yyyy-MM-dd') )
select cal_date,coalesce(overtime,0) overtime from
( SELECT to_date('2022-07-01','yyyy-MM-dd') + generate_series(0, to_date('2022-07-28','yyyy-MM-dd') - to_date('2022-07-01','yyyy-MM-dd') ) cal_date ) c
left join (
select empno,attn_date,duration_actual - duration_roster overtime from ihr.mgt_attendance
where attn_date BETWEEN to_date('2022-07-01','yyyy-MM-dd') and to_date('2022-07-28','yyyy-MM-dd')
and duration_actual - duration_roster > 0
and empno = 'P4370'
) a
on c.cal_date = a.attn_date
ORDER BY cal_date
————————————————
版权声明:本文为CSDN博主「小脑斧学技术」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42270645/article/details/123416135
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
根据springcloud alibaba版本说明下载对应的Seata版本,否则可能会出现各种版本不兼容的问题。
下载地址:https://github.com/seata/seata/releases
资源包下载地址:https://github.com/seata/seata/tree/develop/script
以下步骤按照DB存储为例。
到这里就完成了Seata的基本配置,执行seata/bin/seata-server.bat启动Seata,默认使用本地的配置。
修改config.type=“nacos”
将资源包script放在seata安装目录下
打开配置文件(seata -> script -> config-center -> config.txt)
修改store.mode=“db”
修改数据库连接配置url、username、password
# 如果nacos不在本地,可以通过参数指定nacos服务的IP、端口、groupId、命名空间
liunx: ./nacos-config.sh -h localhost -p 8848 -g SEATA_GROUP -t 命名空间
# python脚本中分组是固定写死的,自定义需要修改脚本
python: nacos-config.py localhost:8848 命名空间
执行seata/bin/seata-server.bat再次启动Seata使用就是nacos中的配置了。
Seata在liunx中启动参数
./seata-server.sh -p 8092 -n 1
-h: 注册到注册中心的ip
-p: Server rpc 监听端口
-m: 全局事务会话信息存储模式,file、db、redis,优先读取启动参数 (Seata-Server 1.3及以上版本支持redis)
-n: Server node,多个Server时,需区分各自节点,用于生成不同区间的transactionId,以免冲突
-e: 多环境配置参考 http://seata.io/en-us/docs/ops/multi-configuration-isolation.html
Seata详细参数配置:https://seata.io/zh-cn/docs/user/configurations.html
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-seataartifactId>
dependency>
seata:
# 事务分组 config.txt中的service.vgroupMapping.my_test_tx_group
tx-service-group: my_test_tx_group
# seata注册中心配置 参考 seata安装目录 -> conf -> registry.conf
registry:
type: nacos
nacos:
application: seata-server # seata服务名
server-addr: localhost:8848 # nacos服务地址
group: SEATA_GROUP # nacos分组 默认为:SEATA_GROUP
namespace: public # nacos命名空间
cluster: default # 集群名称 需要与config.txt中的service.vgroupMapping.my_test_tx_group的配置一致
username: nacos # nacos用户名
password: nacos # nacos密码
# seata配置中心配置 参考 seata安装目录 -> conf -> registry.conf
config:
type: nacos
nacos:
server-addr: localhost:8848 # nacos服务地址
group: SEATA_GROUP # nacos分组
namespace: public # nacos命名空间
username: nacos # nacos用户名
password: nacos# nacos密码
————————————————
版权声明:本文为CSDN博主「卷王2048」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_45818845/article/details/124550408
若依POI(Excel)模块探究
POI是一个java操作Excel的工具包
POI中的对象
1.工作薄对象WorkBook,WorkBook对象的实现类有3个:
HSSFWorkbook(03版Excel,最多只有65536行,导入导出速度快)
XSSFWorkbook(07版Excel,支持百万数据的导入导出,导入导出速度慢)
SXSSFWorkbook(07版Excel升级版,会在过程中生成中间文件,导入导出速度快)
2.工作表对象Sheet
3.行对象Row
4.单元格对象Cell
最基础的demo
需要的maven依赖
<dependencies>
<dependency>
<groupId>org.apache.poigroupId>
<artifactId>poiartifactId>
<version>5.2.2version>
dependency>
<dependency>
<groupId>org.apache.poigroupId>
<artifactId>poi-ooxmlartifactId>
<version>5.2.2version>
dependency>
<dependency>
<groupId>org.junit.jupitergroupId>
<artifactId>junit-jupiterartifactId>
<version>5.8.2version>
<scope>testscope>
dependency>
dependencies>
导出demo
@Test
public void SimplePIO() throws IOException {
String path = "E:\\java代码\\POI练习代码\\output\\";
//新建工作簿
Workbook workbook = new SXSSFWorkbook();
//用工作簿来新建工作表
Sheet sheet = workbook.createSheet(String.valueOf(0));
//用工作表新建行
Row row = sheet.createRow(0);
//用行新建单元格
Cell cell = row.createCell(0);
//向单元格中写入值
cell.setCellValue("666");
Row row1 = sheet.createRow(1);
//向第二行的100列添加元素
for (int i = 0; i < 100; i++) {
row1.createCell(i).setCellValue(i);
}
//用输出流创建一个文件
OutputStream out = new FileOutputStream(path+"test.xlsx");
//将工作簿的内容写在Excel中,输出
workbook.write(out);
out.close();
}
想自定义注解类,先要了解JDK自带的注解
功能:指明了修饰的这个注解的使用范围,即被描述的注解可以用在哪里。
ElementType的取值包含以下几种:
功能:指明修饰的注解的生存周期,即会保留到哪个阶段。
RetentionPolicy的取值包含以下三种:
功能:指明修饰的注解,可以被例如javadoc此类的工具文档化,只负责标记,没有成员取值。
功能:允许子类继承父类中的注解。
注意!:
@interface意思是声明一个注解,方法名对应参数名,返回值类型对应参数类型。
一、前期准备
1.maven依赖
<activiti.version>5.21.0activiti.version>
<dependency>
<groupId>org.activitigroupId>
<artifactId>activiti-engineartifactId>
<version>${activiti.version}version>
dependency>
<dependency>
<groupId>org.activitigroupId>
<artifactId>activiti-springartifactId>
<version>${activiti.version}version>
dependency>
<dependency>
<groupId>org.activitigroupId>
<artifactId>activiti-explorerartifactId>
<version>${activiti.version}version>
<exclusions>
<exclusion>
<artifactId>vaadinartifactId>
<groupId>com.vaadingroupId>
exclusion>
<exclusion>
<artifactId>dcharts-widgetartifactId>
<groupId>org.vaadin.addonsgroupId>
exclusion>
<exclusion>
<artifactId>activiti-simple-workflowartifactId>
<groupId>org.activitigroupId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.activitigroupId>
<artifactId>activiti-modelerartifactId>
<version>${activiti.version}version>
dependency>
<dependency>
<groupId>org.activitigroupId>
<artifactId>activiti-diagram-restartifactId>
<version>${activiti.version}version>
dependency>
二、定义流程
1.使用 activiti-modeler 绘制流程图(此插件集成方式请自行百度)
流程图创建参考此文章
有多种方式(classpath、InputStream、字符串)部署,这里使用InputStream方式。
/**流程引擎(核心对象),默认加载类路径下命名为activiti.cfg.xml*/
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
public void deployementProcessDefinitionByInputStream() throws FileNotFoundException {
//获取资源相对路径
String bpmnPath = "diagrams/helloworld.bpmn";
String pngPath = "diagrams/helloworld.png";
//读取资源作为一个输入流
FileInputStream bpmnfileInputStream = new FileInputStream(bpmnPath);
FileInputStream pngfileInputStream = new FileInputStream(pngPath);
//获取流程定义和部署对象相关的Service
Deployment deployment = processEngine.getRepositoryService()
.createDeployment()//创建部署对象
.addInputStream("helloworld.bpmn",bpmnfileInputStream)
.addInputStream("helloworld.png", pngfileInputStream)
.deploy();//完成部署
System.out.println("部署ID:"+deployment.getId());
System.out.println("部署时间:"+deployment.getDeploymentTime());
}
/**
* 启动流程
*
* @param procDefKey 流程定义KEY
* @param businessTable 业务表表名
* @param businessId 业务表编号
* @param title 流程标题,显示在待办任务标题
* @param vars 流程变量
* @return 流程实例ID
*/
public String startProcess(String procDefKey, String businessTable,
String businessId, String title, Map<String, Object> vars) {
String userId = UserUtils.getUser().getId();
// 用来设置启动流程的人员ID,引擎会自动把用户ID保存到activiti:initiator中
identityService.setAuthenticatedUserId(userId);
// 设置流程变量
if (vars == null) {
vars = Maps.newHashMap();
}
// 设置流程标题
if (StringUtils.isNotBlank(title)) {
vars.put("title", title);
}
// 启动流程
ProcessInstance procIns = runtimeService.startProcessInstanceByKey(procDefKey,
businessTable + ":" + businessId, vars);
// 更新业务表流程实例ID
Act act = new Act();
act.setBusinessTable(businessTable);// 业务表名
act.setBusinessId(businessId); // 业务表ID
act.setProcInsId(procIns.getId());
actDao.updateProcInsIdByBusinessId(act);
return act.getProcInsId();
}
//启动流程后,将流程实例id (procInsId) 保存到业务表中
//dao层
<update id="updateProcInsIdByBusinessId">
UPDATE ${businessTable} SET
proc_ins_id = #{procInsId}
WHERE id = #{businessId}
</update>
Map<String, Object> vars = Maps.newHashMap();
vars.put("pass", 1);//是否通过
...各种参数... 此处省略
/**
* 判断流程是否结束
*
* @param processInstanceId
* @return
*/
public boolean isEnd(String processInstanceId) {
ProcessInstance pi = runtimeService.createProcessInstanceQuery()//
.processInstanceId(processInstanceId)//使用流程实例ID查询
.singleResult();
if (pi == null) {
return true;
} else {
return false;
}
}
/**
* 删除部署的流程实例
* @param procInsId 流程实例ID
* @param deleteReason 删除原因,可为空
*/
public void deleteProcIns(String procInsId, String deleteReason) {
runtimeService.deleteProcessInstance(procInsId, deleteReason);
}
/**
* 提交任务, 并保存意见
*
* @param taskId 任务ID
* @param procInsId 流程实例ID,如果为空,则不保存任务提交意见
* @param comment 任务提交意见的内容
* @param title 流程标题,显示在待办任务标题
* @param vars 任务变量
*/
public void complete(String taskId, String procInsId, String comment,
String title, Map<String, Object> vars) {
// 添加意见
if (StringUtils.isNotBlank(procInsId) && StringUtils.isNotBlank(comment)) {
taskService.addComment(taskId, procInsId, comment);
}
// 设置流程变量
if (vars == null) {
vars = Maps.newHashMap();
}
// 设置流程标题
if (StringUtils.isNotBlank(title)) {
vars.put("title", title);
}
// 提交任务
taskService.complete(taskId, vars);
}
**注:**1、通过 taskId 提交任务:complete只执行一次,再次执行时,该任务已不存在,则无法再次提交,提交失败
2、任务已签收时,处理人与签收人需操持一致,否则任务提交失败
/**
* 签收任务
*
* @param taskId 任务ID
* @param userId 签收用户ID(用户登录名)
*/
public void claim(String taskId, String userId) {
taskService.claim(taskId, userId);
}
用户任务,即需要人来参与的任务,具体设计如下:
<userTask activiti:assignee="${user}" id="spr1" name="审批人1"/>
说明:activiti:assignee:设置任务处理人,指向流程变量“user"
// 流程变量中放入用户id即可
Map<String, Object> vars = Maps.newHashMap();
vars.put("user", "userIdxxxxx");
同一任务多人处理,且所有人处理完成后,流程再跳转到下一环节,具体设计如下:
在userTask节点内,添加multiInstanceLoopCharacteristics节点
<userTask activiti:assignee="${assignee}" activiti:exclusive="true" id="task2" name="会签审核">
<multiInstanceLoopCharacteristics activiti:collection="${users}" activiti:elementVariable="assignee" isSequential="false"/>
userTask>
**说明:**1、activiti:collection:设置用户集合,指向流程变量"users"(java中传入list数组)
2、activiti:elementVariable:设置遍历用户集合的单个对象变量名称,注:勿使用流程变量中的常用变更名
3、activiti:assignee:根据 activiti:elementVariable 中的变量名 设置任务的 办理人
4、isSequential = “false”,表示任务实例并行执行,“true“,表示顺序执行(一个接一个)
用户集合中,有一人不同意,则退出会签,流程实例进入下一环节
<userTask activiti:assignee="${assignee}" activiti:exclusive="true" id="task2" name="会签审核">
<multiInstanceLoopCharacteristics activiti:collection="${users}" activiti:elementVariable="assignee" isSequential="false">
<completionCondition>completionCondition>
multiInstanceLoopCharacteristics>
userTask>
说明:1、在multiInstanceLoopCharacteristics节点中,增加completionCondition节点,设置会签退出的条件
2、当流程变量pass 值为 0 时,退出会签环节,流程实例跳转到下一环节
用户集合中,达到 指定比例 处理后,则退出会签,流程实例进入下一环节
<userTask id="miTasks" name="My Task" activiti:assignee="${assignee}">
<multiInstanceLoopCharacteristics isSequential="false"
activiti:collection="${users}" activiti:elementVariable="assignee" >
<completionCondition>${nrOfCompletedInstances/nrOfInstances >= 0.6 }completionCondition>
multiInstanceLoopCharacteristics>
userTask>
需要用到监听器(暂略)…
@Autowired
private TaskService taskService;
/**
* 获取待办列表
*
* procDefKey 流程定义标识
* @return
*/
public List<Act> todoList(Act act) {
String userId = UserUtils.getUser().getId();
List<Act> result = new ArrayList<Act>();
// =============== 已经签收的任务 ===============
TaskQuery todoTaskQuery = taskService.createTaskQuery().taskAssignee(userId).active()
.includeProcessVariables().orderByTaskCreateTime().desc();
// 设置查询条件
if (StringUtils.isNotBlank(act.getProcDefKey())) {
todoTaskQuery.processDefinitionKey(act.getProcDefKey());
}
//开始时间
if (act.getBeginDate() != null) {
todoTaskQuery.taskCreatedAfter(act.getBeginDate());
}
//结束时间
if (act.getEndDate() != null) {
todoTaskQuery.taskCreatedBefore(act.getEndDate());
}
// 查询列表
List<Task> todoList = todoTaskQuery.list();
for (Task task : todoList) {
Act e = new Act();
e.setTask(task);
e.setVars(task.getProcessVariables());
e.setProcDef(ProcessDefCache.get(task.getProcessDefinitionId()));
e.setStatus("todo");
result.add(e);
}
// =============== 等待签收的任务 ===============
TaskQuery toClaimQuery = taskService.createTaskQuery().taskCandidateUser(userId)
.includeProcessVariables().active().orderByTaskCreateTime().desc();
// 设置查询条件
if (StringUtils.isNotBlank(act.getProcDefKey())) {
toClaimQuery.processDefinitionKey(act.getProcDefKey());
}
//开始时间
if (act.getBeginDate() != null) {
toClaimQuery.taskCreatedAfter(act.getBeginDate());
}
//结束时间
if (act.getEndDate() != null) {
toClaimQuery.taskCreatedBefore(act.getEndDate());
}
// 查询列表
List<Task> toClaimList = toClaimQuery.list();
for (Task task : toClaimList) {
Act e = new Act();
e.setTask(task);
e.setVars(task.getProcessVariables());
e.setProcDef(ProcessDefCache.get(task.getProcessDefinitionId()));
e.setStatus("claim");
result.add(e);
}
return result;
}
/**
* 获取已办任务
*
* @param page procDefKey 流程定义标识
* @return
*/
public List<Act> historicList(Page<Act> page, Act act) {
String userId = UserUtils.getUser().getId();
HistoricTaskInstanceQuery histTaskQuery = historyService.createHistoricTaskInstanceQuery().taskAssignee(userId).finished()
.includeProcessVariables().orderByHistoricTaskInstanceEndTime().desc();
// 设置查询条件
if (StringUtils.isNotBlank(act.getProcDefKey())) {
histTaskQuery.processDefinitionKey(act.getProcDefKey());
}
if (act.getBeginDate() != null) {
histTaskQuery.taskCompletedAfter(act.getBeginDate());
}
if (act.getEndDate() != null) {
histTaskQuery.taskCompletedBefore(act.getEndDate());
}
// 查询列表
List<HistoricTaskInstance> histList = histTaskQuery.listPage(page.getFirstResult(), page.getMaxResults());
//处理分页问题
List<Act> actList = Lists.newArrayList();
for (HistoricTaskInstance histTask : histList) {
Act e = new Act();
e.setHistTask(histTask);
e.setVars(histTask.getProcessVariables());
//e.setProcDef(ProcessDefCache.get(histTask.getProcessDefinitionId()));
e.setStatus("finish");
actList.add(e);
}
return actList;
}
生成png格式图片
public void graphHistoryProcessInstance(String processInstanceId, HttpServletResponse response) throws Exception {
Command<InputStream> cmd = new HistoryProcessInstanceDiagramCmd(processInstanceId);
InputStream is = managementService.executeCommand(cmd);
response.setContentType("image/png");
int len = 0;
byte[] b = new byte[1024];
while ((len = is.read(b, 0, 1024)) != -1) {
response.getOutputStream().write(b, 0, len);
}
is.close();
}
/**
* 获取流转历史列表
*
* @param procInsId 流程实例
* @param startAct 开始活动节点名称
* @param endAct 结束活动节点名称
*/
public List<Act> histoicFlowList(String procInsId, String startAct, String endAct) {
List<Act> actList = Lists.newArrayList();
List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery().processInstanceId(procInsId) .orderByHistoricActivityInstanceStartTime().asc()
.orderByHistoricActivityInstanceEndTime().asc().list();
boolean start = false;
Map<String, Integer> actMap = Maps.newHashMap();
for (int i = 0; i < list.size(); i++) {
HistoricActivityInstance histIns = list.get(i);
// 过滤开始节点前的节点
if (StringUtils.isNotBlank(startAct) && startAct.equals(histIns.getActivityId())) {
start = true;
}
if (StringUtils.isNotBlank(startAct) && !start) {
continue;
}
// 只显示开始节点和结束节点,并且执行环节不为空的任务
if (StringUtils.isNotBlank(histIns.getActivityName()) || "startEvent".equals(histIns.getActivityType()) || "endEvent".equals(histIns.getActivityType())) {
// 给节点增加一个序号
Integer actNum = actMap.get(histIns.getActivityId());
if (actNum == null) {
actMap.put(histIns.getActivityId(), actMap.size());
}
Act e = new Act();
e.setHistIns(histIns);
// 获取流程发起人名称
if ("startEvent".equals(histIns.getActivityType())) {
List<HistoricProcessInstance> il = historyService.createHistoricProcessInstanceQuery().processInstanceId(procInsId)
.orderByProcessInstanceStartTime().asc().list();
if (il.size() > 0) {
if (StringUtils.isNotBlank(il.get(0).getStartUserId())) {
User user = UserUtils.get(il.get(0).getStartUserId());
if (user != null) {
e.setAssignee(histIns.getAssignee());
e.setAssigneeName(user.getName());
}
}
}
}
// 获取任务执行人名称
if (StringUtils.isNotEmpty(histIns.getAssignee())) {
User user = UserUtils.get(histIns.getAssignee());
if (user != null) {
e.setAssignee(histIns.getAssignee());
e.setAssigneeName(user.getName());
}
}
// 获取意见评论内容
if (StringUtils.isNotBlank(histIns.getTaskId())) {
List<Comment> commentList = taskService.getTaskComments(histIns.getTaskId());
if (commentList.size() > 0) {
e.setComment(commentList.get(0).getFullMessage());
}
}
actList.add(e);
}
// 过滤结束节点后的节点
if (StringUtils.isNotBlank(endAct) && endAct.equals(histIns.getActivityId())) {
boolean bl = false;
Integer actNum = actMap.get(histIns.getActivityId());
// 该活动节点,后续节点是否在结束节点之前,在后续节点中是否存在
for (int j = i + 1; j < list.size(); j++) {
HistoricActivityInstance hi = list.get(j);
Integer actNumA = actMap.get(hi.getActivityId());
if ((actNumA != null && actNumA < actNum) || StringUtils.equals(hi.getActivityId(), histIns.getActivityId())) {
bl = true;
}
}
if (!bl) {
break;
}
}
}
return actList;
}
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.*.common.persistence.BaseEntity;
import com.*.common.utils.StringUtils;
import com.*.common.utils.TimeUtils;
import com.*.modules.act.utils.Variable;
/**
* 工作流Entity
*
*/
public class Act extends BaseEntity<Act> {
private static final long serialVersionUID = 1L;
private String taskId; // 任务编号
private String taskName; // 任务名称
private String taskDefKey; // 任务定义Key(任务环节标识)
private String procInsId; // 流程实例ID
private String procDefId; // 流程定义ID
private String procDefKey; // 流程定义Key(流程定义标识)
private String businessTable; // 业务绑定Table
private String businessId; // 业务绑定ID
private String title; // 任务标题
private String status; // 任务状态(todo/claim/finish)
// private String procExecUrl; // 流程执行(办理)RUL
private String comment; // 任务意见
private String flag; // 意见状态
private Task task; // 任务对象
private ProcessDefinition procDef; // 流程定义对象
private ProcessInstance procIns; // 流程实例对象
private HistoricTaskInstance histTask; // 历史任务
private HistoricActivityInstance histIns; //历史活动任务
private String assignee; // 任务执行人编号
private String assigneeName; // 任务执行人名称
private Variable vars; // 流程变量
// private Variable taskVars; // 流程任务变量
private Date beginDate; // 开始查询日期
private Date endDate; // 结束查询日期
private List<Act> list; // 任务列表
private int isView;//是否是查看已办任务
public Act() {
super();
}
public String getTaskId() {
if (taskId == null && task != null) {
taskId = task.getId();
}
return taskId;
}
public void setTaskId(String taskId) {
this.taskId = taskId;
}
public String getTaskName() {
if (taskName == null && task != null) {
taskName = task.getName();
}
return taskName;
}
public void setTaskName(String taskName) {
this.taskName = taskName;
}
public String getTaskDefKey() {
if (taskDefKey == null && task != null) {
taskDefKey = task.getTaskDefinitionKey();
}
return taskDefKey;
}
public void setTaskDefKey(String taskDefKey) {
this.taskDefKey = taskDefKey;
}
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
public Date getTaskCreateDate() {
if (task != null) {
return task.getCreateTime();
}
return null;
}
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
public Date getTaskEndDate() {
if (histTask != null) {
return histTask.getEndTime();
}
return null;
}
@JsonIgnore
public Task getTask() {
return task;
}
public void setTask(Task task) {
this.task = task;
}
@JsonIgnore
public ProcessDefinition getProcDef() {
return procDef;
}
public void setProcDef(ProcessDefinition procDef) {
this.procDef = procDef;
}
public String getProcDefName() {
return procDef.getName();
}
@JsonIgnore
public ProcessInstance getProcIns() {
return procIns;
}
public void setProcIns(ProcessInstance procIns) {
this.procIns = procIns;
if (procIns != null && procIns.getBusinessKey() != null) {
String[] ss = procIns.getBusinessKey().split(":");
setBusinessTable(ss[0]);
setBusinessId(ss[1]);
}
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
@JsonIgnore
public HistoricTaskInstance getHistTask() {
return histTask;
}
public void setHistTask(HistoricTaskInstance histTask) {
this.histTask = histTask;
}
@JsonIgnore
public HistoricActivityInstance getHistIns() {
return histIns;
}
public void setHistIns(HistoricActivityInstance histIns) {
this.histIns = histIns;
}
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
public Date getBeginDate() {
return beginDate;
}
public void setBeginDate(Date beginDate) {
this.beginDate = beginDate;
}
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
public Date getEndDate() {
return endDate;
}
public void setEndDate(Date endDate) {
this.endDate = endDate;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public String getFlag() {
return flag;
}
public void setFlag(String flag) {
this.flag = flag;
}
public String getProcDefId() {
if (procDefId == null && task != null) {
procDefId = task.getProcessDefinitionId();
}
return procDefId;
}
public void setProcDefId(String procDefId) {
this.procDefId = procDefId;
}
public String getProcInsId() {
if (procInsId == null && task != null) {
procInsId = task.getProcessInstanceId();
}
return procInsId;
}
public void setProcInsId(String procInsId) {
this.procInsId = procInsId;
}
public String getBusinessId() {
return businessId;
}
public void setBusinessId(String businessId) {
this.businessId = businessId;
}
public String getBusinessTable() {
return businessTable;
}
public void setBusinessTable(String businessTable) {
this.businessTable = businessTable;
}
public String getAssigneeName() {
return assigneeName;
}
public void setAssigneeName(String assigneeName) {
this.assigneeName = assigneeName;
}
public String getAssignee() {
if (assignee == null && task != null) {
assignee = task.getAssignee();
}
return assignee;
}
public void setAssignee(String assignee) {
this.assignee = assignee;
}
public List<Act> getList() {
return list;
}
public void setList(List<Act> list) {
this.list = list;
}
public Variable getVars() {
return vars;
}
public void setVars(Variable vars) {
this.vars = vars;
}
/**
* 通过Map设置流程变量值
*
* @param map
*/
public void setVars(Map<String, Object> map) {
this.vars = new Variable(map);
}
/**
* 获取流程定义KEY
*
* @return
*/
public String getProcDefKey() {
if (StringUtils.isBlank(procDefKey) && StringUtils.isNotBlank(procDefId)) {
procDefKey = StringUtils.split(procDefId, ":")[0];
}
return procDefKey;
}
public void setProcDefKey(String procDefKey) {
this.procDefKey = procDefKey;
}
/**
* 获取过去的任务历时
*
* @return
*/
public String getDurationTime() {
if (histIns != null && histIns.getDurationInMillis() != null) {
return TimeUtils.toTimeString(histIns.getDurationInMillis());
}
return "";
}
/**
* 是否是一个待办任务
*
* @return
*/
public boolean isTodoTask() {
return "todo".equals(status) || "claim".equals(status);
}
/**
* 是否是已完成任务
*
* @return
*/
public boolean isFinishTask() {
return "finish".equals(status) || StringUtils.isBlank(taskId);
}
@Override
public void preInsert() {
}
@Override
public void preUpdate() {
}
public int getIsView() {
return isView;
}
public void setIsView(int isView) {
this.isView = isView;
}
}
参考链接:https://blog.csdn.net/qq_16605855/article/details/102853683?spm=1001.2014.3001.5506
<definitions id="myProcesses"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://schema.omg.org/spec/BPMN/2.0 BPMN20.xsd"
xmlns="http://schema.omg.org/spec/BPMN/2.0"
typeLanguage="http://www.w3.org/2001/XMLSchema"
expressionLanguage="http://www.w3.org/1999/XPath"
targetNamespace="http://jbpm.org/example/bpmn2">
<process id="My business processs" name="myBusinessProcess">
...
process>
<definitions>
如上代码是流程定义xml的基本代码 definitions是根节点process 是每个流程的起点 必须有一个id和name 如果为process元素定义了name,它会被用做流程的key 如果没有指定name,id会被用做key。 注意key的规则与jPDL一样: 空格和非字母数字的字符会被下划线代替。
?事件:空启动事件
<startEvent id="start" name="myStart" />
空结束事件
<endEvent id="end" name="myEnd" />
最简单的流程定义
<process id="noneStartEndEvent" name="BPMN2 Example none start and end event">
<startEvent id="start" />
<sequenceFlow id="flow1" name="fromStartToEnd"
sourceRef="start" targetRef="end" />
<endEvent id="end" name="End" />
process>
终止结束事件会结束整个流程实例,而空结束事件只会结束当前流程路径。
<endEvent id="terminateEnd" name="myTerminateEnd">
<terminateEventDefinition/>
endEvent>
连接线
<sequenceFlow id="myFlow" name="My Flow"
sourceRef="sourceId" targetRef="targetId" />
<sequenceFlow id=....>
<conditionExpression xsi:type="tFormalExpression">${amount >= 500}conditionExpression>
sequenceFlow>
默认顺序流
认顺序流只会在活动或网关的 所有其他外向顺序流的condition条件为false时才会使用
默认顺序流通过指定活动或网关的 ‘default’ 属性 来使用。
网关是用来控制流程中的流向的
没有定义条件的顺序流会永远被选择
<parallelGateway id="myJoin" name="My synchronizing join" gatewayDirection="converging" />
gatewayDirection属性:
一个流程中的唯一决策。 会有一个外向顺序流被使用?如果多个条件 执行为true,第一个遇到的就会被使用
<process id="exclusiveGateway" name="BPMN2 Example exclusive gateway">
<startEvent id="start" />
<sequenceFlow id="flow1" name="fromStartToExclusiveGateway"
sourceRef="start" targetRef="decideBasedOnAmountGateway" />
<exclusiveGateway id="decideBasedOnAmountGateway" name="decideBasedOnAmount" />
<sequenceFlow id="flow2" name="fromGatewayToEndNotEnough"
sourceRef="decideBasedOnAmountGateway" targetRef="endNotEnough">
<conditionExpression xsi:type="tFormalExpression">
${amount < 100}
conditionExpression>
sequenceFlow>
<sequenceFlow id="flow3" name="fromGatewayToEnEnough"
sourceRef="decideBasedOnAmountGateway" targetRef="endEnough">
<conditionExpression xsi:type="tFormalExpression">
${amount <= 500 && amount >= 100}
conditionExpression>
sequenceFlow>
<sequenceFlow id="flow4" name="fromGatewayToMoreThanEnough"
sourceRef="decideBasedOnAmountGateway" targetRef="endMoreThanEnough">
<conditionExpression xsi:type="tFormalExpression">
${amount > 500}
conditionExpression>
sequenceFlow>
<endEvent id="endNotEnough" name="not enough" />
<endEvent id="endEnough" name="enough" />
<endEvent id="endMoreThanEnough" name="more than enough" />
process>
网关所需变量的提供 Map
xor网关的默认走向 (如果所有条件都是false)
<exclusiveGateway id="decision" name="decideBasedOnAmountAndBankType" default="myFlow"/>
<sequenceFlow id="myFlow" name="fromGatewayToStandard"
sourceRef="decision" targetRef="standard">
sequenceFlow>
并行网关用来切分或同步相关的进入或外出 顺序流。
一进多出的流程设计:并行切分
多进一处的流程设计:并行归并
基本定义
<process id="parallelGateway" name="BPMN2 example parallel gatewar">
//空启动事件
<startEvent id="Start" />
//连接空启动时间和并行网关
<sequenceFlow id="flow1" name="fromStartToSplit"
sourceRef="Start"
targetRef="parallelGatewaySplit" />
//并行网关1
<parallelGateway id="parallelGatewaySplit" name="Split"
gatewayDirection="diverging"/>
//并行网关1连接节点1
<sequenceFlow id="flow2a" name="Leg 1"
sourceRef="parallelGatewaySplit"
targetRef="prepareShipment" />
//定义节点1
<userTask id="prepareShipment" name="Prepare shipment"
implementation="other" />
//节点1连接并行网关2
<sequenceFlow id="flow2b" name="fromPrepareShipmentToJoin"
sourceRef="prepareShipment"
targetRef="parallelGatewayJoin" />
//节点2连接并行网关1
<sequenceFlow id="flow3a" name="Leg 2"
sourceRef="parallelGatewaySplit"
targetRef="billCustomer" />
//定义节点2
<userTask id="billCustomer" name="Bill customer"
implementation="other" />
//节点2连接并行网关2
<sequenceFlow id="flow3b" name="fromLeg2ToJoin"
sourceRef="billCustomer"
targetRef="parallelGatewayJoin" />
//定义并行网关2
<parallelGateway id="parallelGatewayJoin" name="Join"
gatewayDirection="converging"/>
//并行网关2连接空结束事件
<sequenceFlow id="flow4"
sourceRef="parallelGatewayJoin"
targetRef="End">
sequenceFlow>
<endEvent id="End" name="End" />
process>
进行“条件性”切分或汇聚顺序流。
<process id="inclusiveGateway" name="BPMN2 Example inclusive gateway">
<startEvent id="start" />
<sequenceFlow id="flow1" sourceRef="start" targetRef="inclusiveGatewaySplit" />
<inclusiveGateway id="inclusiveGatewaySplit" default="flow3"/>
<sequenceFlow id="flow2" sourceRef="inclusiveGatewaySplit" targetRef="largeDeposit">
<conditionExpression xsi:type="tFormalExpression">${cash > 10000}conditionExpression>
sequenceFlow>
<sequenceFlow id="flow3" sourceRef="inclusiveGatewaySplit" targetRef="standardDeposit" />
<sequenceFlow id="flow4" sourceRef="inclusiveGatewaySplit" targetRef="foreignDeposit">
<conditionExpression xsi:type="tFormalExpression">${bank == 'foreign'}conditionExpression>
sequenceFlow>
<userTask id="largeDeposit" name="Large deposit" />
<sequenceFlow id="flow5" sourceRef="largeDeposit" targetRef="inclusiveGatewayMerge" />
<userTask id="standardDeposit" name="Standard deposit" />
<sequenceFlow id="flow6" sourceRef="standardDeposit" targetRef="inclusiveGatewayMerge" />
<userTask id="foreignDeposit" name="Foreign deposit" />
<sequenceFlow id="flow7" sourceRef="foreignDeposit" targetRef="inclusiveGatewayMerge" />
<inclusiveGateway id="inclusiveGatewayMerge" />
<sequenceFlow id="flow8" sourceRef="inclusiveGatewayMerge" targetRef="theEnd" />
<endEvent id="theEnd" />
process>
一个新人工任务就会被创建,交给用户的任务列表
<userTask id="myTask" name="My task" />
使用多种实现(WebService, WS-humantask,等等)
<userTask id="myTask" name="My task">
<potentialOwner resourceRef="manager" jbpm:type="group">
<resourceAssignmentExpression>
<formalExpression>managementformalExpression>
resourceAssignmentExpression>
potentialOwner>
userTask>
resourceAssignmentExpression 分配任务
potentialOwner 候选人
jbpm:type="group" 定义这是一个用户组的分配方式 如果删除了这个属性,就会默认使用用户组的语法
jbpm:type="user" 分配方式是候选用户
<userTask id="myTask" name="My User task">
<potentialOwner resourceRef="employee" jbpm:type="user">
<resourceAssignmentExpression>
<formalExpression>peterformalExpression>
resourceAssignmentExpression>
potentialOwner>
userTask>
Peter将可以看到任务,因为他是这个任务的候选用户。
List<Task> tasks = taskService.createTaskQuery().candidate("peter").list();
用户组的定义
identityService.createGroup("management");
identityService.createUser("peter", "Peter", "Pan");
identityService.createMembership("peter", "management");
identityService.createUser("mary", "Mary", "Littlelamb");
identityService.createMembership("mary", "management");
所在用户组的人能看到这个任务
// Peter and Mary are both part of management, so they both should see the task
List<Task> tasks = taskService.findGroupTasks("peter");
assertEquals(1, tasks.size());
tasks = taskService.findGroupTasks("mary");
assertEquals(1, tasks.size());
// Mary claims the task
Task task = tasks.get(0);
taskService.takeTask(task.getId(), "mary");
assertNull(taskService.createTaskQuery().candidate("peter").uniqueResult());
taskService.completeTask(task.getId());
assertProcessInstanceEnded(processInstance);
human performer 一个任务直接分配给一个人, 组,角色时
<userTask id="myTask" name="My User task">
<humanPerformer resourceRef="employee">
<resourceAssignmentExpression>
<formalExpression>mary</formalExpression>
</resourceAssignmentExpression>
</humanPerformer>
</userTask>
任务列表中看到这个任务:
List<Task> tasks = taskService.findPersonalTasks("mary");
因为任务分配已经完成,通过使用 formalExpression,它也可以定义表达式 在运行期解析、
比如,如果流程变量'user'被定义了,然后,它可以用在表达式中。
<userTask id="myTask" name="My User task">
<humanPerformer resourceRef="employee">
<resourceAssignmentExpression>
<formalExpression>${user}</formalExpression>
</resourceAssignmentExpression>
</humanPerformer>
</userTask>
Service Task是一个自动活动,它会调用一些服务, 比如web service,java service等等。
<serviceTask id="MyServiceTask" name="My service task"
implementation="Other" operationRef="myOperation" />
implementation 服务的类型 ->WebService, Other或者Unspecified
<interface id="myInterface"
name="org.jbpm.MyJavaServicek">
<operation id="myOperation2" name="myMethod">
<inMessageRef>inputMessageinMessageRef>
<outMessageRef>outputMessageoutMessageRef>
bpmn:operation>
interface>
备注:每个操作都至少有一个 输入信息,并且 最多有一个输出信息。
<message id="inputMessage" name="input message" structureRef="myItemDefinition1" />
脚本任务时一个自动活动,当到达这个任务的时候 流程引擎会执行一个脚本。
<scriptTask id="scriptTask" name="Script Task" scriptLanguage="bsh">
<script>
for(int i=0; i < input.length; i++){
System.out.println(input[i] + " x 2 = " + (input[i]*2));
}]]>
script>
scriptTask>
允许指定 scriptLanguage和script
一个由外部人员执行的任务,但是没有指定是 一个BPM系统或是一个服务会被调用
<manualTask id="myManualTask" name="Call customer" />
receive task是一个任务会等到外部消息的到来。
<receiveTask id="receiveTask" name="wait" />
<process id="embeddedSubprocess">
<startEvent id="theStart" />
<sequenceFlow id="flow1" sourceRef="theStart" targetRef="receiveOrder" />
<receiveTask name="Receive order" id="receiveOrder" />
<sequenceFlow id="flow2" sourceRef="receiveOrder" targetRef="checkCreditSubProcess" />
<subProcess id="checkCreditSubProcess" name="Credit check">
...
subProcess>
<sequenceFlow id="flow9" sourceRef="checkCreditSubProcess" targetRef="theEnd" />
<endEvent id="theEnd" />
process>
<startEvent name="Every Monday morning" id="myStart">
<timerEventDefinition/>
startEvent>
定义方法:
1.timeDate
<startEvent id="myStartEvent" >
<timerEventDefinition>
<timeDate>10/10/2099 00:00:00timeDate>
timerEventDefinition>
startEvent>
2.timeCycle (延迟)
<startEvent id="myStartEvent" >
<timerEventDefinition>
<timeCycle>5 hourstimeCycle>
timerEventDefinition>
startEvent>
3.表达式 (每周五23点)
<startEvent id="myStartEvent" >
<timerEventDefinition>
<timeCycle>0 0 23 ? * FRItimeCycle>
timerEventDefinition>
startEvent>
中间事件用来表示在流程执行过程中发生的事件(比如, 在流程启动之后,在它完成之前)。
定时器事件,触发事件,传播事件
?中间事件既可以抛出也可以捕获:
内部定时器事件用来表示一个流程的延迟。 直接的用例是收集数据, 只在没有人工作的晚上执行大量的逻辑,等等。
————————————————
版权声明:本文为CSDN博主「程序员小强」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_38011415/article/details/101127222
数据库设计规则
1.1 ACT_EVT_LOG(act_evt_log) > 事件日志表
1.2 ACT_GE_PROPERTY > 属性表
属性抽象成为 key-value对,使用该表来保存这些属性。
1.3 ACT_GE_BYTEARRAY > 资源表
注:用来保存部署文件的大文本数据。
保存流程定义图片和xml、Serializable(序列化)的变量,即保存所有二进制数据,特别注意类路径部署时候,不要把svn等隐藏文件或者其他与流程无关的文件也一起部署到该表中,会造成一些错误(可能导致流程定义无法删除)
2.1 ACT_RE_DEPLOYMENT > 部署数据表
2.2 ACT_RE_MODEL > 模型信息表
2.4 ACT_PROCDEF_INFO > 流程定义扩展表
3.1 ACT_ID_USER > 用户信息表
3.2 ACT_ID_INFO > 用户扩展信息表
3.3 ACT_ID_GROUP > 用户组表
3.4 ACT_ID_MEMBERSHIP > 用户与组关系表
4.1 ACT_RU_EXECUTION > 运行时流程实例执行实例表
4.2 ACT_RU_TASK > 用户任务表
4.3 ACT_RU_VARIABLE > 流程参数数据表
4.4 ACT_RU_IDENTITYLINK > 参与者信息表
4.5 ACT_RU_EVENT_SUBSCR > 运行时事件表
4.6 ACT_RU_JOB > 运行作业信息表
4.7 ACT_RU_DEADLETTER_JOB > 无法执行工作表
4.8 ACT_RU_SUSPENDED_JOB > 暂停表
4.9 ACT_RU_TIMER_JOB > 定时器表
5.1 ACT_HI_PROCINST 历史流程实例信息表
与正在运行中的流程实例表act_ru_execution正好对应
5.2 ACT_HI_DETAIL 流程明细表
5.3 ACT_HI_ACTINS 历史节点表
5.4 ACT_HI_TASKINST 历史任务流程实例信息
5.5 ACT_HI_IDENTITYLINK > 历史流程人员表
5.6 ACT_HI_COMMENT > 历史审批意见表
5.7 ACT_HI_VARINST > 历史变量信息
与运行中的参数存储表 act_ru_variable正好对应
————————————————
版权声明:本文为CSDN博主「coding的大博哥」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ITWANGBOIT/article/details/106507332
前言
Activiti的参数有两种类型,一类是流程参数,一类是任务参数;流程参数是通过RuntimeService服务组件设置,在设置的过程中需要传入执行流ID;任务参数是通过TaskService服务组件设置,在设置的过程中需要传入任务ID;所以我们可以形象的认为流程参数是和执行流绑定到了一起,任务参数是和任务绑定到了一起。任务参数可以查看这篇文章《Activiti任务参数的设置方式和作用域》
1:启动流程实例时设置参数,用runtimeService.startProcessInstanceById(流程id,参数map);此种方式设置的流程参数相当于设置到了主执行流中。
2:在流程启动之后,用runtimeService.setVariable(执行流Id,参数名,参数值)或者runtimeService.setVariableLocal(执行流Id,参数名,参数值)的方式设置参数;此种方式设置参数依靠执行流,可以是主执行流,也可以是子执行流。
1:流程实例中只有一个执行流时,即只有主执行流时;无论是在启动流程时设置流程参数,还是在启动后通过setVariable或者通过setVariableLocal的方式来设置流程参数,则都可以通过getVariable和getVariableLocal的方式获取到设置的流程参数;
2:流程实例中除了主执行流外,还有子执行流时;此时情况比较复杂,分开描述:
(1)当在主执行流中设置流程参数,无论以何种方式设置,则在主执行流中通过任何方式都可以获取到,在所有子执行流中也可以通过getVariable的方式获取到;
(2)当在某个子执行流中设置流程参数时,如果是通过setVariable方式设置,则在所有子执行流中都可以通过getVariable方式获取到,在主执行流中可以通过任何方式获取到;
(3)当在某个子执行流中设置流程参数时,如果是以setVariableLocal方式设置,则在当前子执行流中可以通过任何方式获取到,在其他子执行流中不可以获取到,在主执行流中也不可以获取到,因为参数的作用域仅仅局限于当前设置的子执行流中,而且子执行流执行结束之后,参数就会失效。
3:流程参数作用域的个人理解
(1)在一个流程实例中,流程参数的作用域可以理解为有两类区域,一类共有区域(只有一个共有区域,所有执行流共享),一类私有区域(每个子执行流都有自己的私有区域);
(2)主执行流只能操作共有区域,无论是通过setVariable和getVariable,还是通过setVariableLocal和getVariableLocal;操作的都是共享的那个共有区域;
(3)子执行流即可以操作共有区域(setVariable和getVariable),也可以操作自己的私有区域(setVariableLocal和getVariableLocal)(子执行流的getVariable也可以操作自己的私有区域);
(4)主执行流无论通过何种方式设置参数,都是将参数设置到共有区域中;主执行流无论通过何种方式获取都是从共有区域中获取参数;
(5)子执行流通过setVariable设置的参数,被设置到了共有区域;子执行流通过getVariable方式是从共有区域里获取参数(也会从自己的私有区域获取参数);
(6)子执行流通过setVariableLocal设置的参数,被设置到了自己的私有区域里;子执行流通过getVariableLocal从自己的私有区域里获取参数。
4:流程参数在数据库中的表现形式
(1)现象
(2)总结
————————————————
版权声明:本文为CSDN博主「余张的故事」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/fututadeyoushang/article/details/82698087
流程启动方式
activiti的流程启动总结来说有四种启动方式,
根据processDefinitionKey启动也就是根据流程定义文件的key启动,是activiti最常用的启动方式。
示例
流程定义文件,key为my-process
<process id="my-process">
<startEvent id="start" />
<sequenceFlow id="flow1" sourceRef="start" targetRef="someTask" />
<userTask id="someTask" name="Activiti is awesome!" />
<sequenceFlow id="flow2" sourceRef="someTask" targetRef="end" />
<endEvent id="end" />
process>
Java代码
@Test
@org.activiti.engine.test.Deployment(resources = "my-process.bpmn20.xml")
public void testStartProcessInstanceByKey() {
RuntimeService runtimeService = activitiRule.getRuntimeService();
Map<String, Object> map = Maps.newHashMap();
map.put("name", "zhangxingr");
map.put("sex", "man");
map.put("age", "21");
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("my-process", map);
logger.info("processInstance = {}", processInstance);
}
示例
Java代码
@Test
@org.activiti.engine.test.Deployment(resources = "my-process.bpmn20.xml")
public void testStartProcessInstanceById() {
RuntimeService runtimeService = activitiRule.getRuntimeService();
Map<String, Object> map = Maps.newHashMap();
map.put("name", "zhangxingr");
map.put("sex", "man");
map.put("age", "21");
ProcessDefinition processDefinition = activitiRule.getRepositoryService()
.createProcessDefinitionQuery().singleResult();
ProcessInstance processInstance = runtimeService
.startProcessInstanceById(processDefinition.getId(), map);
logger.info("processInstance = {}, process'key = {}, process'name = {}",
processInstance, processInstance.getProcessDefinitionKey(),
processInstance.getName());
}
根据message启动就要复杂一些,需要改动一下流程定义文件的startEvent,增加messageEventDefinition。
示例
流程定义文件
<message id="messageStart" name="my-message"/>
<process id="my-process">
<startEvent id="start">
<messageEventDefinition messageRef="messageStart"/>
startEvent>
<sequenceFlow id="flow1" sourceRef="start" targetRef="someTask" />
<userTask id="someTask" name="Activiti is awesome!" />
<sequenceFlow id="flow2" sourceRef="someTask" targetRef="end" />
<endEvent id="end" />
process>
Java代码
@Test
@org.activiti.engine.test.Deployment(resources = "my-process-message.bpmn20.xml")
public void testMessageStart() {
RuntimeService runtimeService = activitiRule.getRuntimeService();
ProcessInstance processInstance = runtimeService
.startProcessInstanceByMessage("my-message");
logger.info("processInstance = {}", processInstance);
}
根据message启动最终其实还是会走到用processDefinitionId来启动。。。
源码解析
public ProcessInstance execute(CommandContext commandContext) {
if (messageName == null) {
throw new ActivitiIllegalArgumentException("Cannot start process instance by message: message name is null");
}
MessageEventSubscriptionEntity messageEventSubscription = commandContext.getEventSubscriptionEntityManager().findMessageStartEventSubscriptionByName(messageName, tenantId);
if (messageEventSubscription == null) {
throw new ActivitiObjectNotFoundException("Cannot start process instance by message: no subscription to message with name '" + messageName + "' found.", MessageEventSubscriptionEntity.class);
}
String processDefinitionId = messageEventSubscription.getConfiguration();
if (processDefinitionId == null) {
throw new ActivitiException("Cannot start process instance by message: subscription to message with name '" + messageName + "' is not a message start event.");
}
DeploymentManager deploymentCache = commandContext.getProcessEngineConfiguration().getDeploymentManager();
ProcessDefinition processDefinition = deploymentCache.findDeployedProcessDefinitionById(processDefinitionId);
if (processDefinition == null) {
throw new ActivitiObjectNotFoundException("No process definition found for id '" + processDefinitionId + "'", ProcessDefinition.class);
}
ProcessInstanceHelper processInstanceHelper = commandContext.getProcessEngineConfiguration().getProcessInstanceHelper();
ProcessInstance processInstance = processInstanceHelper.createAndStartProcessInstanceByMessage(processDefinition, messageName, processVariables, transientVariables);
return processInstance;
}
从上面可以看到,最终还是生成了一个processDefinitionId然后调createAndStartProcessInstanceByMessage来启动,具体可以去跟下源码。
Java代码
@Test
@org.activiti.engine.test.Deployment(resources = "my-process.bpmn20.xml")
public void testProcessInstanceBuilder() {
RuntimeService runtimeService = activitiRule.getRuntimeService();
Map<String, Object> map = Maps.newHashMap();
map.put("name", "zhangxingr");
map.put("sex", "man");
map.put("age", "21");
ProcessInstanceBuilder builder = runtimeService.createProcessInstanceBuilder();
ProcessInstance processInstance = builder.processDefinitionKey("my-process")
.businessKey("businessKey001")
.name("我的流程实例")
.start();
logger.info("processInstance = {}", processInstance);
}
至此,我们的编程大杂烩(二)
就告一段落了。喜欢我的话可以关注我的微信公众号我爱学习呀嘻嘻
,不定期分享各类资源哦。
编程大杂烩(三)
编程大杂烩(四)
编程大杂烩(一)