首页
留言
导航
统计
Search
1
追番推荐!免费看动漫的网站 - 支持在线观看和磁力下载
2,510 阅读
2
推荐31个docker应用,每一个都很实用
1,311 阅读
3
PVE自动启动 虚拟机 | 容器 顺序设置及参数说明
931 阅读
4
一条命令,永久激活!Office 2024!
618 阅读
5
优选 Cloudflare 官方 / 中转 IP
490 阅读
默认分类
服务器
宝塔
VPS
Docker
OpenWRT
Nginx
群晖
前端编程
Vue
React
Angular
NodeJS
uni-app
后端编程
Java
Python
SpringBoot
SpringCloud
流程引擎
检索引擎
Linux
CentOS
Ubuntu
Debian
数据库
Redis
MySQL
Oracle
虚拟机
VMware
VirtualBox
PVE
Hyper-V
计算机
网络技术
网站源码
主题模板
登录
Search
标签搜索
Java
小程序
Redis
SpringBoot
docker
Typecho
Cloudflare
docker部署
虚拟机
WordPress
群晖
uni-app
CentOS
Vue
Java类库
Linux命令
防火墙配置
Mysql
脚本
Nginx
微醺
累计撰写
264
篇文章
累计收到
11
条评论
首页
栏目
默认分类
服务器
宝塔
VPS
Docker
OpenWRT
Nginx
群晖
前端编程
Vue
React
Angular
NodeJS
uni-app
后端编程
Java
Python
SpringBoot
SpringCloud
流程引擎
检索引擎
Linux
CentOS
Ubuntu
Debian
数据库
Redis
MySQL
Oracle
虚拟机
VMware
VirtualBox
PVE
Hyper-V
计算机
网络技术
网站源码
主题模板
页面
留言
导航
统计
搜索到
43
篇与
的结果
2023-02-13
详解 Java8 Stream 用法
Java8(jdk1.8)的新特性主要是 Lambda 表达式和流,当流和 Lambda 表达式结合起来一起使用时,因为流申明式处理数据集合的特点,可以让代码变得简洁易读如果有一个需求,需要对数据库查询到的菜肴进行一个处理:筛选出卡路里小于 400 的菜肴对筛选出的菜肴进行一个排序获取排序后菜肴的名字菜肴:Dish.javapublic class Dish { private String name; private boolean vegetarian; private int calories; private Type type; // getter and setter } Java8 以前的实现方式private List<String> beforeJava7(List<Dish> dishList) { List<Dish> lowCaloricDishes = new ArrayList<>(); //1.筛选出卡路里小于400的菜肴 for (Dish dish : dishList) { if (dish.getCalories() < 400) { lowCaloricDishes.add(dish); } } //2.对筛选出的菜肴进行排序 Collections.sort(lowCaloricDishes, new Comparator<Dish>() { @Override public int compare(Dish o1, Dish o2) { return Integer.compare(o1.getCalories(), o2.getCalories()); } }); //3.获取排序后菜肴的名字 List<String> lowCaloricDishesName = new ArrayList<>(); for (Dish d : lowCaloricDishes) { lowCaloricDishesName.add(d.getName()); } return lowCaloricDishesName; } Java8 之后的实现方式 private List<String> afterJava8(List<Dish> dishList) { return dishList.stream() .filter(d -> d.getCalories() < 400) //筛选出卡路里小于400的菜肴 .sorted(comparing(Dish::getCalories)) //根据卡路里进行排序 .map(Dish::getName) //提取菜肴名称 .collect(Collectors.toList()); //转换为List } 不拖泥带水,一气呵成,原来需要写24代码实现的功能现在只需5行就可以完成了高高兴兴写完需求这时候又有新需求了,新需求如下:对数据库查询到的菜肴根据菜肴种类进行分类,返回一个Map<Type, List<Dish>>的结果这要是放在jdk8之前肯定会头皮发麻Java8 以前的实现方式private static Map<Type, List<Dish>> beforeJdk8(List<Dish> dishList) { Map<Type, List<Dish>> result = new HashMap<>(); for (Dish dish : dishList) { //不存在则初始化 if (result.get(dish.getType())==null) { List<Dish> dishes = new ArrayList<>(); dishes.add(dish); result.put(dish.getType(), dishes); } else { //存在则追加 result.get(dish.getType()).add(dish); } } return result; } 还好 jdk8 有 Stream,再也不用担心复杂集合处理需求Java8 以后的实现方式private static Map<Type, List<Dish>> afterJdk8(List<Dish> dishList) { return dishList.stream().collect(groupingBy(Dish::getType)); } 又是一行代码解决了需求,忍不住大喊Stream API牛批,接下来将详细介绍流什么是流流是从支持数据处理操作的源生成的元素序列,源可以是数组、文件、集合、函数。流不是集合元素,它不是数据结构并不保存数据,它的主要目的在于计算如何生成流生成流的方式主要有五种通过集合生成,应用中最常用的一种List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5); Stream<Integer> stream = integerList.stream(); 通过集合的stream方法生成流通过数组生成int[] intArr = new int[]{1, 2, 3, 4, 5}; IntStream stream = Arrays.stream(intArr); 通过Arrays.stream方法生成流,并且该方法生成的流是数值流【即IntStream】而不是Stream<Integer>。补充一点使用数值流可以避免计算过程中拆箱装箱,提高性能。Stream API提供了mapToInt、mapToDouble、mapToLong三种方式将对象流【即Stream<T>】转换成对应的数值流,同时提供了boxed方法将数值流转换为对象流通过值生成Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5); 通过Stream的of方法生成流,通过Stream的empty方法可以生成一个空流通过文件生成Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset()) 通过Files.line方法得到一个流,并且得到的每个流是给定文件中的一行通过函数生成 提供了iterate和generate两个静态方法从函数中生成流iteratorStream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(5); iterate方法接受两个参数,第一个为初始化值,第二个为进行的函数操作,因为iterator生成的流为无限流,通过limit方法对流进行了截断,只生成 5 个偶数generatorStream<Double> stream = Stream.generate(Math::random).limit(5); generate方法接受一个参数,方法参数类型为Supplier<T>,由它为流提供值。generate生成的流也是无限流,因此通过limit对流进行了截断流的操作类型流的操作类型主要分为两种中间操作 一个流可以后面跟随零个或多个中间操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的,仅仅调用到这类方法,并没有真正开始流的遍历,真正的遍历需等到终端操作时,常见的中间操作有下面即将介绍的filter、map等终端操作 一个流有且只能有一个终端操作,当这个操作执行后,流就被关闭了,无法再被操作,因此一个流只能被遍历一次,若想在遍历需要通过源数据在生成流。终端操作的执行,才会真正开始流的遍历。如下面即将介绍的count、collect等流使用流的使用将分为终端操作和中间操作进行介绍中间操作filter 筛选List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5); Stream<Integer> stream = integerList.stream().filter(i -> i > 3); 通过使用filter方法进行条件筛选,filter的方法参数为一个条件distinct 去除重复元素List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5); Stream<Integer> stream = integerList.stream().distinct(); 通过distinct方法快速去除重复的元素limit 返回指定流个数List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5); Stream<Integer> stream = integerList.stream().limit(3); 通过limit方法指定返回流的个数,limit的参数值必须>=0,否则将会抛出异常skip 跳过流中的元素 List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5); Stream<Integer> stream = integerList.stream().skip(2); 通过skip方法跳过流中的元素,上述例子跳过前两个元素,所以打印结果为2,3,4,5,skip的参数值必须>=0,否则将会抛出异常map 流映射所谓流映射就是将接受的元素映射成另外一个元素List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action"); Stream<Integer> stream = stringList.stream().map(String::length); 通过map方法可以完成映射,该例子完成中String -> Integer的映射,之前上面的例子通过map方法完成了Dish->String的映射flatMap 流转换将一个流中的每个值都转换为另一个流List<String> wordList = Arrays.asList("Hello", "World"); List<String> strList = wordList.stream() .map(w -> w.split(" ")) .flatMap(Arrays::stream) .distinct() .collect(Collectors.toList()); map(w -> w.split(" "))的返回值为Stream<String[]>,我们想获取Stream<String>,可以通过flatMap方法完成Stream<String[]> ->Stream<String>的转换元素匹配提供了三种匹配方式allMatch 匹配所有List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5); if (integerList.stream().allMatch(i -> i > 3)) { System.out.println("值都大于3"); } 通过allMatch方法实现anyMatch 匹配其中一个List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5); if (integerList.stream().anyMatch(i -> i > 3)) { System.out.println("存在大于3的值"); } 等同于for (Integer i : integerList) { if (i > 3) { System.out.println("存在大于3的值"); break; } } 存在大于 3 的值则打印,java8中通过anyMatch方法实现这个功能noneMatch 全部不匹配List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5); if (integerList.stream().noneMatch(i -> i > 3)) { System.out.println("值都小于3"); } 通过noneMatch方法实现终端操作统计流中元素个数通过 countList<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5); Long result = integerList.stream().count(); 通过使用count方法统计出流中元素个数通过 countingList<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5); Long result = integerList.stream().collect(counting()); 最后一种统计元素个数的方法在与collect联合使用的时候特别有用查找提供了两种查找方式findFirst 查找第一个List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5); Optional<Integer> result = integerList.stream().filter(i -> i > 3).findFirst(); 通过findFirst方法查找到第一个大于三的元素并打印findAny 随机查找一个List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5); Optional<Integer> result = integerList.stream().filter(i -> i > 3).findAny(); 通过findAny方法查找到其中一个大于三的元素并打印,因为内部进行优化的原因,当找到第一个满足大于三的元素时就结束,该方法结果和findFirst方法结果一样。提供findAny方法是为了更好的利用并行流,findFirst方法在并行上限制更多【本篇文章将不介绍并行流】reduce 将流中的元素组合起来假设我们对一个集合中的值进行求和jdk8 之前int sum = 0; for (int i : integerList) { sum += i; } jdk8 之后通过 reduce 进行处理int sum = integerList.stream().reduce(0, (a, b) -> (a + b)); 一行就可以完成,还可以使用方法引用简写成:int sum = integerList.stream().reduce(0, Integer::sum); reduce接受两个参数,一个初始值这里是0,一个BinaryOperator<T> accumulator 来将两个元素结合起来产生一个新值, 另外reduce方法还有一个没有初始化值的重载方法获取流中最小最大值通过 min/max 获取最小最大值Optional<Integer> min = menu.stream().map(Dish::getCalories).min(Integer::compareTo); Optional<Integer> max = menu.stream().map(Dish::getCalories).max(Integer::compareTo); 也可以写成:OptionalInt min = menu.stream().mapToInt(Dish::getCalories).min(); OptionalInt max = menu.stream().mapToInt(Dish::getCalories).max(); min获取流中最小值,max获取流中最大值,方法参数为Comparator<? super T> comparator通过 minBy/maxBy 获取最小最大值Optional<Integer> min = menu.stream().map(Dish::getCalories).collect(minBy(Integer::compareTo)); Optional<Integer> max = menu.stream().map(Dish::getCalories).collect(maxBy(Integer::compareTo)); minBy获取流中最小值,maxBy获取流中最大值,方法参数为Comparator<? super T> comparator通过 reduce 获取最小最大值Optional<Integer> min = menu.stream().map(Dish::getCalories).reduce(Integer::min); Optional<Integer> max = menu.stream().map(Dish::getCalories).reduce(Integer::max); 求和通过 summingIntint sum = menu.stream().collect(summingInt(Dish::getCalories)); 如果数据类型为double、long,则通过summingDouble、summingLong方法进行求和通过 reduceint sum = menu.stream().map(Dish::getCalories).reduce(0, Integer::sum); 通过 sumint sum = menu.stream().mapToInt(Dish::getCalories).sum(); 在上面求和、求最大值、最小值的时候,对于相同操作有不同的方法可以选择执行。可以选择collect、reduce、min/max/sum方法,推荐使用min、max、sum方法。因为它最简洁易读,同时通过mapToInt将对象流转换为数值流,避免了装箱和拆箱操作通过 averagingInt 求平均值double average = menu.stream().collect(averagingInt(Dish::getCalories)); 如果数据类型为double、long,则通过averagingDouble、averagingLong方法进行求平均通过 summarizingInt 同时求总和、平均值、最大值、最小值IntSummaryStatistics intSummaryStatistics = menu.stream().collect(summarizingInt(Dish::getCalories)); double average = intSummaryStatistics.getAverage(); //获取平均值 int min = intSummaryStatistics.getMin(); //获取最小值 int max = intSummaryStatistics.getMax(); //获取最大值 long sum = intSummaryStatistics.getSum(); //获取总和 如果数据类型为double、long,则通过summarizingDouble、summarizingLong方法通过 foreach 进行元素遍历List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5); integerList.stream().forEach(System.out::println); 而在 jdk8 之前实现遍历:for (int i : integerList) { System.out.println(i); } jdk8 之后遍历元素来的更为方便,原来的 for-each 直接通过 foreach 方法就能实现了返回集合List<String> strings = menu.stream().map(Dish::getName).collect(toList()); Set<String> sets = menu.stream().map(Dish::getName).collect(toSet()); 只举例了一部分,还有很多其他方法 jdk8 之前 List<String> stringList = new ArrayList<>(); Set<String> stringSet = new HashSet<>(); for (Dish dish : menu) { stringList.add(dish.getName()); stringSet.add(dish.getName()); } 通过遍历和返回集合的使用发现流只是把原来的外部迭代放到了内部进行,这也是流的主要特点之一。内部迭代可以减少好多代码量通过 joining 拼接流中的元素String result = menu.stream().map(Dish::getName).collect(Collectors.joining(", ")); 默认如果不通过map方法进行映射处理拼接的toString方法返回的字符串,joining 的方法参数为元素的分界符,如果不指定生成的字符串将是一串的,可读性不强进阶通过 groupingBy 进行分组Map<Type, List<Dish>> result = dishList.stream().collect(groupingBy(Dish::getType)); 在collect方法中传入groupingBy进行分组,其中groupingBy的方法参数为分类函数。还可以通过嵌套使用groupingBy进行多级分类Map<Type, List<Dish>> result = menu.stream().collect(groupingBy(Dish::getType, groupingBy(dish -> { if (dish.getCalories() <= 400) return CaloricLevel.DIET; else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL; else return CaloricLevel.FAT; }))); 进阶通过 partitioningBy 进行分区分区是特殊的分组,它分类依据是 true 和 false,所以返回的结果最多可以分为两组Map<Boolean, List<Dish>> result = menu.stream().collect(partitioningBy(Dish :: isVegetarian)) 等同于Map<Boolean, List<Dish>> result = menu.stream().collect(groupingBy(Dish :: isVegetarian)) 这个例子可能并不能看出分区和分类的区别,甚至觉得分区根本没有必要,换个明显一点的例子:List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5); Map<Boolean, List<Integer>> result = integerList.stream().collect(partitioningBy(i -> i < 3)); 返回值的键仍然是布尔类型,但是它的分类是根据范围进行分类的,分区比较适合处理根据范围进行分类总结通过使用Stream API可以简化代码,同时提高了代码可读性,赶紧在项目里用起来。
2023年02月13日
47 阅读
0 评论
0 点赞
2023-02-11
SpringBoot集成JWT实现token验证
JWT官网:https://jwt.io/ 什么是JWT Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).定义了一种简洁的,自包含的方法用于通信双方之间以JSON对象的形式安全的传递信息。因为数字签名的存在,这些信息是可信的,JWT可以使用HMAC算法或者是RSA的公私秘钥对进行签名。 JWT请求流程 1. 用户使用账号和面发出post请求; 2. 服务器使用私钥创建一个jwt; 3. 服务器返回这个jwt给浏览器; 4. 浏览器将该jwt串在请求头中像服务器发送请求; 5. 服务器验证该jwt; 6. 返回响应的资源给浏览器。 JWT的主要应用场景 身份认证在这种场景下,一旦用户完成了登陆,在接下来的每个请求中包含JWT,可以用来验证用户身份以及对路由,服务和资源的访问权限进行验证。由于它的开销非常小,可以轻松的在不同域名的系统中传递,所有目前在单点登录(SSO)中比较广泛的使用了该技术。 信息交换在通信的双方之间使用JWT对数据进行编码是一种非常安全的方式,由于它的信息是经过签名的,可以确保发送者发送的信息是没有经过伪造的。 优点 1.简洁(Compact): 可以通过URL,POST参数或者在HTTP header发送,因为数据量小,传输速度也很快 2.自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库 3.因为Token是以JSON加密的形式保存在客户端的,所以JWT是跨语言的,原则上任何web形式都支持。 4.不需要在服务端保存会话信息,特别适用于分布式微服务。 JWT的结构 JWT是由三段信息构成的,将这三段信息文本用.连接一起就构成了JWT字符串。 就像这样: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ JWT包含了三部分: Header 头部(标题包含了令牌的元数据,并且包含签名和/或加密算法的类型) Payload 负载 (类似于飞机上承载的物品) Signature 签名/签证 Header JWT的头部承载两部分信息:token类型和采用的加密算法。 { "alg": "HS256", "typ": "JWT" } 声明类型:这里是jwt 声明加密的算法:通常直接使用 HMAC SHA256 加密算法是单向函数散列算法,常见的有MD5、SHA、HAMC。 MD5(message-digest algorithm 5) (信息-摘要算法)缩写,广泛用于加密和解密技术,常用于文件校验。校验?不管文件多大,经过MD5后都能生成唯一的MD5值 SHA (Secure Hash Algorithm,安全散列算法),数字签名等密码学应用中重要的工具,安全性高于MD5 HMAC (Hash Message Authentication Code),散列消息鉴别码,基于密钥的Hash算法的认证协议。用公开函数和密钥产生一个固定长度的值作为认证标识,用这个标识鉴别消息的完整性。常用于接口签名验证 Payload 载荷就是存放有效信息的地方。 有效信息包含三个部分 1.标准中注册的声明 2.公共的声明 3.私有的声明 标准中注册的声明 (建议但不强制使用) : iss: jwt签发者 sub: 面向的用户(jwt所面向的用户) aud: 接收jwt的一方 exp: 过期时间戳(jwt的过期时间,这个过期时间必须要大于签发时间) nbf: 定义在什么时间之前,该jwt都是不可用的. iat: jwt的签发时间 jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。 公共的声明 : 公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密. 私有的声明 : 私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。 Signature jwt的第三部分是一个签证信息 这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。 密钥secret是保存在服务端的,服务端会根据这个密钥进行生成token和进行验证,所以需要保护好。 下面来进行SpringBoot和JWT的集成 引入JWT依赖,由于是基于Java,所以需要的是java-jwt <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.4.0</version> </dependency> 需要自定义两个注解 用来跳过验证的PassToken @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface PassToken { boolean required() default true; } 需要登录才能进行操作的注解UserLoginToken @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface UserLoginToken { boolean required() default true; } @Target:注解的作用目标 @Target(ElementType.TYPE)——接口、类、枚举、注解 @Target(ElementType.FIELD)——字段、枚举的常量 @Target(ElementType.METHOD)——方法 @Target(ElementType.PARAMETER)——方法参数 @Target(ElementType.CONSTRUCTOR) ——构造函数 @Target(ElementType.LOCAL_VARIABLE)——局部变量 @Target(ElementType.ANNOTATION_TYPE)——注解 @Target(ElementType.PACKAGE)——包 @Retention:注解的保留位置 RetentionPolicy.SOURCE:这种类型的Annotations只在源代码级别保留,编译时就会被忽略,在class字节码文件中不包含。 RetentionPolicy.CLASS:这种类型的Annotations编译时被保留,默认的保留策略,在class文件中存在,但JVM将会忽略,运行时无法获得。 RetentionPolicy.RUNTIME:这种类型的Annotations将被JVM保留,所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用。 @Document:说明该注解将被包含在javadoc中 @Inherited:说明子类可以继承父类中的该注解 简单自定义一个实体类User,使用lombok简化实体类的编写 @Data @AllArgsConstructor @NoArgsConstructor public class User { String Id; String username; String password; } 需要写token的生成方法 public String getToken(User user) { String token=""; token= JWT.create().withAudience(user.getId()) .sign(Algorithm.HMAC256(user.getPassword())); return token; } Algorithm.HMAC256():使用HS256生成token,密钥则是用户的密码,唯一密钥的话可以保存在服务端。 withAudience()存入需要保存在token的信息,这里我把用户ID存入token中 接下来需要写一个拦截器去获取token并验证token public class AuthenticationInterceptor implements HandlerInterceptor { @Autowired UserService userService; @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception { String token = httpServletRequest.getHeader("token");// 从 http 请求头中取出 token // 如果不是映射到方法直接通过 if(!(object instanceof HandlerMethod)){ return true; } HandlerMethod handlerMethod=(HandlerMethod)object; Method method=handlerMethod.getMethod(); //检查是否有passtoken注释,有则跳过认证 if (method.isAnnotationPresent(PassToken.class)) { PassToken passToken = method.getAnnotation(PassToken.class); if (passToken.required()) { return true; } } //检查有没有需要用户权限的注解 if (method.isAnnotationPresent(UserLoginToken.class)) { UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class); if (userLoginToken.required()) { // 执行认证 if (token == null) { throw new RuntimeException("无token,请重新登录"); } // 获取 token 中的 user id String userId; try { userId = JWT.decode(token).getAudience().get(0); } catch (JWTDecodeException j) { throw new RuntimeException("401"); } User user = userService.findUserById(userId); if (user == null) { throw new RuntimeException("用户不存在,请重新登录"); } // 验证 token JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build(); try { jwtVerifier.verify(token); } catch (JWTVerificationException e) { throw new RuntimeException("401"); } return true; } } return true; } @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { } 实现一个拦截器就需要实现HandlerInterceptor接口 HandlerInterceptor接口主要定义了三个方法 1.boolean preHandle (): 预处理回调方法,实现处理器的预处理,第三个参数为响应的处理器,自定义Controller,返回值为true表示继续流程(如调用下一个拦截器或处理器)或者接着执行 postHandle()和afterCompletion();false表示流程中断,不会继续调用其他的拦截器或处理器,中断执行。 2.void postHandle(): 后处理回调方法,实现处理器的后处理(DispatcherServlet进行视图返回渲染之前进行调用),此时我们可以通过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null。 3.void afterCompletion(): 整个请求处理完毕回调方法,该方法也是需要当前对应的Interceptor的preHandle()的返回值为true时才会执行,也就是在DispatcherServlet渲染了对应的视图之后执行。用于进行资源清理。整个请求处理完毕回调方法。如性能监控中我们可以在此记录结束时间并输出消耗时间,还可以进行一些资源清理,类似于try-catch-finally中的finally,但仅调用处理器执行链中 主要流程: 1.从 http 请求头中取出 token, 2.判断是否映射到方法 3.检查是否有passtoken注释,有则跳过认证 4.检查有没有需要用户登录的注解,有则需要取出并验证 5.认证通过则可以访问,不通过会报相关错误信息 配置拦截器 在配置类上添加了注解@Configuration,标明了该类是一个配置类并且会将该类作为一个SpringBean添加到IOC容器内 @Configuration public class InterceptorConfig extends WebMvcConfigurerAdapter { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authenticationInterceptor()) .addPathPatterns("/**"); // 拦截所有请求,通过判断是否有 @LoginRequired 注解 决定是否需要登录 } @Bean public AuthenticationInterceptor authenticationInterceptor() { return new AuthenticationInterceptor(); } } WebMvcConfigurerAdapter该抽象类其实里面没有任何的方法实现,只是空实现了接口 WebMvcConfigurer内的全部方法,并没有给出任何的业务逻辑处理,这一点设计恰到好处的让我们不必去实现那些我们不用的方法,都交由WebMvcConfigurerAdapter抽象类空实现,如果我们需要针对具体的某一个方法做出逻辑处理,仅仅需要在 WebMvcConfigurerAdapter子类中@Override对应方法就可以了。 注: 在SpringBoot2.0及Spring 5.0中WebMvcConfigurerAdapter已被废弃 网上有说改为继承WebMvcConfigurationSupport,不过试了下,还是过期的 解决方法: 直接实现WebMvcConfigurer (官方推荐) @Configuration public class InterceptorConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authenticationInterceptor()) .addPathPatterns("/**"); } @Bean public AuthenticationInterceptor authenticationInterceptor() { return new AuthenticationInterceptor(); } } InterceptorRegistry内的addInterceptor需要一个实现HandlerInterceptor接口的拦截器实例,addPathPatterns方法用于设置拦截器的过滤路径规则。 这里我拦截所有请求,通过判断是否有@LoginRequired注解 决定是否需要登录 在数据访问接口中加入登录操作注解 @RestController @RequestMapping("api") public class UserApi { @Autowired UserService userService; @Autowired TokenService tokenService; //登录 @PostMapping("/login") public Object login(@RequestBody User user){ JSONObject jsonObject=new JSONObject(); User userForBase=userService.findByUsername(user); if(userForBase==null){ jsonObject.put("message","登录失败,用户不存在"); return jsonObject; }else { if (!userForBase.getPassword().equals(user.getPassword())){ jsonObject.put("message","登录失败,密码错误"); return jsonObject; }else { String token = tokenService.getToken(userForBase); jsonObject.put("token", token); jsonObject.put("user", userForBase); return jsonObject; } } } @UserLoginToken @GetMapping("/getMessage") public String getMessage(){ return "你已通过验证"; } } 不加注解的话默认不验证,登录接口一般是不验证的。在getMessage()中我加上了登录注解,说明该接口必须登录获取token后,在请求头中加上token并通过验证才可以访问 下面进行测试,启动项目,使用postman测试接口 在没token的情况下访问api/getMessage接口 我这里使用了统一异常处理,所以只看到错误message 下面进行登录,从而获取token 登录操作我没加验证注解,所以可以直接访问 把token加在请求头中,再次访问api/getMessage接口 注意:这里的key一定不能错,因为在拦截器中是取关键字token的值 String token = httpServletRequest.getHeader("token"); 加上token之后就可以顺利通过验证和进行接口访问了
2023年02月11日
158 阅读
1 评论
0 点赞
2023-02-11
Activiti工作流引擎数据库表结构
文档适用于 Activiti 5—6 Activiti 工作流总共包含 23 张数据表(现在是25张,新增了 ACT_EVT_LOG 和 ACT_PROCDEF_INFO ) 一、结构设计 1、逻辑结构设计 Activiti使用到的表都是ACT_开头的。 表开头说明ACT_RE_*’RE’表示repository(存储),RepositoryService接口所操作的表。带此前缀的表包含的是静态信息,如,流程定义,流程的资源(图片,规则等)ACT_RU_*‘RU’表示runtime,运行时表-RuntimeService。这是运行时的表 存储着流程变量,用户任务,变量,职责(job)等运行时的数据。Activiti只存储实例执行期间的运行时数据,当流程实例结束时,将删除这些记录。 这就保证了这些运行时的表小且快ACT_ID_*’ID’表示identity (组织机构),IdentityService接口所操作的表。用户记录,流程中使用到的用户和组。这些表包含标识的信息,如用户,用户组,等等ACT_HI_*’HI’表示history,历史数据表,HistoryService。就是这些表包含着流程执行的历史相关数据,如结束的流程实例,变量,任务,等等ACT_GE_*全局通用数据及设置(general),各种情况都使用的数据 2、所有表的含义 一般数据 (ACT_GE_) 表名解释ACT_GE_BYTEARRAY二进制数据表,存储通用的流程定义和流程资源。ACT_GE_PROPERTY系统相关属性,属性数据表存储整个流程引擎级别的数据,初始化表结构时,会默认插入三条记录。 流程历史记录 (ACT_HI_) 表名解释ACT_HI_ACTINST历史节点表ACT_HI_ATTACHMENT历史附件表ACT_HI_COMMENT历史意见表ACT_HI_DETAIL历史详情表,提供历史变量的查询ACT_HI_IDENTITYLINK历史流程人员表ACT_HI_PROCINST历史流程实例表ACT_HI_TASKINST历史任务实例表ACT_HI_VARINST历史变量表 用户用户组表 (ACT_ID_) 表名解释ACT_ID_GROUP用户组信息表ACT_ID_INFO用户扩展信息表ACT_ID_MEMBERSHIP用户与用户组对应信息表ACT_ID_USER用户信息表 流程定义表 (ACT_RE_) 表名解释ACT_RE_DEPLOYMENT部署信息表ACT_RE_MODEL流程设计模型部署表ACT_RE_PROCDEF流程定义数据表 运行实例表 (ACT_RU_) 表名解释ACT_RU_EVENT_SUBSCR运行时事件 throwEvent、catchEvent 时间监听信息表ACT_RU_EXECUTION运行时流程执行实例ACT_RU_IDENTITYLINK运行时流程人员表,主要存储任务节点与参与者的相关信息ACT_RU_JOB运行时定时任务数据表ACT_RU_TASK运行时任务节点表ACT_RU_VARIABLE运行时流程变量数据表 其它 表名解释ACT_EVT_LOG事件日志ACT_PROCDEF_INFO流程定义的动态变更信息 二、表以及索引信息 1、act_ge_bytearray 二进制数据表,存储通用的流程定义和流程资源。(act_ge_bytearray) 保存流程定义图片和xml、Serializable(序列化)的变量,即保存所有二进制数据,特别注意类路径部署时候,不要把svn等隐藏文件或者其他与流程无关的文件也一起部署到该表中,会造成一些错误(可能导致流程定义无法删除)。 表结构说明 字段名称字段描述数据类型主键为空取值说明ID_ID_nvarchar(64)Y主键IDREV_乐观锁intYVersion(版本)NAME_名称nvarchar(255)Y部署的文件名称,如:leave.bpmn.png,leave.bpmn20.xmlDEPLOYMENT_ID_部署IDnvarchar(64)Y部署表IDBYTES_字节varbinary(max)Y部署文件GENERATED_是否是引擎生成tinyintY0为用户生成,1为activiti生成 索引说明 2、act_ge_property 属性数据表(act_ge_property) 属性数据表。存储整个流程引擎级别的数据。 表结构说明 字段名称字段描述数据类型主键为空取值说明NAME_名称nvarchar(64)√schema.versionschema.historynext.dbidVALUE_值nvarchar(300)√5.create(5.)REV_乐观锁int√version 索引说明 3、act_hi_actinst 历史节点表(act_hi_actinst) 历史活动信息。这里记录流程流转过的所有节点,与HI_TASKINST不同的是,taskinst只记录usertask内容 表结构说明 字段名称字段描述数据类型主键为空取值说明ID_ID_nvarchar(64)√PROC_DEF_ID_流程定义IDnvarchar(64)PROC_INST_ID_流程实例IDnvarchar(64)EXECUTION_ID_执行实例IDnvarchar(64)ACT_ID_节点IDnvarchar(225)节点定义IDTASK_ID_任务实例IDnvarchar(64)√任务实例ID 其他节点类型实例ID在这里为空CALL_PROC_INST_ID_调用外部的流程实例IDnvarchar(64)√调用外部流程的流程实例ID’ACT_NAME_节点名称nvarchar(225)√节点定义名称ACT_TYPE_节点类型nvarchar(225)如startEvent、userTaskASSIGNEE_签收人nvarchar(64)√节点签收人START_TIME_开始时间datetime2013-09-15 11:30:00END_TIME_结束时间datetime√2013-09-15 11:30:00DURATION_耗时numeric(19,0)√毫秒值 索引说明 4、act_hi_attachment 历史附件表( act_hi_attachment ) 表结构说明 字段名称字段描述数据类型主键为空取值说明ID_ID_nvarchar(64)√主键IDREV_乐观锁integer√VersionUSER_ID_用户IDnvarchar(255)√用户IDNAME_名称nvarchar(255)√附件名称DESCRIPTION_描述nvarchar(4000)√描述TYPE_类型nvarchar(255)√附件类型TASK_ID_任务实例IDnvarchar(64)√节点实例IDPROC_INST_ID_流程实例IDnvarchar(64)√流程实例IDURL_URL_nvarchar(4000)√附件地址CONTENT_ID_字节表的IDnvarchar(64)√ACT_GE_BYTEARRAY的ID 索引说明 5、act_hi_comment 历史意见表( act_hi_comment ) 表结构说明 字段名称字段描述数据类型主键为空取值说明ID_ID_nvarchar(64)√主键IDTYPE_类型nvarchar(255)√类型:event(事件)comment(意见)TIME_时间datetime填写时间’USER_ID_用户IDnvarchar(64)√填写人TASK_ID_节点任务IDnvarchar(64)√节点实例IDPROC_INST_ID_流程实例IDnvarchar(255)√流程实例IDACTION_行为类型nvarchar(64)√见备注1MESSAGE_基本内容nvarchar(4000)√用于存放流程产生的信息,比如审批意见FULL_MSG_全部内容varbinary(max)√附件地址 索引说明 6、act_hi_detail 历史详情表( act_hi_detail ) 流程中产生的变量详细,包括控制流程流转的变量,业务表单中填写的流程需要用到的变量等。 表结构说明 字段名称字段描述数据类型主键为空取值说明ID_ID_nvarchar(64)√主键TYPE_类型nvarchar(255)见备注2PROC_INST_ID_流程实例IDnvarchar(64)√流程实例IDEXECUTION_ID_执行实例IDnvarchar(64)√执行实例IDTASK_ID_任务实例IDnvarchar(64)√任务实例IDACT_INST_ID_节点实例IDnvarchar(64)√ACT_HI_ACTINST表的IDNAME_名称nvarchar(255)名称VAR_TYPE_参数类型nvarchar(255)√见备注3REV_乐观锁int√VersionTIME_时间戳datetime创建时间BYTEARRAY_ID_字节表IDnvarchar√ACT_GE_BYTEARRAY表的IDDOUBLE_DOUBLE_double precision√存储变量类型为DoubleLONG_LONG_numeric√存储变量类型为longTEXT_TEXT_nvarchar√存储变量值类型为StringTEXT2_TEXT2_nvarchar√此处存储的是JPA持久化对象时,才会有值。此值为对象ID 索引说明 7、act_ru_identitylink 历史流程人员表( act_ru_identitylink ) 任务参与者数据表。主要存储历史节点参与者的信息 表结构说明 字段名称字段描述数据类型主键为空取值说明ID_ID_nvarchar(64)√ID_GROUP_ID_组IDnvarchar(255)√组IDTYPE_类型nvarchar(255)√备注4USER_ID_用户IDnvarchar(255)√用户IDTASK_ID_节点实例IDnvarchar(64)√节点实例IDPROC_INST_ID_流程实例IDnvarchar(64)√流程实例ID 索引说明 8、act_hi_procinst 历史流程实例表(act_hi_procinst) 表结构说明 字段名称字段描述数据类型主键为空取值说明ID_ID_nvarchar(64)√主键IDPROC_INST_ID_流程实例IDnvarchar(64)流程实例IDBUSINESS_KEY_业务主键nvarchar(255)√业务主键,业务表单的IDPROC_DEF_ID_流程定义IDnvarchar(64)流程定义IDSTART_TIME_开始时间datetime开始时间END_TIME_结束时间datetime√结束时间DURATION_耗时Numeric(19)√耗时START_USER_ID_起草人nvarchar(255)√起草人START_ACT_ID_开始节点IDnvarchar(255)√起草环节IDEND_ACT_ID_结束节点IDnvarchar(255)√结束环节IDSUPER_PROCESS_INSTANCE_ID_父流程实例IDnvarchar(64)√父流程实例IDDELETE_REASON_删除原因nvarchar(4000)√删除原因 索引说明 9、act_hi_taskinst 历史任务实例表( act_hi_taskinst ) 表结构说明 字段名称字段描述数据类型主键为空取值说明ID_ID_nvarchar(64)√主键IDPROC_DEF_ID_流程定义IDnvarchar(64)√流程定义IDTASK_DEF_KEY_节点定义IDnvarchar(255)√节点定义IDPROC_INST_ID_流程实例IDnvarchar(64)√流程实例IDEXECUTION_ID_执行实例IDnvarchar(64)√执行实例IDNAME_名称varchar(255)√名称PARENT_TASK_ID_父节点实例IDnvarchar(64)√父节点实例IDDESCRIPTION_描述nvarchar(400)√描述OWNER_实际签收人 任务的拥有者nvarchar(255)√签收人(默认为空,只有在委托时才有值)ASSIGNEE_签收人或被委托nvarchar(255)√签收人或被委托START_TIME_开始时间datetime开始时间CLAIM_TIME_提醒时间datetime√提醒时间END_TIME_结束时间datetime√结束时间DURATION_耗时numeric(19)√耗时DELETE_REASON_删除原因nvarchar(4000)√删除原因(completed,deleted)PRIORITY_优先级别int√优先级别DUE_DATE_过期时间datetime√过期时间,表明任务应在多长时间内完成FORM_KEY_节点定义的formkeynvarchar(255)√desinger节点定义的form_key属性 索引说明 10、act_hi_varinst 历史变量表( act_hi_varinst ) 表结构说明 字段名称字段描述数据类型主键为空取值说明ID_ID_nvarchar(64)√ID_PROC_INST_ID_流程实例IDnvarchar(64)√流程实例IDEXECUTION_ID_执行实例IDnvarchar(255)√执行实例IDTASK_ID_任务实例IDnvarchar(64)√任务实例IDNAME_名称nvarchar(64)参数名称(英文)VAR_TYPE_参数类型varchar(255)√备注5REV_乐观锁nvarchar(64)√乐观锁 VersionBYTEARRAY_ID_字节表IDnvarchar(400)√ACT_GE_BYTEARRAY表的主键DOUBLE_DOUBLE_nvarchar(255)√存储DoubleType类型的数据LONG_LONG_nvarchar(255)√存储LongType类型的数据TEXT_TEXT_datetime√备注6TEXT2_TEXT2_datetime√此处存储的是JPA持久化对象时,才会有值。此值为对象ID 索引说明 11、act_id_group 用户组信息表( act_id_group ) 表结构说明 字段名称字段描述数据类型主键为空取值说明ID_ID_nvarchar(64)√主键IDREV_乐观锁int√乐观锁VersionNAME_名称nvarchar(255)√组名称TYPE_类型nvarchar(255)√类型 索引说明 12、act_id_info 用户扩展信息表( act_id_info ) 表结构说明 字段名称字段描述数据类型主键为空取值说明ID_ID_nvarchar(64)√主键IDREV_乐观锁int√乐观锁VersionUSER_ID_用户IDnvarchar(64)√TYPE_类型nvarchar(64)√KEY_nvarchar(255)√VALUE_nvarchar(255)√PASSWORD_Image√PARENT_ID_nvarchar(255)√ 索引说明 13、act_id_membership 用户与分组对应信息表( act_id_membership ) 用来保存用户的分组信息。 表结构说明 字段名称字段描述数据类型主键为空取值说明USER_ID用户IDnvarchar(64)√GROUP_ID用户组IDnvarchar(64)√ 索引说明 14、act_id_user 用户信息表( act_id_user ) 表结构说明 字段名称字段描述数据类型主键为空取值说明ID_ID_nvarchar(64)√主键IDREV_乐观锁int√乐观锁VersionFIRST_姓nvarchar(255)√LAST_名nvarchar(255)√EMAIL_EMAIL_nvarchar(255)√PWD_密码nvarchar(255)√PICTURE_ID_图片IDnvarchar(64)√ 索引说明 15、act_re_deployment 部署信息表( act_re_deployment ) 部署流程定义时需要被持久化保存下来的信息。 表结构说明 字段名称字段描述数据类型主键为空取值说明ID_ID_nvarchar(64)√主键IDNAME_部署名称nvarchar(255)√部署文件名CATEGORY_分类nvarchar(255)√类别DEPLOY_TIME_部署时间datetime√部署时间 索引说明 16、act_re_model 流程设计模型部署表( act_re_model ) 流程设计器设计流程后,保存数据到该表。 表结构说明 字段名称字段描述数据类型主键为空取值说明ID_ID_nvarchar(64)√ID_REV_乐观锁int√乐观锁NAME_名称nvarchar(255)√名称KEY_KEY_nvarchar(255)√分类CATEGORY_分类nvarchar(255)√分类CREATE_TIME_创建时间datetime√创建时间LAST_UPDATE_TIME_最新修改时间datetime√最新修改时间VERSION_版本int√版本META_INFO_META_INFO_nvarchar(255)√以json格式保存流程定义的信息DEPLOYMENT_ID_部署IDnvarchar(255)√部署IDEDITOR_SOURCE_VALUE_ID_datetime√EDITOR_SOURCE_EXTRA_VALUE_ID_datetime√ 索引说明 17、act_re_procdef 流程定义数据表( act_re_procdef ) 业务流程定义数据表。此表和 ACT_RE_DEPLOYMENT 是多对一的关系,即,一个部署的bar包里可能包含多个流程定义文件,每个流程定义文件都会有一条记录在 ACT_REPROCDEF 表内,每个流程定义的数据,都会对于 ACT_GE_BYTEARRAY 表内的一个资源文件和 PNG 图片文件。和 ACT_GE_BYTEARRAY 的关联是通过程序用ACT_GE_BYTEARRAY.NAME 与 ACT_RE_PROCDEF.NAME 完成的,在数据库表结构中没有体现。 表结构说明 字段名称字段描述数据类型主键为空取值说明ID_ID_nvarchar(64)√ID_REV_乐观锁int√乐观锁CATEGORY_分类nvarchar(255)√流程定义的Namespace就是类别NAME_名称nvarchar(255)√名称KEY_定义的KEYnvarchar(255)流程定义IDVERSION_版本int版本DEPLOYMENT_ID_部署表IDnvarchar(64)√部署表IDRESOURCE_NAME_bpmn文件名称nvarchar(4000)√流程bpmn文件名称DGRM_RESOURCE_NAME_png图片名称nvarchar(4000)√流程图片名称DESCRIPTION_描述nvarchar(4000)√描述HAS_START_FORM_KEY_是否存在开始节点formKeytinyint√start节点是否存在formKey 0否 1是SUSPENSION_STATE_是否挂起tinyint√1 激活 2挂起 索引说明 18、act_ru_event_subscr 事件订阅表(act_ru_event_subscr) 事件订阅表。此表包含所有当前存在的事件订阅。它包括预期事件的类型,名称和配置,以及有关相应流程实例和执行的信息。 表结构说明 字段名称字段描述数据类型主键为空取值说明ID_事件IDnvarchar(64)√事件IDREV_版本int√乐观锁VersionEVENT_TYPE_事件类型nvarchar(255)事件类型EVENT_NAME_事件名称nvarchar(255)√事件名称EXECUTION_ID_执行实例IDnvarchar(64)√执行实例IDPROC_INST_ID_流程实例IDnvarchar(64)√流程实例IDACTIVITY_ID_活动实例IDnvarchar(64)√活动实例IDCONFIGURATION_配置nvarchar(255)√配置CREATED_是否创建datetime默认值 当前系统时间戳CURRENT_TIMESTAMP 索引说明 19、act_ru_execution 运行时流程执行实例表( act_ru_execution ) 表结构说明 字段名称字段描述数据类型主键为空取值说明ID_ID_nvarchar(64)√ID_REV_乐观锁int√乐观锁PROC_INST_ID_流程实例IDnvarchar(64)流程实例IDBUSINESS_KEY_业务主键IDnvarchar(255)√业务主键IDPARENT_ID_父节点实例IDnvarchar(64)√父节点实例IDPROC_DEF_ID_流程定义IDnvarchar(64)√流程定义IDSUPER_EXEC_SUPER_EXEC_nvarchar(64)√SUPER_EXEC_ACT_ID_节点实例IDnvarchar(255)√节点实例ID即ACT_HI_ACTINST中IDIS_ACTIVE_是否存活tinyint√是否存活IS_CONCURRENT_是否并行tinyint√是否为并行(true/false)IS_SCOPE_IS_SCOPE_tinyint√IS_SCOPE_IS_EVENT_SCOPE_IS_EVENT_SCOPE_tinyint√IS_EVENT_SCOPE_SUSPENSION_STATE_是否挂起tinyint√挂起状态 1激活 2挂起CACHED_ENT_STATE_int√ 索引说明 20、act_ru_identitylink 运行时流程人员表( act_ru_identitylink ) 任务参与者数据表。主要存储当前节点参与者的信息。 表结构说明 字段名称字段描述数据类型主键为空取值说明ID_ID_nvarchar(64)√ID_REV_乐观锁int√乐观锁GROUP_ID_组IDnvarchar(64)√组IDTYPE_类型nvarchar(255)√备注7USER_ID_用户IDnvarchar(64)√用户IDTASK_ID_节点实例IDnvarchar(64)√节点实例IDPROC_INST_ID_流程实例IDnvarchar(64)√流程实例IDPROC_DEF_ID_流程定义IDnvarchar(255)√流程定义ID 索引说明 21、act_ru_job 运行时定时任务数据表( act_ru_job ) 表结构说明 字段名称字段描述数据类型主键为空取值说明ID_标识nvarchar(64)√标识REV_版本int√版本TYPE_类型nvarchar(255)类型LOCK_EXP_TIME_锁定释放时间datetime√锁定释放时间LOCK_OWNER_挂起者nvarchar(255)√挂起者EXCLUSIVE_bit√EXECUTION_ID_执行实例IDnvarchar(64)√执行实例IDPROCESS_INSTANCE_ID_流程实例IDnvarchar(64)√流程实例IDPROC_DEF_ID_流程定义IDnvarchar(64)√流程定义IDRETRIES_int√EXCEPTION_STACK_ID_异常信息IDnvarchar(64)√异常信息IDEXCEPTION_MSG_异常信息nvarchar(4000)√异常信息DUEDATE_到期时间datetime√到期时间REPEAT_重复nvarchar(255)√重复HANDLER_TYPE_处理类型nvarchar(255)√处理类型HANDLER_CFG_nvarchar(4000)√标识 索引说明 22、act_ru_task 运行时任务节点表( act_ru_task ) 表结构说明 字段名称字段描述数据类型主键为空取值说明ID_ID_nvarchar(64)√ID_REV_乐观锁int√乐观锁EXECUTION_ID_执行实例IDnvarchar(64)√执行实例IDPROC_INST_ID_流程实例IDnvarchar(64)√流程实例IDPROC_DEF_ID_流程定义IDnvarchar(64)√流程定义IDNAME_节点定义名称nvarchar(255)√节点定义名称PARENT_TASK_ID_父节点实例IDnvarchar(64)√父节点实例IDDESCRIPTION_节点定义描述nvarchar(4000)√节点定义描述TASK_DEF_KEY_节点定义的KEYnvarchar(255)√任务定义的IDOWNER_实际签收人nvarchar(255)√拥有者(一般情况下为空,只有在委托时才有值)ASSIGNEE_签收人或委托人nvarchar(255)√签收人或委托人DELEGATION_委托类型nvarchar(64)√备注8PRIORITY_优先级别int√优先级别,默认为:50CREATE_TIME_创建时间datetime√创建时间DUE_DATE_过期时间datetime√耗时SUSPENSION_STATE_是否挂起int√1代表激活 2代表挂起 索引说明 23、act_ru_variable 运行时流程变量数据表( act_ru_variable ) 表结构说明 字段名称字段描述数据类型主键为空取值说明ID_ID_nvarchar(64)√主键标识REV_乐观锁int√乐观锁TYPE_类型nvarchar(255)备注9NAME_名称nvarchar(255)变量名称EXECUTION_ID_执行实例IDnvarchar(64)√执行的IDPROC_INST_ID_流程实例IDnvarchar(64)√流程实例IDTASK_ID_节点实例IDnvarchar(64)√节点实例ID(Local)BYTEARRAY_ID_字节表IDnvarchar(64)√字节表的ID(ACT_GE_BYTEARRAY)DOUBLE_DOUBLE_float√存储变量类型为DoubleLONG_LONG_numeric(19)√存储变量类型为longTEXT_TEXT_nvarchar(4000)√‘存储变量值类型为String 如此处存储持久化对象时,值jpa对象的classTEXT2_TEXT2_nvarchar(4000)√此处存储的是JPA持久化对象时,才会有值。此值为对象ID 索引说明
2023年02月11日
41 阅读
0 评论
1 点赞
1
...
4
5