Commit dcdde36253e2ffe65da37eb9649efe04c88dc6d6
Merge remote-tracking branch 'origin/master-jdk17' into HEAD
Showing
29 changed files
with
1112 additions
and
2871 deletions
sql/dm/flowable-patch-6.8.0/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/config/DmFlowableConfiguration.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.bpm.framework.flowable.config; | |
| 2 | + | |
| 3 | +import org.flowable.common.engine.impl.AbstractEngineConfiguration; | |
| 4 | +import org.springframework.beans.factory.config.BeanPostProcessor; | |
| 5 | +import org.springframework.context.annotation.Bean; | |
| 6 | +import org.springframework.context.annotation.Configuration; | |
| 7 | + | |
| 8 | +import java.sql.Connection; | |
| 9 | +import java.sql.SQLException; | |
| 10 | + | |
| 11 | +/** | |
| 12 | + * 达梦数据库的 Flowable 配置 | |
| 13 | + * | |
| 14 | + * @author 芋道源码 | |
| 15 | + */ | |
| 16 | +@Configuration(proxyBeanMethods = false) | |
| 17 | +public class DmFlowableConfiguration { | |
| 18 | + | |
| 19 | + /** | |
| 20 | + * 在 Spring 管理的各个 Flowable 引擎初始化前设置达梦方言与插入策略 | |
| 21 | + */ | |
| 22 | + @Bean | |
| 23 | + public static BeanPostProcessor dmEngineConfigurationBeanPostProcessor() { | |
| 24 | + return new BeanPostProcessor() { | |
| 25 | + | |
| 26 | + @Override | |
| 27 | + public Object postProcessBeforeInitialization(Object bean, String beanName) { | |
| 28 | + if (!(bean instanceof AbstractEngineConfiguration)) { | |
| 29 | + return bean; | |
| 30 | + } | |
| 31 | + AbstractEngineConfiguration configuration = (AbstractEngineConfiguration) bean; | |
| 32 | + if (configuration.getDataSource() == null) { | |
| 33 | + return bean; | |
| 34 | + } | |
| 35 | + // 在各个 Spring 引擎初始化前处理,避免子引擎重新开启批量插入;使用实际数据源识别达梦 | |
| 36 | + try (Connection connection = configuration.getDataSource().getConnection()) { | |
| 37 | + if ("DM DBMS".equalsIgnoreCase(connection.getMetaData().getDatabaseProductName())) { | |
| 38 | + configuration.setDatabaseType("oracle"); | |
| 39 | + // 特殊:达梦普通用户执行 Oracle INSERT ALL 时可能访问 SYS.SYSDUAL2,改为普通插入,避免扩大数据库权限 | |
| 40 | + configuration.setBulkInsertEnabled(false); | |
| 41 | + } | |
| 42 | + } catch (SQLException e) { | |
| 43 | + throw new IllegalStateException("读取 Flowable 数据库类型失败", e); | |
| 44 | + } | |
| 45 | + return bean; | |
| 46 | + } | |
| 47 | + }; | |
| 48 | + } | |
| 49 | + | |
| 50 | +} | ... | ... |
sql/dm/flowable-patch-6.8.0/src/main/java/liquibase/database/core/DmDatabase.java
0 → 100644
| 1 | +package liquibase.database.core; | |
| 2 | + | |
| 3 | +import liquibase.database.DatabaseConnection; | |
| 4 | +import liquibase.exception.DatabaseException; | |
| 5 | + | |
| 6 | +/** | |
| 7 | + * 达梦数据库的 Liquibase 适配,复用 Oracle 的字段类型与建表语法 | |
| 8 | + * | |
| 9 | + * @author 芋道源码 | |
| 10 | + */ | |
| 11 | +public class DmDatabase extends OracleDatabase { | |
| 12 | + | |
| 13 | + @Override | |
| 14 | + public String getShortName() { | |
| 15 | + // 独立注册达梦,避免与 Oracle 的 SPI 注册键及优先级冲突 | |
| 16 | + return "dm"; | |
| 17 | + } | |
| 18 | + | |
| 19 | + @Override | |
| 20 | + protected String getDefaultDatabaseProductName() { | |
| 21 | + return "DM DBMS"; | |
| 22 | + } | |
| 23 | + | |
| 24 | + @Override | |
| 25 | + public boolean isCorrectDatabaseImplementation(DatabaseConnection connection) throws DatabaseException { | |
| 26 | + return "DM DBMS".equalsIgnoreCase(connection.getDatabaseProductName()); | |
| 27 | + } | |
| 28 | + | |
| 29 | + @Override | |
| 30 | + public String getDefaultDriver(String url) { | |
| 31 | + return url != null && url.startsWith("jdbc:dm:") ? "dm.jdbc.driver.DmDriver" : null; | |
| 32 | + } | |
| 33 | + | |
| 34 | + @Override | |
| 35 | + public Integer getDefaultPort() { | |
| 36 | + return 5236; | |
| 37 | + } | |
| 38 | + | |
| 39 | + @Override | |
| 40 | + public int getDatabaseMinorVersion() throws DatabaseException { | |
| 41 | + // 特殊:部分达梦驱动返回非数字次版本号,避免 Oracle 版本判断时解析失败 | |
| 42 | + try { | |
| 43 | + return super.getDatabaseMinorVersion(); | |
| 44 | + } catch (DatabaseException e) { | |
| 45 | + return 0; | |
| 46 | + } | |
| 47 | + } | |
| 48 | + | |
| 49 | +} | ... | ... |
sql/dm/flowable-patch-6.8.0/src/main/resources/META-INF/services/liquibase.database.Database
0 → 100644
| 1 | +liquibase.database.core.DmDatabase | ... | ... |
sql/dm/flowable-patch-8.0.0/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/config/DmFlowableConfiguration.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.bpm.framework.flowable.config; | |
| 2 | + | |
| 3 | +import org.flowable.common.engine.impl.AbstractEngineConfiguration; | |
| 4 | +import org.springframework.beans.factory.config.BeanPostProcessor; | |
| 5 | +import org.springframework.context.annotation.Bean; | |
| 6 | +import org.springframework.context.annotation.Configuration; | |
| 7 | + | |
| 8 | +import java.sql.Connection; | |
| 9 | +import java.sql.SQLException; | |
| 10 | + | |
| 11 | +/** | |
| 12 | + * 达梦数据库的 Flowable 配置 | |
| 13 | + * | |
| 14 | + * @author 芋道源码 | |
| 15 | + */ | |
| 16 | +@Configuration(proxyBeanMethods = false) | |
| 17 | +public class DmFlowableConfiguration { | |
| 18 | + | |
| 19 | + /** | |
| 20 | + * 在 Spring 管理的各个 Flowable 引擎初始化前设置达梦方言与插入策略 | |
| 21 | + */ | |
| 22 | + @Bean | |
| 23 | + public static BeanPostProcessor dmEngineConfigurationBeanPostProcessor() { | |
| 24 | + return new BeanPostProcessor() { | |
| 25 | + | |
| 26 | + @Override | |
| 27 | + public Object postProcessBeforeInitialization(Object bean, String beanName) { | |
| 28 | + if (!(bean instanceof AbstractEngineConfiguration)) { | |
| 29 | + return bean; | |
| 30 | + } | |
| 31 | + AbstractEngineConfiguration configuration = (AbstractEngineConfiguration) bean; | |
| 32 | + if (configuration.getDataSource() == null) { | |
| 33 | + return bean; | |
| 34 | + } | |
| 35 | + // 在各个 Spring 引擎初始化前处理,避免子引擎重新开启批量插入;使用实际数据源识别达梦 | |
| 36 | + try (Connection connection = configuration.getDataSource().getConnection()) { | |
| 37 | + if ("DM DBMS".equalsIgnoreCase(connection.getMetaData().getDatabaseProductName())) { | |
| 38 | + configuration.setDatabaseType("oracle"); | |
| 39 | + // 特殊:达梦普通用户执行 Oracle INSERT ALL 时可能访问 SYS.SYSDUAL2,改为普通插入,避免扩大数据库权限 | |
| 40 | + configuration.setBulkInsertEnabled(false); | |
| 41 | + } | |
| 42 | + } catch (SQLException e) { | |
| 43 | + throw new IllegalStateException("读取 Flowable 数据库类型失败", e); | |
| 44 | + } | |
| 45 | + return bean; | |
| 46 | + } | |
| 47 | + }; | |
| 48 | + } | |
| 49 | + | |
| 50 | +} | ... | ... |
sql/dm/flowable-patch-tests/README.md
0 → 100644
| 1 | +# Flowable 达梦补丁回归测试 | |
| 2 | + | |
| 3 | +本目录是独立的 Maven 测试工程,直接编译同级 `flowable-patch-${flowable.version}` 补丁目录的源码,验证补丁本身。它不加入主工程模块,不参与业务默认构建,也不要复制到业务模块。 | |
| 4 | + | |
| 5 | +补丁安装请阅读项目文档《工作流(达梦适配)》,本文只说明如何测试。 | |
| 6 | + | |
| 7 | +## 1. 版本矩阵 | |
| 8 | + | |
| 9 | +| Profile | JDK | Spring Boot | Flowable | 被测补丁 | | |
| 10 | +| --- | --- | --- | --- | --- | | |
| 11 | +| `jdk8` | JDK8 | 2.7.18 | 6.8.0 | `flowable-patch-6.8.0` | | |
| 12 | +| 默认 | JDK17 或 JDK25 | 3.5.15 | 8.0.0 | `flowable-patch-8.0.0` | | |
| 13 | +| `sb4` | JDK25 | 4.1.1 | 8.0.0 | `flowable-patch-8.0.0` | | |
| 14 | + | |
| 15 | +各组合的编译输出位于 `target/<Spring Boot 版本>-<Flowable 版本>`,互不覆盖。 | |
| 16 | + | |
| 17 | +## 2. 准备数据库 | |
| 18 | + | |
| 19 | +先创建独立的非生产测试账号,只授予该测试 schema 的建表权限,**不要使用生产库**。6.8.0 与 8.0.0 请使用不同账号/schema 分别测试,不能在同一工作流 schema 混跑两个 Flowable 版本。 | |
| 20 | + | |
| 21 | +通过环境变量指定数据库,密码由本地环境提供,不写入 Git: | |
| 22 | + | |
| 23 | +```bash | |
| 24 | +export DM_JDBC_URL='jdbc:dm://127.0.0.1:15236' | |
| 25 | +export DM_JDBC_USER='FLOWABLE_TEST' | |
| 26 | +export DM_JDBC_PASSWORD='本地测试密码' | |
| 27 | +``` | |
| 28 | + | |
| 29 | +## 3. 执行测试 | |
| 30 | + | |
| 31 | +在 `ruoyi-vue-pro` 仓库根目录(包含 `sql/` 目录的一级)执行: | |
| 32 | + | |
| 33 | +```bash | |
| 34 | +# 使用 JDK8:Spring Boot 2.7.18 + Flowable 6.8.0 | |
| 35 | +mvn -f sql/dm/flowable-patch-tests/pom.xml -Pjdk8 test | |
| 36 | + | |
| 37 | +# 使用 JDK17 或 JDK25:Spring Boot 3.5.15 + Flowable 8.0.0 | |
| 38 | +mvn -f sql/dm/flowable-patch-tests/pom.xml test | |
| 39 | + | |
| 40 | +# 使用 JDK25:Spring Boot 4.1.1 + Flowable 8.0.0 | |
| 41 | +mvn -f sql/dm/flowable-patch-tests/pom.xml -Psb4 test | |
| 42 | +``` | |
| 43 | + | |
| 44 | +未配置 `DM_JDBC_URL` 时,只运行基于 H2 的非达梦检查(其它数据库不受补丁影响;6.8.0 另检查 SPI 注册顺序),真实达梦用例会在 Maven 输出中显示为 `Skipped`。此时**不能据此认定达梦适配通过**;验收时应确认真实达梦用例没有被跳过。 | |
| 45 | + | |
| 46 | +## 4. 测试覆盖与边界 | |
| 47 | + | |
| 48 | +覆盖 Spring Boot 自动装配(不手工关闭应用级 Liquibase)、首次建表与保持 `flowable.database-schema-update: true` 的重复启动、流程部署/发起/两级审批、退回与拒绝、取消、长文本和布尔变量、历史与分页、事务回滚、并行网关、定时 Job 执行、DMN 建表与决策,以及自动装配后一次部署多个 DMN/事件定义。6.8.0 另覆盖 Oracle/DM 两种 SPI 注册顺序,以及 DM 元数据与布尔类型识别。 | |
| 49 | + | |
| 50 | +不包含完整芋道 Web 服务(微服务的网关、注册中心等)及前端浏览器验收,也不代表已验证所有流程模型、并发性能、跨版本历史库升级,或所有第三方 Flowable 用法。补丁合并到业务模块后,仍需在实际项目中验证启动及流程功能。 | |
| 51 | + | |
| 52 | +## 5. 实现说明 | |
| 53 | + | |
| 54 | +- 两个版本的 `DmFlowableConfiguration` 保持一致。通过 Spring `BeanPostProcessor`,在各个 Flowable 引擎配置 Bean 初始化前,按引擎实际连接的 JDBC 元数据识别达梦,设置工作流 Oracle 方言及普通插入,覆盖主引擎和 Spring 事件/DMN 子引擎。不修改 Flowable 原类,不替换全局 MyBatis `DatabaseIdProvider`,不依赖数据源名称或 `yudao.dameng.enabled` 开关。 | |
| 55 | +- 对达梦关闭 `INSERT ALL` 批量插入,避免部分 DM8 环境下普通用户访问 `SYS.SYSDUAL2` 失败,无需给业务账号授予 DBA。仅关闭主引擎不够,子引擎会将自己的批量实体重新注册到共享 SQL 工厂。代价是批量写入吞吐可能降低,需按实际负载压测。 | |
| 56 | +- 补丁只处理 Spring 管理的 Flowable 配置 Bean。手动 `new` 的非 Bean 配置、独立接入的其它引擎不会被自动覆盖,需要自行设置方言及插入策略。 | |
| 57 | +- 仅 6.8.0 使用 Liquibase SPI 注册 `DmDatabase extends OracleDatabase`,复用 Oracle 的字段、布尔值及 schema 识别逻辑,不覆盖 `BooleanType`。注册短名为独立的 `dm`,避免与原生 Oracle 实现竞争同一个注册键;Flowable 方言仍为 `oracle`,两者是不同机制。Flowable 6.8.0 自带的环境后处理器默认关闭 Spring Boot 应用级 Liquibase,内部事件/DMN 的 Liquibase 建表仍正常执行。 | |
| 58 | +- 8.0.0 的事件/DMN 已改用 SQL 脚本,补丁仅包含 Spring 配置,不引入 Liquibase。若额外加入 Liquibase 依赖,Boot 3 会默认查找应用级 changelog 导致启动失败;不要用 `spring.liquibase.enabled=false` 掩盖该问题。 | |
| 59 | +- DM 的 `COMPATIBLE_MODE` 是数据库配置,`oracle` 是 Flowable 方言,两者不是同一设置。不要在已有库上直接更换方言、重建表或混用两个 Flowable 版本;升级现有工作流数据库须另行备份、验证官方升级脚本,补丁不是升级脚本。 | ... | ... |
sql/dm/flowable-patch-tests/pom.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | |
| 2 | +<project xmlns="http://maven.apache.org/POM/4.0.0" | |
| 3 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
| 4 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> | |
| 5 | + <modelVersion>4.0.0</modelVersion> | |
| 6 | + <groupId>cn.iocoder.boot</groupId> | |
| 7 | + <artifactId>dm-flowable-patch-tests</artifactId> | |
| 8 | + <version>1.0-SNAPSHOT</version> | |
| 9 | + <properties> | |
| 10 | + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |
| 11 | + <flowable.version>8.0.0</flowable.version> | |
| 12 | + <spring.boot.version>3.5.15</spring.boot.version> | |
| 13 | + <maven.compiler.release>17</maven.compiler.release> | |
| 14 | + </properties> | |
| 15 | + <dependencyManagement> | |
| 16 | + <dependencies> | |
| 17 | + <dependency> | |
| 18 | + <groupId>org.springframework.boot</groupId> | |
| 19 | + <artifactId>spring-boot-dependencies</artifactId> | |
| 20 | + <version>${spring.boot.version}</version> | |
| 21 | + <type>pom</type> | |
| 22 | + <scope>import</scope> | |
| 23 | + </dependency> | |
| 24 | + </dependencies> | |
| 25 | + </dependencyManagement> | |
| 26 | + <dependencies> | |
| 27 | + <dependency> | |
| 28 | + <groupId>org.flowable</groupId> | |
| 29 | + <artifactId>flowable-spring-boot-starter-process</artifactId> | |
| 30 | + <version>${flowable.version}</version> | |
| 31 | + </dependency> | |
| 32 | + <dependency> | |
| 33 | + <groupId>org.flowable</groupId> | |
| 34 | + <artifactId>flowable-dmn-spring-configurator</artifactId> | |
| 35 | + <version>${flowable.version}</version> | |
| 36 | + </dependency> | |
| 37 | + <dependency> | |
| 38 | + <groupId>com.dameng</groupId> | |
| 39 | + <artifactId>DmJdbcDriver18</artifactId> | |
| 40 | + <version>8.1.3.140</version> | |
| 41 | + <scope>test</scope> | |
| 42 | + </dependency> | |
| 43 | + <dependency> | |
| 44 | + <groupId>com.h2database</groupId> | |
| 45 | + <artifactId>h2</artifactId> | |
| 46 | + <scope>test</scope> | |
| 47 | + </dependency> | |
| 48 | + <dependency> | |
| 49 | + <groupId>org.junit.jupiter</groupId> | |
| 50 | + <artifactId>junit-jupiter</artifactId> | |
| 51 | + <scope>test</scope> | |
| 52 | + </dependency> | |
| 53 | + </dependencies> | |
| 54 | + <build> | |
| 55 | + <directory>${project.basedir}/target/${spring.boot.version}-${flowable.version}</directory> | |
| 56 | + <sourceDirectory>../flowable-patch-${flowable.version}/src/main/java</sourceDirectory> | |
| 57 | + <resources> | |
| 58 | + <resource> | |
| 59 | + <directory>../flowable-patch-${flowable.version}/src/main/resources</directory> | |
| 60 | + </resource> | |
| 61 | + </resources> | |
| 62 | + <plugins> | |
| 63 | + <plugin> | |
| 64 | + <groupId>org.apache.maven.plugins</groupId> | |
| 65 | + <artifactId>maven-compiler-plugin</artifactId> | |
| 66 | + <version>3.13.0</version> | |
| 67 | + </plugin> | |
| 68 | + <plugin> | |
| 69 | + <groupId>org.apache.maven.plugins</groupId> | |
| 70 | + <artifactId>maven-surefire-plugin</artifactId> | |
| 71 | + <version>3.5.3</version> | |
| 72 | + </plugin> | |
| 73 | + </plugins> | |
| 74 | + </build> | |
| 75 | + <profiles> | |
| 76 | + <profile> | |
| 77 | + <id>jdk8</id> | |
| 78 | + <properties> | |
| 79 | + <flowable.version>6.8.0</flowable.version> | |
| 80 | + <spring.boot.version>2.7.18</spring.boot.version> | |
| 81 | + <maven.compiler.release>8</maven.compiler.release> | |
| 82 | + </properties> | |
| 83 | + <dependencies> | |
| 84 | + <dependency> | |
| 85 | + <groupId>org.liquibase</groupId> | |
| 86 | + <artifactId>liquibase-core</artifactId> | |
| 87 | + </dependency> | |
| 88 | + </dependencies> | |
| 89 | + <build> | |
| 90 | + <plugins> | |
| 91 | + <plugin> | |
| 92 | + <groupId>org.codehaus.mojo</groupId> | |
| 93 | + <artifactId>build-helper-maven-plugin</artifactId> | |
| 94 | + <version>3.6.1</version> | |
| 95 | + <executions> | |
| 96 | + <execution> | |
| 97 | + <id>liquibase-patch-tests</id> | |
| 98 | + <phase>generate-test-sources</phase> | |
| 99 | + <goals><goal>add-test-source</goal></goals> | |
| 100 | + <configuration> | |
| 101 | + <sources><source>src/test/liquibase</source></sources> | |
| 102 | + </configuration> | |
| 103 | + </execution> | |
| 104 | + </executions> | |
| 105 | + </plugin> | |
| 106 | + </plugins> | |
| 107 | + </build> | |
| 108 | + </profile> | |
| 109 | + <profile> | |
| 110 | + <id>sb4</id> | |
| 111 | + <properties> | |
| 112 | + <spring.boot.version>4.1.1</spring.boot.version> | |
| 113 | + <maven.compiler.release>25</maven.compiler.release> | |
| 114 | + </properties> | |
| 115 | + </profile> | |
| 116 | + </profiles> | |
| 117 | +</project> | ... | ... |
sql/dm/flowable-patch-tests/src/test/java/cn/iocoder/yudao/module/bpm/DmFlowablePatchTest.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.bpm; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.module.bpm.framework.flowable.config.DmFlowableConfiguration; | |
| 4 | +import org.flowable.engine.ProcessEngine; | |
| 5 | +import org.flowable.engine.history.HistoricProcessInstance; | |
| 6 | +import org.flowable.engine.runtime.ProcessInstance; | |
| 7 | +import org.flowable.dmn.engine.DmnEngine; | |
| 8 | +import org.flowable.dmn.spring.SpringDmnEngineConfiguration; | |
| 9 | +import org.flowable.eventregistry.impl.EventRegistryEngine; | |
| 10 | +import org.flowable.eventregistry.api.EventDeployment; | |
| 11 | +import org.flowable.spring.SpringProcessEngineConfiguration; | |
| 12 | +import org.flowable.task.api.Task; | |
| 13 | +import org.junit.jupiter.api.Test; | |
| 14 | +import org.springframework.boot.WebApplicationType; | |
| 15 | +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; | |
| 16 | +import org.springframework.boot.builder.SpringApplicationBuilder; | |
| 17 | +import org.springframework.context.ConfigurableApplicationContext; | |
| 18 | +import org.springframework.context.annotation.Bean; | |
| 19 | +import org.springframework.context.annotation.Configuration; | |
| 20 | +import org.springframework.context.annotation.Import; | |
| 21 | +import org.springframework.jdbc.datasource.DataSourceTransactionManager; | |
| 22 | +import org.springframework.jdbc.datasource.DriverManagerDataSource; | |
| 23 | +import org.springframework.transaction.support.TransactionTemplate; | |
| 24 | + | |
| 25 | +import javax.sql.DataSource; | |
| 26 | +import java.sql.Connection; | |
| 27 | +import java.util.Collections; | |
| 28 | +import java.util.List; | |
| 29 | +import java.util.Map; | |
| 30 | +import java.util.UUID; | |
| 31 | +import java.nio.charset.StandardCharsets; | |
| 32 | +import java.io.ByteArrayOutputStream; | |
| 33 | +import java.io.InputStream; | |
| 34 | + | |
| 35 | +import static org.junit.jupiter.api.Assertions.*; | |
| 36 | +import static org.junit.jupiter.api.Assumptions.assumeTrue; | |
| 37 | + | |
| 38 | +/** | |
| 39 | + * 达梦补丁的版本兼容与真实数据库回归测试 | |
| 40 | + * | |
| 41 | + * @author 芋道源码 | |
| 42 | + */ | |
| 43 | +class DmFlowablePatchTest { | |
| 44 | + | |
| 45 | + @Test | |
| 46 | + void testOtherDatabaseUnchanged() throws Exception { | |
| 47 | + DriverManagerDataSource dataSource = new DriverManagerDataSource("jdbc:h2:mem:dm_patch_regression", "sa", ""); | |
| 48 | + SpringProcessEngineConfiguration configuration = configuration(dataSource); | |
| 49 | + configureDm(configuration); | |
| 50 | + assertNull(configuration.getDatabaseType()); | |
| 51 | + assertTrue(configuration.isBulkInsertEnabled()); | |
| 52 | + } | |
| 53 | + | |
| 54 | + @Test | |
| 55 | + void testDmSchemaAndProcess() throws Exception { | |
| 56 | + DataSource dataSource = dmDataSource(); | |
| 57 | + try (Connection connection = dataSource.getConnection()) { | |
| 58 | + assertEquals("DM DBMS", connection.getMetaData().getDatabaseProductName()); | |
| 59 | + } | |
| 60 | + String instanceId; | |
| 61 | + SpringProcessEngineConfiguration first = configuration(dataSource); | |
| 62 | + first.setDatabaseSchemaUpdate("true"); | |
| 63 | + configureDm(first); | |
| 64 | + assertEquals("oracle", first.getDatabaseType()); | |
| 65 | + ProcessEngine engine = first.buildProcessEngine(); | |
| 66 | + try { | |
| 67 | + engine.getRepositoryService().createDeployment().addClasspathResource("approval.bpmn20.xml").deploy(); | |
| 68 | + ProcessInstance instance = engine.getRuntimeService().startProcessInstanceByKey("dmApproval", "dm-test", | |
| 69 | + Collections.<String, Object>singletonMap("amount", 100)); | |
| 70 | + instanceId = instance.getId(); | |
| 71 | + Task task = engine.getTaskService().createTaskQuery().processInstanceId(instanceId).singleResult(); | |
| 72 | + assertEquals("审批一", task.getName()); | |
| 73 | + engine.getTaskService().setAssignee(task.getId(), "1"); | |
| 74 | + engine.getTaskService().complete(task.getId()); | |
| 75 | + assertEquals("审批二", engine.getTaskService().createTaskQuery().processInstanceId(instanceId).singleResult().getName()); | |
| 76 | + } finally { | |
| 77 | + engine.close(); | |
| 78 | + } | |
| 79 | + // 再次开启 schema-update,验证已存在的工作流表不会重复创建 | |
| 80 | + SpringProcessEngineConfiguration second = configuration(dataSource); | |
| 81 | + second.setDatabaseSchemaUpdate("true"); | |
| 82 | + configureDm(second); | |
| 83 | + engine = second.buildProcessEngine(); | |
| 84 | + try { | |
| 85 | + Task task = engine.getTaskService().createTaskQuery().processInstanceId(instanceId).singleResult(); | |
| 86 | + engine.getTaskService().complete(task.getId()); | |
| 87 | + assertNull(engine.getRuntimeService().createProcessInstanceQuery().processInstanceId(instanceId).singleResult()); | |
| 88 | + HistoricProcessInstance history = engine.getHistoryService().createHistoricProcessInstanceQuery() | |
| 89 | + .processInstanceId(instanceId).singleResult(); | |
| 90 | + assertNotNull(history.getEndTime()); | |
| 91 | + assertEquals(2, engine.getHistoryService().createHistoricTaskInstanceQuery().processInstanceId(instanceId).finished().count()); | |
| 92 | + assertEquals(1, engine.getRepositoryService().createProcessDefinitionQuery().processDefinitionKey("dmApproval").listPage(0, 1).size()); | |
| 93 | + } finally { | |
| 94 | + engine.close(); | |
| 95 | + } | |
| 96 | + } | |
| 97 | + | |
| 98 | + @Test | |
| 99 | + void testDmReturnAndReject() { | |
| 100 | + ProcessEngine engine = dmEngine(); | |
| 101 | + try { | |
| 102 | + engine.getRepositoryService().createDeployment().addClasspathResource("approval.bpmn20.xml").deploy(); | |
| 103 | + ProcessInstance instance = engine.getRuntimeService().startProcessInstanceByKey("dmApproval"); | |
| 104 | + Task task = engine.getTaskService().createTaskQuery().processInstanceId(instance.getId()).singleResult(); | |
| 105 | + engine.getTaskService().complete(task.getId()); | |
| 106 | + engine.getRuntimeService().createChangeActivityStateBuilder().processInstanceId(instance.getId()) | |
| 107 | + .moveActivityIdTo("second", "first").changeState(); | |
| 108 | + assertEquals("审批一", engine.getTaskService().createTaskQuery().processInstanceId(instance.getId()).singleResult().getName()); | |
| 109 | + engine.getRuntimeService().deleteProcessInstance(instance.getId(), "审批拒绝"); | |
| 110 | + assertEquals("审批拒绝", engine.getHistoryService().createHistoricProcessInstanceQuery() | |
| 111 | + .processInstanceId(instance.getId()).singleResult().getDeleteReason()); | |
| 112 | + } finally { | |
| 113 | + engine.close(); | |
| 114 | + } | |
| 115 | + } | |
| 116 | + | |
| 117 | + @Test | |
| 118 | + void testDmCancelAndVariables() { | |
| 119 | + ProcessEngine engine = dmEngine(); | |
| 120 | + try { | |
| 121 | + engine.getRepositoryService().createDeployment().addClasspathResource("approval.bpmn20.xml").deploy(); | |
| 122 | + String longText = String.join("", Collections.nCopies(3000, "达梦🙂")); | |
| 123 | + ProcessInstance instance = engine.getRuntimeService().startProcessInstanceByKey("dmApproval", "cancel-test", | |
| 124 | + Collections.<String, Object>singletonMap("longText", longText)); | |
| 125 | + engine.getRuntimeService().setVariable(instance.getId(), "approved", true); | |
| 126 | + assertEquals(longText, engine.getRuntimeService().getVariable(instance.getId(), "longText")); | |
| 127 | + assertEquals(true, engine.getRuntimeService().getVariable(instance.getId(), "approved")); | |
| 128 | + engine.getRuntimeService().deleteProcessInstance(instance.getId(), "发起人取消"); | |
| 129 | + assertEquals("发起人取消", engine.getHistoryService().createHistoricProcessInstanceQuery() | |
| 130 | + .processInstanceBusinessKey("cancel-test").orderByProcessInstanceStartTime().desc().listPage(0, 1).get(0).getDeleteReason()); | |
| 131 | + assertEquals(longText, engine.getHistoryService().createHistoricVariableInstanceQuery() | |
| 132 | + .processInstanceId(instance.getId()).variableName("longText").singleResult().getValue()); | |
| 133 | + } finally { | |
| 134 | + engine.close(); | |
| 135 | + } | |
| 136 | + } | |
| 137 | + | |
| 138 | + @Test | |
| 139 | + void testDmParallelGateway() { | |
| 140 | + ProcessEngine engine = dmEngine(); | |
| 141 | + try { | |
| 142 | + engine.getRepositoryService().createDeployment().addClasspathResource("parallel.bpmn20.xml").deploy(); | |
| 143 | + ProcessInstance instance = engine.getRuntimeService().startProcessInstanceByKey("dmParallel"); | |
| 144 | + List<Task> tasks = engine.getTaskService().createTaskQuery().processInstanceId(instance.getId()).list(); | |
| 145 | + assertEquals(2, tasks.size()); | |
| 146 | + engine.getTaskService().complete(tasks.get(0).getId()); | |
| 147 | + assertEquals(1, engine.getRuntimeService().createProcessInstanceQuery().processInstanceId(instance.getId()).count()); | |
| 148 | + engine.getTaskService().complete(tasks.get(1).getId()); | |
| 149 | + assertEquals(0, engine.getRuntimeService().createProcessInstanceQuery().processInstanceId(instance.getId()).count()); | |
| 150 | + } finally { | |
| 151 | + engine.close(); | |
| 152 | + } | |
| 153 | + } | |
| 154 | + | |
| 155 | + @Test | |
| 156 | + void testDmTimer() { | |
| 157 | + ProcessEngine engine = dmEngine(); | |
| 158 | + try { | |
| 159 | + engine.getRepositoryService().createDeployment().addClasspathResource("timer.bpmn20.xml").deploy(); | |
| 160 | + ProcessInstance instance = engine.getRuntimeService().startProcessInstanceByKey("dmTimer"); | |
| 161 | + String timerId = engine.getManagementService().createTimerJobQuery().processInstanceId(instance.getId()).singleResult().getId(); | |
| 162 | + String jobId = engine.getManagementService().moveTimerToExecutableJob(timerId).getId(); | |
| 163 | + engine.getManagementService().executeJob(jobId); | |
| 164 | + assertEquals(0, engine.getManagementService().createTimerJobQuery().processInstanceId(instance.getId()).count()); | |
| 165 | + assertEquals("定时后审批", engine.getTaskService().createTaskQuery().processInstanceId(instance.getId()).singleResult().getName()); | |
| 166 | + engine.getRuntimeService().deleteProcessInstance(instance.getId(), "测试结束"); | |
| 167 | + } finally { | |
| 168 | + engine.close(); | |
| 169 | + } | |
| 170 | + } | |
| 171 | + | |
| 172 | + @Test | |
| 173 | + void testDmDmnSchemaAndDecision() { | |
| 174 | + DataSource dataSource = dmDataSource(); | |
| 175 | + SpringDmnEngineConfiguration configuration = new SpringDmnEngineConfiguration(); | |
| 176 | + configuration.setDataSource(dataSource); | |
| 177 | + configuration.setTransactionManager(new DataSourceTransactionManager(dataSource)); | |
| 178 | + configureDm(configuration); | |
| 179 | + configuration.setDatabaseSchemaUpdate("true"); | |
| 180 | + DmnEngine engine = configuration.buildDmnEngine(); | |
| 181 | + try { | |
| 182 | + engine.getDmnRepositoryService().createDeployment().addClasspathResource("amount.dmn").deploy(); | |
| 183 | + Map<String, Object> result = engine.getDmnDecisionService().createExecuteDecisionBuilder() | |
| 184 | + .decisionKey("dmAmount").variable("amount", 100).executeWithSingleResult(); | |
| 185 | + assertEquals("通过", result.get("result")); | |
| 186 | + } finally { | |
| 187 | + engine.close(); | |
| 188 | + } | |
| 189 | + // DMN 在 6.8.0 使用 Liquibase,重复启动必须能识别已存在的 changelog 表 | |
| 190 | + configuration = new SpringDmnEngineConfiguration(); | |
| 191 | + configuration.setDataSource(dataSource); | |
| 192 | + configuration.setTransactionManager(new DataSourceTransactionManager(dataSource)); | |
| 193 | + configureDm(configuration); | |
| 194 | + configuration.setDatabaseSchemaUpdate("true"); | |
| 195 | + engine = configuration.buildDmnEngine(); | |
| 196 | + engine.close(); | |
| 197 | + } | |
| 198 | + | |
| 199 | + @Test | |
| 200 | + void testBootAutoConfiguration() { | |
| 201 | + dmDataSource(); | |
| 202 | + try (ConfigurableApplicationContext context = new SpringApplicationBuilder(TestApplication.class) | |
| 203 | + .web(WebApplicationType.NONE) | |
| 204 | + .properties("flowable.database-schema-update=true", "flowable.async-executor-activate=false", | |
| 205 | + "logging.level.root=WARN", "spring.main.banner-mode=off") | |
| 206 | + .run()) { | |
| 207 | + ProcessEngine engine = context.getBean(ProcessEngine.class); | |
| 208 | + assertEquals("oracle", engine.getProcessEngineConfiguration().getDatabaseType()); | |
| 209 | + assertFalse(((SpringProcessEngineConfiguration) engine.getProcessEngineConfiguration()).isBulkInsertEnabled()); | |
| 210 | + assertNotNull(engine.getRepositoryService().createDeploymentQuery().listPage(0, 1)); | |
| 211 | + DmnEngine dmn = context.getBean(DmnEngine.class); | |
| 212 | + assertEquals("oracle", dmn.getDmnEngineConfiguration().getDatabaseType()); | |
| 213 | + assertFalse(dmn.getDmnEngineConfiguration().isBulkInsertEnabled()); | |
| 214 | + assertEquals("通过", dmn.getDmnDecisionService().createExecuteDecisionBuilder() | |
| 215 | + .decisionKey("dmAutoTwo").variable("amount", 100).executeWithSingleResult().get("result")); | |
| 216 | + String model = resourceText("/amount.dmn"); | |
| 217 | + dmn.getDmnRepositoryService().createDeployment().addString("one.dmn", model) | |
| 218 | + .addString("two.dmn", model.replace("dmAmount", "dmAmountTwo")).deploy(); | |
| 219 | + assertEquals("通过", dmn.getDmnDecisionService().createExecuteDecisionBuilder() | |
| 220 | + .decisionKey("dmAmountTwo").variable("amount", 100).executeWithSingleResult().get("result")); | |
| 221 | + EventRegistryEngine event = context.getBean(EventRegistryEngine.class); | |
| 222 | + assertEquals("oracle", event.getEventRegistryEngineConfiguration().getDatabaseType()); | |
| 223 | + assertFalse(event.getEventRegistryEngineConfiguration().isBulkInsertEnabled()); | |
| 224 | + assertTrue(event.getEventRepositoryService().createEventDefinitionQuery().eventDefinitionKey("dmAutoEventOne").count() > 0); | |
| 225 | + assertTrue(event.getEventRepositoryService().createEventDefinitionQuery().eventDefinitionKey("dmAutoEventTwo").count() > 0); | |
| 226 | + EventDeployment deployment = event.getEventRepositoryService().createDeployment() | |
| 227 | + .addString("one.event", "{\"key\":\"dmEventOne\",\"name\":\"One\",\"payload\":[]}") | |
| 228 | + .addString("two.event", "{\"key\":\"dmEventTwo\",\"name\":\"Two\",\"payload\":[]}").deploy(); | |
| 229 | + assertEquals(2, event.getEventRepositoryService().createEventDefinitionQuery().deploymentId(deployment.getId()).count()); | |
| 230 | + } | |
| 231 | + } | |
| 232 | + | |
| 233 | + private ProcessEngine dmEngine() { | |
| 234 | + SpringProcessEngineConfiguration configuration = configuration(dmDataSource()); | |
| 235 | + configuration.setDatabaseSchemaUpdate("true"); | |
| 236 | + configureDm(configuration); | |
| 237 | + return configuration.buildProcessEngine(); | |
| 238 | + } | |
| 239 | + | |
| 240 | + private static DataSource dmDataSource() { | |
| 241 | + assumeTrue(System.getenv("DM_JDBC_URL") != null, "设置 DM_JDBC_URL 后执行真实达梦测试"); | |
| 242 | + return new DriverManagerDataSource(System.getenv("DM_JDBC_URL"), | |
| 243 | + System.getenv("DM_JDBC_USER"), System.getenv("DM_JDBC_PASSWORD")); | |
| 244 | + } | |
| 245 | + | |
| 246 | + @Test | |
| 247 | + void testDmTransactionRollback() { | |
| 248 | + ProcessEngine engine = dmEngine(); | |
| 249 | + try { | |
| 250 | + engine.getRepositoryService().createDeployment().addClasspathResource("approval.bpmn20.xml").deploy(); | |
| 251 | + String businessKey = UUID.randomUUID().toString(); | |
| 252 | + SpringProcessEngineConfiguration configuration = (SpringProcessEngineConfiguration) engine.getProcessEngineConfiguration(); | |
| 253 | + TransactionTemplate transaction = new TransactionTemplate(configuration.getTransactionManager()); | |
| 254 | + assertThrows(IllegalStateException.class, () -> transaction.execute(status -> { | |
| 255 | + engine.getRuntimeService().startProcessInstanceByKey("dmApproval", businessKey); | |
| 256 | + throw new IllegalStateException("回滚测试"); | |
| 257 | + })); | |
| 258 | + assertEquals(0, engine.getRuntimeService().createProcessInstanceQuery().processInstanceBusinessKey(businessKey).count()); | |
| 259 | + assertEquals(0, engine.getHistoryService().createHistoricProcessInstanceQuery().processInstanceBusinessKey(businessKey).count()); | |
| 260 | + } finally { | |
| 261 | + engine.close(); | |
| 262 | + } | |
| 263 | + } | |
| 264 | + | |
| 265 | + private void configureDm(Object configuration) { | |
| 266 | + DmFlowableConfiguration.dmEngineConfigurationBeanPostProcessor().postProcessBeforeInitialization(configuration, "testEngineConfiguration"); | |
| 267 | + } | |
| 268 | + | |
| 269 | + private String resourceText(String name) { | |
| 270 | + try (InputStream input = getClass().getResourceAsStream(name); | |
| 271 | + ByteArrayOutputStream output = new ByteArrayOutputStream()) { | |
| 272 | + byte[] buffer = new byte[1024]; | |
| 273 | + int count; | |
| 274 | + while ((count = input.read(buffer)) != -1) { | |
| 275 | + output.write(buffer, 0, count); | |
| 276 | + } | |
| 277 | + return new String(output.toByteArray(), StandardCharsets.UTF_8); | |
| 278 | + } catch (java.io.IOException e) { | |
| 279 | + throw new IllegalStateException(e); | |
| 280 | + } | |
| 281 | + } | |
| 282 | + | |
| 283 | + private SpringProcessEngineConfiguration configuration(DataSource dataSource) { | |
| 284 | + SpringProcessEngineConfiguration configuration = new SpringProcessEngineConfiguration(); | |
| 285 | + configuration.setDataSource(dataSource); | |
| 286 | + configuration.setTransactionManager(new DataSourceTransactionManager(dataSource)); | |
| 287 | + configuration.setAsyncExecutorActivate(false); | |
| 288 | + return configuration; | |
| 289 | + } | |
| 290 | + | |
| 291 | + @Configuration(proxyBeanMethods = false) | |
| 292 | + @EnableAutoConfiguration | |
| 293 | + @Import(DmFlowableConfiguration.class) | |
| 294 | + static class TestApplication { | |
| 295 | + | |
| 296 | + @Bean | |
| 297 | + public DataSource dataSource() { | |
| 298 | + return dmDataSource(); | |
| 299 | + } | |
| 300 | + | |
| 301 | + } | |
| 302 | + | |
| 303 | +} | ... | ... |
sql/dm/flowable-patch-tests/src/test/liquibase/cn/iocoder/yudao/module/bpm/DmDatabaseTest.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.bpm; | |
| 2 | + | |
| 3 | +import liquibase.database.DatabaseConnection; | |
| 4 | +import liquibase.database.DatabaseFactory; | |
| 5 | +import liquibase.database.core.DmDatabase; | |
| 6 | +import liquibase.database.core.H2Database; | |
| 7 | +import liquibase.database.core.MySQLDatabase; | |
| 8 | +import liquibase.database.core.OracleDatabase; | |
| 9 | +import liquibase.database.jvm.JdbcConnection; | |
| 10 | +import liquibase.datatype.DataTypeFactory; | |
| 11 | +import liquibase.datatype.core.BooleanType; | |
| 12 | +import org.junit.jupiter.api.AfterEach; | |
| 13 | +import org.junit.jupiter.api.Test; | |
| 14 | +import org.springframework.jdbc.datasource.DriverManagerDataSource; | |
| 15 | + | |
| 16 | +import java.lang.reflect.Proxy; | |
| 17 | +import java.sql.Connection; | |
| 18 | + | |
| 19 | +import static org.junit.jupiter.api.Assertions.*; | |
| 20 | +import static org.junit.jupiter.api.Assumptions.assumeTrue; | |
| 21 | + | |
| 22 | +/** | |
| 23 | + * Flowable 6.8.0 的达梦 Liquibase SPI 测试 | |
| 24 | + * | |
| 25 | + * @author 芋道源码 | |
| 26 | + */ | |
| 27 | +class DmDatabaseTest { | |
| 28 | + | |
| 29 | + @AfterEach | |
| 30 | + void tearDown() { | |
| 31 | + DatabaseFactory.reset(); | |
| 32 | + } | |
| 33 | + | |
| 34 | + @Test | |
| 35 | + void testDriverAndOtherDatabases() throws Exception { | |
| 36 | + DmDatabase database = new DmDatabase(); | |
| 37 | + assertEquals("dm", database.getShortName()); | |
| 38 | + assertEquals("dm.jdbc.driver.DmDriver", database.getDefaultDriver("jdbc:dm://localhost:5236")); | |
| 39 | + assertNull(database.getDefaultDriver(null)); | |
| 40 | + assertNull(database.getDefaultDriver("jdbc:mysql://localhost/test")); | |
| 41 | + assertEquals(5236, database.getDefaultPort()); | |
| 42 | + DriverManagerDataSource dataSource = new DriverManagerDataSource("jdbc:h2:mem:dm_spi_regression", "sa", ""); | |
| 43 | + try (Connection connection = dataSource.getConnection()) { | |
| 44 | + assertInstanceOf(H2Database.class, DatabaseFactory.getInstance() | |
| 45 | + .findCorrectDatabaseImplementation(new JdbcConnection(connection))); | |
| 46 | + } | |
| 47 | + assertEquals(new BooleanType().toDatabaseDataType(new MySQLDatabase()).toString(), | |
| 48 | + DataTypeFactory.getInstance().fromDescription("boolean", new MySQLDatabase()) | |
| 49 | + .toDatabaseDataType(new MySQLDatabase()).toString()); | |
| 50 | + } | |
| 51 | + | |
| 52 | + @Test | |
| 53 | + void testOracleAndDmRegistrationOrder() throws Exception { | |
| 54 | + for (boolean dmFirst : new boolean[]{true, false}) { | |
| 55 | + DatabaseFactory factory = DatabaseFactory.getInstance(); | |
| 56 | + factory.clearRegistry(); | |
| 57 | + factory.register(dmFirst ? new DmDatabase() : new OracleDatabase()); | |
| 58 | + factory.register(dmFirst ? new OracleDatabase() : new DmDatabase()); | |
| 59 | + assertInstanceOf(DmDatabase.class, factory.findCorrectDatabaseImplementation(metadata("DM DBMS"))); | |
| 60 | + assertEquals(OracleDatabase.class, factory.findCorrectDatabaseImplementation(metadata("Oracle")).getClass()); | |
| 61 | + } | |
| 62 | + } | |
| 63 | + | |
| 64 | + @Test | |
| 65 | + void testDmMetadataAndBooleanType() throws Exception { | |
| 66 | + assumeTrue(System.getenv("DM_JDBC_URL") != null, "设置 DM_JDBC_URL 后执行真实达梦测试"); | |
| 67 | + DriverManagerDataSource dataSource = new DriverManagerDataSource(System.getenv("DM_JDBC_URL"), | |
| 68 | + System.getenv("DM_JDBC_USER"), System.getenv("DM_JDBC_PASSWORD")); | |
| 69 | + try (Connection connection = dataSource.getConnection()) { | |
| 70 | + liquibase.database.Database database = DatabaseFactory.getInstance() | |
| 71 | + .findCorrectDatabaseImplementation(new JdbcConnection(connection)); | |
| 72 | + assertInstanceOf(DmDatabase.class, database); | |
| 73 | + assertEquals("NUMBER(1)", DataTypeFactory.getInstance().fromDescription("boolean", database) | |
| 74 | + .toDatabaseDataType(database).toString()); | |
| 75 | + } | |
| 76 | + } | |
| 77 | + | |
| 78 | + private DatabaseConnection metadata(String product) { | |
| 79 | + return (DatabaseConnection) Proxy.newProxyInstance(getClass().getClassLoader(), | |
| 80 | + new Class[]{DatabaseConnection.class}, (proxy, method, arguments) -> { | |
| 81 | + if ("getDatabaseProductName".equals(method.getName())) return product; | |
| 82 | + if ("getConnectionUserName".equals(method.getName())) return "TEST_USER"; | |
| 83 | + if ("getURL".equals(method.getName())) return "DM DBMS".equals(product) ? "jdbc:dm://test" : "jdbc:oracle:thin:@test"; | |
| 84 | + if (method.getReturnType() == int.class) return 8; | |
| 85 | + if (method.getReturnType() == boolean.class) return false; | |
| 86 | + return null; | |
| 87 | + }); | |
| 88 | + } | |
| 89 | + | |
| 90 | +} | ... | ... |
sql/dm/flowable-patch-tests/src/test/resources/amount.dmn
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | |
| 2 | +<definitions xmlns="https://www.omg.org/spec/DMN/20191111/MODEL/" | |
| 3 | + id="dmRules" name="达梦决策测试" namespace="http://www.iocoder.cn/dm-test"> | |
| 4 | + <decision id="dmAmount" name="金额审核"> | |
| 5 | + <decisionTable id="amountTable" hitPolicy="FIRST"> | |
| 6 | + <input id="inputAmount"><inputExpression id="amountExpression" typeRef="number"><text>amount</text></inputExpression></input> | |
| 7 | + <output id="outputResult" name="result" typeRef="string"/> | |
| 8 | + <rule id="accepted"> | |
| 9 | + <inputEntry id="amountCondition"><text>>= 0</text></inputEntry> | |
| 10 | + <outputEntry id="resultValue"><text>"通过"</text></outputEntry> | |
| 11 | + </rule> | |
| 12 | + </decisionTable> | |
| 13 | + </decision> | |
| 14 | +</definitions> | ... | ... |
sql/dm/flowable-patch-tests/src/test/resources/approval.bpmn20.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | |
| 2 | +<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" | |
| 3 | + targetNamespace="http://www.iocoder.cn/dm-test"> | |
| 4 | + <process id="dmApproval" name="达梦审批回归" isExecutable="true"> | |
| 5 | + <startEvent id="start"/> | |
| 6 | + <userTask id="first" name="审批一"/> | |
| 7 | + <userTask id="second" name="审批二"/> | |
| 8 | + <endEvent id="end"/> | |
| 9 | + <sequenceFlow id="f1" sourceRef="start" targetRef="first"/> | |
| 10 | + <sequenceFlow id="f2" sourceRef="first" targetRef="second"/> | |
| 11 | + <sequenceFlow id="f3" sourceRef="second" targetRef="end"/> | |
| 12 | + </process> | |
| 13 | +</definitions> | ... | ... |
sql/dm/flowable-patch-tests/src/test/resources/dmn/auto.dmn
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | |
| 2 | +<definitions xmlns="https://www.omg.org/spec/DMN/20191111/MODEL/" | |
| 3 | + id="autoRules" name="自动部署测试" namespace="http://www.iocoder.cn/dm-test"> | |
| 4 | + <decision id="dmAutoOne" name="自动决策一"> | |
| 5 | + <decisionTable id="tableOne" hitPolicy="FIRST"> | |
| 6 | + <input id="inputOne"><inputExpression id="expressionOne" typeRef="number"><text>amount</text></inputExpression></input> | |
| 7 | + <output id="outputOne" name="result" typeRef="string"/> | |
| 8 | + <rule id="ruleOne"> | |
| 9 | + <inputEntry id="conditionOne"><text>>= 0</text></inputEntry> | |
| 10 | + <outputEntry id="valueOne"><text>"通过"</text></outputEntry> | |
| 11 | + </rule> | |
| 12 | + </decisionTable> | |
| 13 | + </decision> | |
| 14 | + <decision id="dmAutoTwo" name="自动决策二"> | |
| 15 | + <decisionTable id="tableTwo" hitPolicy="FIRST"> | |
| 16 | + <input id="inputTwo"><inputExpression id="expressionTwo" typeRef="number"><text>amount</text></inputExpression></input> | |
| 17 | + <output id="outputTwo" name="result" typeRef="string"/> | |
| 18 | + <rule id="ruleTwo"> | |
| 19 | + <inputEntry id="conditionTwo"><text>>= 0</text></inputEntry> | |
| 20 | + <outputEntry id="valueTwo"><text>"通过"</text></outputEntry> | |
| 21 | + </rule> | |
| 22 | + </decisionTable> | |
| 23 | + </decision> | |
| 24 | +</definitions> | ... | ... |
sql/dm/flowable-patch-tests/src/test/resources/eventregistry/one.event
0 → 100644
| 1 | +{"key":"dmAutoEventOne","name":"自动事件一","payload":[]} | ... | ... |
sql/dm/flowable-patch-tests/src/test/resources/eventregistry/two.event
0 → 100644
| 1 | +{"key":"dmAutoEventTwo","name":"自动事件二","payload":[]} | ... | ... |
sql/dm/flowable-patch-tests/src/test/resources/logback-test.xml
0 → 100644
sql/dm/flowable-patch-tests/src/test/resources/parallel.bpmn20.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | |
| 2 | +<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" | |
| 3 | + targetNamespace="http://www.iocoder.cn/dm-test"> | |
| 4 | + <process id="dmParallel" isExecutable="true"> | |
| 5 | + <startEvent id="start"/> | |
| 6 | + <parallelGateway id="fork"/> | |
| 7 | + <userTask id="left" name="并行审批一"/> | |
| 8 | + <userTask id="right" name="并行审批二"/> | |
| 9 | + <parallelGateway id="join"/> | |
| 10 | + <endEvent id="end"/> | |
| 11 | + <sequenceFlow id="f1" sourceRef="start" targetRef="fork"/> | |
| 12 | + <sequenceFlow id="f2" sourceRef="fork" targetRef="left"/> | |
| 13 | + <sequenceFlow id="f3" sourceRef="fork" targetRef="right"/> | |
| 14 | + <sequenceFlow id="f4" sourceRef="left" targetRef="join"/> | |
| 15 | + <sequenceFlow id="f5" sourceRef="right" targetRef="join"/> | |
| 16 | + <sequenceFlow id="f6" sourceRef="join" targetRef="end"/> | |
| 17 | + </process> | |
| 18 | +</definitions> | ... | ... |
sql/dm/flowable-patch-tests/src/test/resources/timer.bpmn20.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | |
| 2 | +<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" | |
| 3 | + targetNamespace="http://www.iocoder.cn/dm-test"> | |
| 4 | + <process id="dmTimer" isExecutable="true"> | |
| 5 | + <startEvent id="start"/> | |
| 6 | + <intermediateCatchEvent id="wait"> | |
| 7 | + <timerEventDefinition><timeDuration>P1D</timeDuration></timerEventDefinition> | |
| 8 | + </intermediateCatchEvent> | |
| 9 | + <userTask id="approval" name="定时后审批"/> | |
| 10 | + <endEvent id="end"/> | |
| 11 | + <sequenceFlow id="f1" sourceRef="start" targetRef="wait"/> | |
| 12 | + <sequenceFlow id="f2" sourceRef="wait" targetRef="approval"/> | |
| 13 | + <sequenceFlow id="f3" sourceRef="approval" targetRef="end"/> | |
| 14 | + </process> | |
| 15 | +</definitions> | ... | ... |
sql/dm/flowable-patch/src/main/java/liquibase/database/core/DmDatabase.java deleted
| 1 | -package liquibase.database.core; | |
| 2 | - | |
| 3 | -import java.lang.reflect.Method; | |
| 4 | -import java.sql.Connection; | |
| 5 | -import java.sql.ResultSet; | |
| 6 | -import java.sql.SQLException; | |
| 7 | -import java.sql.Statement; | |
| 8 | -import java.util.ArrayList; | |
| 9 | -import java.util.Arrays; | |
| 10 | -import java.util.HashSet; | |
| 11 | -import java.util.List; | |
| 12 | -import java.util.Locale; | |
| 13 | -import java.util.Map; | |
| 14 | -import java.util.Properties; | |
| 15 | -import java.util.Set; | |
| 16 | -import java.util.regex.Matcher; | |
| 17 | -import java.util.regex.Pattern; | |
| 18 | -import liquibase.CatalogAndSchema; | |
| 19 | -import liquibase.Scope; | |
| 20 | -import liquibase.database.AbstractJdbcDatabase; | |
| 21 | -import liquibase.database.DatabaseConnection; | |
| 22 | -import liquibase.database.OfflineConnection; | |
| 23 | -import liquibase.database.jvm.JdbcConnection; | |
| 24 | -import liquibase.exception.DatabaseException; | |
| 25 | -import liquibase.exception.UnexpectedLiquibaseException; | |
| 26 | -import liquibase.exception.ValidationErrors; | |
| 27 | -import liquibase.executor.ExecutorService; | |
| 28 | -import liquibase.statement.DatabaseFunction; | |
| 29 | -import liquibase.statement.SequenceCurrentValueFunction; | |
| 30 | -import liquibase.statement.SequenceNextValueFunction; | |
| 31 | -import liquibase.statement.core.RawCallStatement; | |
| 32 | -import liquibase.statement.core.RawSqlStatement; | |
| 33 | -import liquibase.structure.DatabaseObject; | |
| 34 | -import liquibase.structure.core.Catalog; | |
| 35 | -import liquibase.structure.core.Index; | |
| 36 | -import liquibase.structure.core.PrimaryKey; | |
| 37 | -import liquibase.structure.core.Schema; | |
| 38 | -import liquibase.util.JdbcUtils; | |
| 39 | -import liquibase.util.StringUtil; | |
| 40 | - | |
| 41 | -public class DmDatabase extends AbstractJdbcDatabase { | |
| 42 | - private static final String PRODUCT_NAME = "DM DBMS"; | |
| 43 | - | |
| 44 | - @Override | |
| 45 | - protected String getDefaultDatabaseProductName() { | |
| 46 | - return PRODUCT_NAME; | |
| 47 | - } | |
| 48 | - | |
| 49 | - /** | |
| 50 | - * Is this AbstractDatabase subclass the correct one to use for the given connection. | |
| 51 | - * | |
| 52 | - * @param conn | |
| 53 | - */ | |
| 54 | - @Override | |
| 55 | - public boolean isCorrectDatabaseImplementation(DatabaseConnection conn) throws DatabaseException { | |
| 56 | - return PRODUCT_NAME.equalsIgnoreCase(conn.getDatabaseProductName()); | |
| 57 | - } | |
| 58 | - | |
| 59 | - /** | |
| 60 | - * If this database understands the given url, return the default driver class name. Otherwise return null. | |
| 61 | - * | |
| 62 | - * @param url | |
| 63 | - */ | |
| 64 | - @Override | |
| 65 | - public String getDefaultDriver(String url) { | |
| 66 | - if(url.startsWith("jdbc:dm")) { | |
| 67 | - return "dm.jdbc.driver.DmDriver"; | |
| 68 | - } | |
| 69 | - | |
| 70 | - return null; | |
| 71 | - } | |
| 72 | - | |
| 73 | - /** | |
| 74 | - * Returns an all-lower-case short name of the product. Used for end-user selecting of database type | |
| 75 | - * such as the DBMS precondition. | |
| 76 | - */ | |
| 77 | - @Override | |
| 78 | - public String getShortName() { | |
| 79 | - return "dm"; | |
| 80 | - } | |
| 81 | - | |
| 82 | - @Override | |
| 83 | - public Integer getDefaultPort() { | |
| 84 | - return 5236; | |
| 85 | - } | |
| 86 | - | |
| 87 | - /** | |
| 88 | - * Returns whether this database support initially deferrable columns. | |
| 89 | - */ | |
| 90 | - @Override | |
| 91 | - public boolean supportsInitiallyDeferrableColumns() { | |
| 92 | - return true; | |
| 93 | - } | |
| 94 | - | |
| 95 | - @Override | |
| 96 | - public boolean supportsTablespaces() { | |
| 97 | - return true; | |
| 98 | - } | |
| 99 | - | |
| 100 | - @Override | |
| 101 | - public int getPriority() { | |
| 102 | - return PRIORITY_DEFAULT; | |
| 103 | - } | |
| 104 | - | |
| 105 | - private static final Pattern PROXY_USER = Pattern.compile(".*(?:thin|oci)\\:(.+)/@.*"); | |
| 106 | - | |
| 107 | - protected final int SHORT_IDENTIFIERS_LENGTH = 30; | |
| 108 | - protected final int LONG_IDENTIFIERS_LEGNTH = 128; | |
| 109 | - public static final int ORACLE_12C_MAJOR_VERSION = 12; | |
| 110 | - | |
| 111 | - private Set<String> reservedWords = new HashSet<>(); | |
| 112 | - private Set<String> userDefinedTypes; | |
| 113 | - private Map<String, String> savedSessionNlsSettings; | |
| 114 | - | |
| 115 | - private Boolean canAccessDbaRecycleBin; | |
| 116 | - private Integer databaseMajorVersion; | |
| 117 | - private Integer databaseMinorVersion; | |
| 118 | - | |
| 119 | - /** | |
| 120 | - * Default constructor for an object that represents the Oracle Database DBMS. | |
| 121 | - */ | |
| 122 | - public DmDatabase() { | |
| 123 | - super.unquotedObjectsAreUppercased = true; | |
| 124 | - //noinspection HardCodedStringLiteral | |
| 125 | - super.setCurrentDateTimeFunction("SYSTIMESTAMP"); | |
| 126 | - // Setting list of Oracle's native functions | |
| 127 | - //noinspection HardCodedStringLiteral | |
| 128 | - dateFunctions.add(new DatabaseFunction("SYSDATE")); | |
| 129 | - //noinspection HardCodedStringLiteral | |
| 130 | - dateFunctions.add(new DatabaseFunction("SYSTIMESTAMP")); | |
| 131 | - //noinspection HardCodedStringLiteral | |
| 132 | - dateFunctions.add(new DatabaseFunction("CURRENT_TIMESTAMP")); | |
| 133 | - //noinspection HardCodedStringLiteral | |
| 134 | - super.sequenceNextValueFunction = "%s.nextval"; | |
| 135 | - //noinspection HardCodedStringLiteral | |
| 136 | - super.sequenceCurrentValueFunction = "%s.currval"; | |
| 137 | - } | |
| 138 | - | |
| 139 | - private void tryProxySession(final String url, final Connection con) { | |
| 140 | - Matcher m = PROXY_USER.matcher(url); | |
| 141 | - if (m.matches()) { | |
| 142 | - Properties props = new Properties(); | |
| 143 | - props.put("PROXY_USER_NAME", m.group(1)); | |
| 144 | - try { | |
| 145 | - Method method = con.getClass().getMethod("openProxySession", int.class, Properties.class); | |
| 146 | - method.setAccessible(true); | |
| 147 | - method.invoke(con, 1, props); | |
| 148 | - } catch (Exception e) { | |
| 149 | - Scope.getCurrentScope().getLog(getClass()).info("Could not open proxy session on OracleDatabase: " + e.getCause().getMessage()); | |
| 150 | - } | |
| 151 | - } | |
| 152 | - } | |
| 153 | - | |
| 154 | - @Override | |
| 155 | - public int getDatabaseMajorVersion() throws DatabaseException { | |
| 156 | - if (databaseMajorVersion == null) { | |
| 157 | - return super.getDatabaseMajorVersion(); | |
| 158 | - } else { | |
| 159 | - return databaseMajorVersion; | |
| 160 | - } | |
| 161 | - } | |
| 162 | - | |
| 163 | - @Override | |
| 164 | - public int getDatabaseMinorVersion() throws DatabaseException { | |
| 165 | - if (databaseMinorVersion == null) { | |
| 166 | - return super.getDatabaseMinorVersion(); | |
| 167 | - } else { | |
| 168 | - return databaseMinorVersion; | |
| 169 | - } | |
| 170 | - } | |
| 171 | - | |
| 172 | - @Override | |
| 173 | - public String getJdbcCatalogName(CatalogAndSchema schema) { | |
| 174 | - return null; | |
| 175 | - } | |
| 176 | - | |
| 177 | - @Override | |
| 178 | - public String getJdbcSchemaName(CatalogAndSchema schema) { | |
| 179 | - return correctObjectName((schema.getCatalogName() == null) ? schema.getSchemaName() : schema.getCatalogName(), Schema.class); | |
| 180 | - } | |
| 181 | - | |
| 182 | - @Override | |
| 183 | - protected String getAutoIncrementClause(final String generationType, final Boolean defaultOnNull) { | |
| 184 | - if (StringUtil.isEmpty(generationType)) { | |
| 185 | - return super.getAutoIncrementClause(); | |
| 186 | - } | |
| 187 | - | |
| 188 | - String autoIncrementClause = "GENERATED %s AS IDENTITY"; // %s -- [ ALWAYS | BY DEFAULT [ ON NULL ] ] | |
| 189 | - String generationStrategy = generationType; | |
| 190 | - if (Boolean.TRUE.equals(defaultOnNull) && generationType.toUpperCase().equals("BY DEFAULT")) { | |
| 191 | - generationStrategy += " ON NULL"; | |
| 192 | - } | |
| 193 | - return String.format(autoIncrementClause, generationStrategy); | |
| 194 | - } | |
| 195 | - | |
| 196 | - @Override | |
| 197 | - public String generatePrimaryKeyName(String tableName) { | |
| 198 | - if (tableName.length() > 27) { | |
| 199 | - //noinspection HardCodedStringLiteral | |
| 200 | - return "PK_" + tableName.toUpperCase(Locale.US).substring(0, 27); | |
| 201 | - } else { | |
| 202 | - //noinspection HardCodedStringLiteral | |
| 203 | - return "PK_" + tableName.toUpperCase(Locale.US); | |
| 204 | - } | |
| 205 | - } | |
| 206 | - | |
| 207 | - @Override | |
| 208 | - public boolean isReservedWord(String objectName) { | |
| 209 | - return reservedWords.contains(objectName.toUpperCase()); | |
| 210 | - } | |
| 211 | - | |
| 212 | - @Override | |
| 213 | - public boolean supportsSequences() { | |
| 214 | - return true; | |
| 215 | - } | |
| 216 | - | |
| 217 | - /** | |
| 218 | - * Oracle supports catalogs in liquibase terms | |
| 219 | - * | |
| 220 | - * @return false | |
| 221 | - */ | |
| 222 | - @Override | |
| 223 | - public boolean supportsSchemas() { | |
| 224 | - return false; | |
| 225 | - } | |
| 226 | - | |
| 227 | - @Override | |
| 228 | - protected String getConnectionCatalogName() throws DatabaseException { | |
| 229 | - if (getConnection() instanceof OfflineConnection) { | |
| 230 | - return getConnection().getCatalog(); | |
| 231 | - } | |
| 232 | - try { | |
| 233 | - //noinspection HardCodedStringLiteral | |
| 234 | - return Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor("jdbc", this).queryForObject(new RawCallStatement("select sys_context( 'userenv', 'current_schema' ) from dual"), String.class); | |
| 235 | - } catch (Exception e) { | |
| 236 | - //noinspection HardCodedStringLiteral | |
| 237 | - Scope.getCurrentScope().getLog(getClass()).info("Error getting default schema", e); | |
| 238 | - } | |
| 239 | - return null; | |
| 240 | - } | |
| 241 | - | |
| 242 | - @Override | |
| 243 | - public String getDefaultCatalogName() {//NOPMD | |
| 244 | - return (super.getDefaultCatalogName() == null) ? null : super.getDefaultCatalogName().toUpperCase(Locale.US); | |
| 245 | - } | |
| 246 | - | |
| 247 | - /** | |
| 248 | - * <p>Returns an Oracle date literal with the same value as a string formatted using ISO 8601.</p> | |
| 249 | - * | |
| 250 | - * <p>Convert an ISO8601 date string to one of the following results: | |
| 251 | - * to_date('1995-05-23', 'YYYY-MM-DD') | |
| 252 | - * to_date('1995-05-23 09:23:59', 'YYYY-MM-DD HH24:MI:SS')</p> | |
| 253 | - * <p> | |
| 254 | - * Implementation restriction:<br> | |
| 255 | - * Currently, only the following subsets of ISO8601 are supported:<br> | |
| 256 | - * <ul> | |
| 257 | - * <li>YYYY-MM-DD</li> | |
| 258 | - * <li>YYYY-MM-DDThh:mm:ss</li> | |
| 259 | - * </ul> | |
| 260 | - */ | |
| 261 | - @Override | |
| 262 | - public String getDateLiteral(String isoDate) { | |
| 263 | - String normalLiteral = super.getDateLiteral(isoDate); | |
| 264 | - | |
| 265 | - if (isDateOnly(isoDate)) { | |
| 266 | - return "TO_DATE(" + normalLiteral + ", 'YYYY-MM-DD')"; | |
| 267 | - } else if (isTimeOnly(isoDate)) { | |
| 268 | - return "TO_DATE(" + normalLiteral + ", 'HH24:MI:SS')"; | |
| 269 | - } else if (isTimestamp(isoDate)) { | |
| 270 | - return "TO_TIMESTAMP(" + normalLiteral + ", 'YYYY-MM-DD HH24:MI:SS.FF')"; | |
| 271 | - } else if (isDateTime(isoDate)) { | |
| 272 | - int seppos = normalLiteral.lastIndexOf('.'); | |
| 273 | - if (seppos != -1) { | |
| 274 | - normalLiteral = normalLiteral.substring(0, seppos) + "'"; | |
| 275 | - } | |
| 276 | - return "TO_DATE(" + normalLiteral + ", 'YYYY-MM-DD HH24:MI:SS')"; | |
| 277 | - } | |
| 278 | - return "UNSUPPORTED:" + isoDate; | |
| 279 | - } | |
| 280 | - | |
| 281 | - @Override | |
| 282 | - public boolean isSystemObject(DatabaseObject example) { | |
| 283 | - if (example == null) { | |
| 284 | - return false; | |
| 285 | - } | |
| 286 | - | |
| 287 | - if (this.isLiquibaseObject(example)) { | |
| 288 | - return false; | |
| 289 | - } | |
| 290 | - | |
| 291 | - if (example instanceof Schema) { | |
| 292 | - //noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral | |
| 293 | - if ("SYSTEM".equals(example.getName()) || "SYS".equals(example.getName()) || "CTXSYS".equals(example.getName()) || "XDB".equals(example.getName())) { | |
| 294 | - return true; | |
| 295 | - } | |
| 296 | - //noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral | |
| 297 | - if ("SYSTEM".equals(example.getSchema().getCatalogName()) || "SYS".equals(example.getSchema().getCatalogName()) || "CTXSYS".equals(example.getSchema().getCatalogName()) || "XDB".equals(example.getSchema().getCatalogName())) { | |
| 298 | - return true; | |
| 299 | - } | |
| 300 | - } else if (isSystemObject(example.getSchema())) { | |
| 301 | - return true; | |
| 302 | - } | |
| 303 | - if (example instanceof Catalog) { | |
| 304 | - //noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral | |
| 305 | - if (("SYSTEM".equals(example.getName()) || "SYS".equals(example.getName()) || "CTXSYS".equals(example.getName()) || "XDB".equals(example.getName()))) { | |
| 306 | - return true; | |
| 307 | - } | |
| 308 | - } else if (example.getName() != null) { | |
| 309 | - //noinspection HardCodedStringLiteral | |
| 310 | - if (example.getName().startsWith("BIN$")) { //oracle deleted table | |
| 311 | - boolean filteredInOriginalQuery = this.canAccessDbaRecycleBin(); | |
| 312 | - if (!filteredInOriginalQuery) { | |
| 313 | - filteredInOriginalQuery = StringUtil.trimToEmpty(example.getSchema().getName()).equalsIgnoreCase(this.getConnection().getConnectionUserName()); | |
| 314 | - } | |
| 315 | - | |
| 316 | - if (filteredInOriginalQuery) { | |
| 317 | - return !((example instanceof PrimaryKey) || (example instanceof Index) || (example instanceof | |
| 318 | - liquibase.statement.UniqueConstraint)); | |
| 319 | - } else { | |
| 320 | - return true; | |
| 321 | - } | |
| 322 | - } else //noinspection HardCodedStringLiteral | |
| 323 | - if (example.getName().startsWith("AQ$")) { //oracle AQ tables | |
| 324 | - return true; | |
| 325 | - } else //noinspection HardCodedStringLiteral | |
| 326 | - if (example.getName().startsWith("DR$")) { //oracle index tables | |
| 327 | - return true; | |
| 328 | - } else //noinspection HardCodedStringLiteral | |
| 329 | - if (example.getName().startsWith("SYS_IOT_OVER")) { //oracle system table | |
| 330 | - return true; | |
| 331 | - } else //noinspection HardCodedStringLiteral,HardCodedStringLiteral | |
| 332 | - if ((example.getName().startsWith("MDRT_") || example.getName().startsWith("MDRS_")) && example.getName().endsWith("$")) { | |
| 333 | - // CORE-1768 - Oracle creates these for spatial indices and will remove them when the index is removed. | |
| 334 | - return true; | |
| 335 | - } else //noinspection HardCodedStringLiteral | |
| 336 | - if (example.getName().startsWith("MLOG$_")) { //Created by materliaized view logs for every table that is part of a materialized view. Not available for DDL operations. | |
| 337 | - return true; | |
| 338 | - } else //noinspection HardCodedStringLiteral | |
| 339 | - if (example.getName().startsWith("RUPD$_")) { //Created by materialized view log tables using primary keys. Not available for DDL operations. | |
| 340 | - return true; | |
| 341 | - } else //noinspection HardCodedStringLiteral | |
| 342 | - if (example.getName().startsWith("WM$_")) { //Workspace Manager backup tables. | |
| 343 | - return true; | |
| 344 | - } else //noinspection HardCodedStringLiteral | |
| 345 | - if ("CREATE$JAVA$LOB$TABLE".equals(example.getName())) { //This table contains the name of the Java object, the date it was loaded, and has a BLOB column to store the Java object. | |
| 346 | - return true; | |
| 347 | - } else //noinspection HardCodedStringLiteral | |
| 348 | - if ("JAVA$CLASS$MD5$TABLE".equals(example.getName())) { //This is a hash table that tracks the loading of Java objects into a schema. | |
| 349 | - return true; | |
| 350 | - } else //noinspection HardCodedStringLiteral | |
| 351 | - if (example.getName().startsWith("ISEQ$$_")) { //System-generated sequence | |
| 352 | - return true; | |
| 353 | - } else //noinspection HardCodedStringLiteral | |
| 354 | - if (example.getName().startsWith("USLOG$")) { //for update materialized view | |
| 355 | - return true; | |
| 356 | - } else if (example.getName().startsWith("SYS_FBA")) { //for Flashback tables | |
| 357 | - return true; | |
| 358 | - } | |
| 359 | - } | |
| 360 | - | |
| 361 | - return super.isSystemObject(example); | |
| 362 | - } | |
| 363 | - | |
| 364 | - @Override | |
| 365 | - public boolean supportsAutoIncrement() { | |
| 366 | - // Oracle supports Identity beginning with version 12c | |
| 367 | - boolean isAutoIncrementSupported = false; | |
| 368 | - | |
| 369 | - try { | |
| 370 | - if (getDatabaseMajorVersion() >= 12) { | |
| 371 | - isAutoIncrementSupported = true; | |
| 372 | - } | |
| 373 | - | |
| 374 | - // Returning true will generate create table command with 'IDENTITY' clause, example: | |
| 375 | - // CREATE TABLE AutoIncTest (IDPrimaryKey NUMBER(19) GENERATED BY DEFAULT AS IDENTITY NOT NULL, TypeID NUMBER(3) NOT NULL, Description NVARCHAR2(50), CONSTRAINT PK_AutoIncTest PRIMARY KEY (IDPrimaryKey)); | |
| 376 | - | |
| 377 | - // While returning false will continue to generate create table command without 'IDENTITY' clause, example: | |
| 378 | - // CREATE TABLE AutoIncTest (IDPrimaryKey NUMBER(19) NOT NULL, TypeID NUMBER(3) NOT NULL, Description NVARCHAR2(50), CONSTRAINT PK_AutoIncTest PRIMARY KEY (IDPrimaryKey)); | |
| 379 | - | |
| 380 | - } catch (DatabaseException ex) { | |
| 381 | - isAutoIncrementSupported = false; | |
| 382 | - } | |
| 383 | - | |
| 384 | - return isAutoIncrementSupported; | |
| 385 | - } | |
| 386 | - | |
| 387 | - | |
| 388 | -// public Set<UniqueConstraint> findUniqueConstraints(String schema) throws DatabaseException { | |
| 389 | -// Set<UniqueConstraint> returnSet = new HashSet<UniqueConstraint>(); | |
| 390 | -// | |
| 391 | -// List<Map> maps = new Executor(this).queryForList(new RawSqlStatement("SELECT UC.CONSTRAINT_NAME, UCC.TABLE_NAME, UCC.COLUMN_NAME FROM USER_CONSTRAINTS UC, USER_CONS_COLUMNS UCC WHERE UC.CONSTRAINT_NAME=UCC.CONSTRAINT_NAME AND CONSTRAINT_TYPE='U' ORDER BY UC.CONSTRAINT_NAME")); | |
| 392 | -// | |
| 393 | -// UniqueConstraint constraint = null; | |
| 394 | -// for (Map map : maps) { | |
| 395 | -// if (constraint == null || !constraint.getName().equals(constraint.getName())) { | |
| 396 | -// returnSet.add(constraint); | |
| 397 | -// Table table = new Table((String) map.get("TABLE_NAME")); | |
| 398 | -// constraint = new UniqueConstraint(map.get("CONSTRAINT_NAME").toString(), table); | |
| 399 | -// } | |
| 400 | -// } | |
| 401 | -// if (constraint != null) { | |
| 402 | -// returnSet.add(constraint); | |
| 403 | -// } | |
| 404 | -// | |
| 405 | -// return returnSet; | |
| 406 | -// } | |
| 407 | - | |
| 408 | - @Override | |
| 409 | - public boolean supportsRestrictForeignKeys() { | |
| 410 | - return false; | |
| 411 | - } | |
| 412 | - | |
| 413 | - @Override | |
| 414 | - public int getDataTypeMaxParameters(String dataTypeName) { | |
| 415 | - //noinspection HardCodedStringLiteral | |
| 416 | - if ("BINARY_FLOAT".equals(dataTypeName.toUpperCase())) { | |
| 417 | - return 0; | |
| 418 | - } | |
| 419 | - //noinspection HardCodedStringLiteral | |
| 420 | - if ("BINARY_DOUBLE".equals(dataTypeName.toUpperCase())) { | |
| 421 | - return 0; | |
| 422 | - } | |
| 423 | - return super.getDataTypeMaxParameters(dataTypeName); | |
| 424 | - } | |
| 425 | - | |
| 426 | - public String getSystemTableWhereClause(String tableNameColumn) { | |
| 427 | - List<String> clauses = new ArrayList<String>(Arrays.asList("BIN$", | |
| 428 | - "AQ$", | |
| 429 | - "DR$", | |
| 430 | - "SYS_IOT_OVER", | |
| 431 | - "MLOG$_", | |
| 432 | - "RUPD$_", | |
| 433 | - "WM$_", | |
| 434 | - "ISEQ$$_", | |
| 435 | - "USLOG$", | |
| 436 | - "SYS_FBA")); | |
| 437 | - | |
| 438 | - for (int i = 0;i<clauses.size(); i++) { | |
| 439 | - clauses.set(i, tableNameColumn+" NOT LIKE '"+clauses.get(i)+"%'"); | |
| 440 | - } | |
| 441 | - return "("+ StringUtil.join(clauses, " AND ") + ")"; | |
| 442 | - } | |
| 443 | - | |
| 444 | - @Override | |
| 445 | - public boolean jdbcCallsCatalogsSchemas() { | |
| 446 | - return true; | |
| 447 | - } | |
| 448 | - | |
| 449 | - public Set<String> getUserDefinedTypes() { | |
| 450 | - if (userDefinedTypes == null) { | |
| 451 | - userDefinedTypes = new HashSet<>(); | |
| 452 | - if ((getConnection() != null) && !(getConnection() instanceof OfflineConnection)) { | |
| 453 | - try { | |
| 454 | - try { | |
| 455 | - //noinspection HardCodedStringLiteral | |
| 456 | - userDefinedTypes.addAll(Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor("jdbc", this).queryForList(new RawSqlStatement("SELECT DISTINCT TYPE_NAME FROM ALL_TYPES"), String.class)); | |
| 457 | - } catch (DatabaseException e) { //fall back to USER_TYPES if the user cannot see ALL_TYPES | |
| 458 | - //noinspection HardCodedStringLiteral | |
| 459 | - userDefinedTypes.addAll(Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor("jdbc", this).queryForList(new RawSqlStatement("SELECT TYPE_NAME FROM USER_TYPES"), String.class)); | |
| 460 | - } | |
| 461 | - } catch (DatabaseException e) { | |
| 462 | - //ignore error | |
| 463 | - } | |
| 464 | - } | |
| 465 | - } | |
| 466 | - | |
| 467 | - return userDefinedTypes; | |
| 468 | - } | |
| 469 | - | |
| 470 | - @Override | |
| 471 | - public String generateDatabaseFunctionValue(DatabaseFunction databaseFunction) { | |
| 472 | - //noinspection HardCodedStringLiteral | |
| 473 | - if ((databaseFunction != null) && "current_timestamp".equalsIgnoreCase(databaseFunction.toString())) { | |
| 474 | - return databaseFunction.toString(); | |
| 475 | - } | |
| 476 | - if ((databaseFunction instanceof SequenceNextValueFunction) || (databaseFunction instanceof | |
| 477 | - SequenceCurrentValueFunction)) { | |
| 478 | - String quotedSeq = super.generateDatabaseFunctionValue(databaseFunction); | |
| 479 | - // replace "myschema.my_seq".nextval with "myschema"."my_seq".nextval | |
| 480 | - return quotedSeq.replaceFirst("\"([^\\.\"]+)\\.([^\\.\"]+)\"", "\"$1\".\"$2\""); | |
| 481 | - | |
| 482 | - } | |
| 483 | - | |
| 484 | - return super.generateDatabaseFunctionValue(databaseFunction); | |
| 485 | - } | |
| 486 | - | |
| 487 | - @Override | |
| 488 | - public ValidationErrors validate() { | |
| 489 | - ValidationErrors errors = super.validate(); | |
| 490 | - DatabaseConnection connection = getConnection(); | |
| 491 | - if ((connection == null) || (connection instanceof OfflineConnection)) { | |
| 492 | - //noinspection HardCodedStringLiteral | |
| 493 | - Scope.getCurrentScope().getLog(getClass()).info("Cannot validate offline database"); | |
| 494 | - return errors; | |
| 495 | - } | |
| 496 | - | |
| 497 | - if (!canAccessDbaRecycleBin()) { | |
| 498 | - errors.addWarning(getDbaRecycleBinWarning()); | |
| 499 | - } | |
| 500 | - | |
| 501 | - return errors; | |
| 502 | - | |
| 503 | - } | |
| 504 | - | |
| 505 | - public String getDbaRecycleBinWarning() { | |
| 506 | - //noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral, | |
| 507 | - // HardCodedStringLiteral | |
| 508 | - //noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral | |
| 509 | - return "Liquibase needs to access the DBA_RECYCLEBIN table so we can automatically handle the case where " + | |
| 510 | - "constraints are deleted and restored. Since Oracle doesn't properly restore the original table names " + | |
| 511 | - "referenced in the constraint, we use the information from the DBA_RECYCLEBIN to automatically correct this" + | |
| 512 | - " issue.\n" + | |
| 513 | - "\n" + | |
| 514 | - "The user you used to connect to the database (" + getConnection().getConnectionUserName() + | |
| 515 | - ") needs to have \"SELECT ON SYS.DBA_RECYCLEBIN\" permissions set before we can perform this operation. " + | |
| 516 | - "Please run the following SQL to set the appropriate permissions, and try running the command again.\n" + | |
| 517 | - "\n" + | |
| 518 | - " GRANT SELECT ON SYS.DBA_RECYCLEBIN TO " + getConnection().getConnectionUserName() + ";"; | |
| 519 | - } | |
| 520 | - | |
| 521 | - public boolean canAccessDbaRecycleBin() { | |
| 522 | - if (canAccessDbaRecycleBin == null) { | |
| 523 | - DatabaseConnection connection = getConnection(); | |
| 524 | - if ((connection == null) || (connection instanceof OfflineConnection)) { | |
| 525 | - return false; | |
| 526 | - } | |
| 527 | - | |
| 528 | - Statement statement = null; | |
| 529 | - try { | |
| 530 | - statement = ((JdbcConnection) connection).createStatement(); | |
| 531 | - @SuppressWarnings("HardCodedStringLiteral") ResultSet resultSet = statement.executeQuery("select 1 from dba_recyclebin where 0=1"); | |
| 532 | - resultSet.close(); //don't need to do anything with the result set, just make sure statement ran. | |
| 533 | - this.canAccessDbaRecycleBin = true; | |
| 534 | - } catch (Exception e) { | |
| 535 | - //noinspection HardCodedStringLiteral | |
| 536 | - if ((e instanceof SQLException) && e.getMessage().startsWith("ORA-00942")) { //ORA-00942: table or view does not exist | |
| 537 | - this.canAccessDbaRecycleBin = false; | |
| 538 | - } else { | |
| 539 | - //noinspection HardCodedStringLiteral | |
| 540 | - Scope.getCurrentScope().getLog(getClass()).warning("Cannot check dba_recyclebin access", e); | |
| 541 | - this.canAccessDbaRecycleBin = false; | |
| 542 | - } | |
| 543 | - } finally { | |
| 544 | - JdbcUtils.close(null, statement); | |
| 545 | - } | |
| 546 | - } | |
| 547 | - | |
| 548 | - return canAccessDbaRecycleBin; | |
| 549 | - } | |
| 550 | - | |
| 551 | - @Override | |
| 552 | - public boolean supportsNotNullConstraintNames() { | |
| 553 | - return true; | |
| 554 | - } | |
| 555 | - | |
| 556 | - /** | |
| 557 | - * Tests if the given String would be a valid identifier in Oracle DBMS. In Oracle, a valid identifier has | |
| 558 | - * the following form (case-insensitive comparison): | |
| 559 | - * 1st character: A-Z | |
| 560 | - * 2..n characters: A-Z0-9$_# | |
| 561 | - * The maximum length of an identifier differs by Oracle version and object type. | |
| 562 | - */ | |
| 563 | - public boolean isValidOracleIdentifier(String identifier, Class<? extends DatabaseObject> type) { | |
| 564 | - if ((identifier == null) || (identifier.length() < 1)) | |
| 565 | - return false; | |
| 566 | - | |
| 567 | - if (!identifier.matches("^(i?)[A-Z][A-Z0-9\\$\\_\\#]*$")) | |
| 568 | - return false; | |
| 569 | - | |
| 570 | - /* | |
| 571 | - * @todo It seems we currently do not have a class for tablespace identifiers, and all other classes | |
| 572 | - * we do know seem to be supported as 12cR2 long identifiers, so: | |
| 573 | - */ | |
| 574 | - return (identifier.length() <= LONG_IDENTIFIERS_LEGNTH); | |
| 575 | - } | |
| 576 | - | |
| 577 | - /** | |
| 578 | - * Returns the maximum number of bytes (NOT: characters) for an identifier. For Oracle <=12c Release 20, this | |
| 579 | - * is 30 bytes, and starting from 12cR2, up to 128 (except for tablespaces, PDB names and some other rather rare | |
| 580 | - * object types). | |
| 581 | - * | |
| 582 | - * @return the maximum length of an object identifier, in bytes | |
| 583 | - */ | |
| 584 | - public int getIdentifierMaximumLength() { | |
| 585 | - try { | |
| 586 | - if (getDatabaseMajorVersion() < ORACLE_12C_MAJOR_VERSION) { | |
| 587 | - return SHORT_IDENTIFIERS_LENGTH; | |
| 588 | - } else if ((getDatabaseMajorVersion() == ORACLE_12C_MAJOR_VERSION) && (getDatabaseMinorVersion() <= 1)) { | |
| 589 | - return SHORT_IDENTIFIERS_LENGTH; | |
| 590 | - } else { | |
| 591 | - return LONG_IDENTIFIERS_LEGNTH; | |
| 592 | - } | |
| 593 | - } catch (DatabaseException ex) { | |
| 594 | - throw new UnexpectedLiquibaseException("Cannot determine the Oracle database version number", ex); | |
| 595 | - } | |
| 596 | - | |
| 597 | - } | |
| 598 | -} |
sql/dm/flowable-patch/src/main/java/liquibase/datatype/core/BooleanType.java deleted
| 1 | -package liquibase.datatype.core; | |
| 2 | - | |
| 3 | -import liquibase.change.core.LoadDataChange; | |
| 4 | -import liquibase.database.Database; | |
| 5 | -import liquibase.database.core.*; | |
| 6 | -import liquibase.datatype.DataTypeInfo; | |
| 7 | -import liquibase.datatype.DatabaseDataType; | |
| 8 | -import liquibase.datatype.LiquibaseDataType; | |
| 9 | -import liquibase.exception.UnexpectedLiquibaseException; | |
| 10 | -import liquibase.statement.DatabaseFunction; | |
| 11 | -import liquibase.util.StringUtil; | |
| 12 | - | |
| 13 | -import java.util.Locale; | |
| 14 | -import java.util.regex.Pattern; | |
| 15 | - | |
| 16 | -@DataTypeInfo(name = "boolean", aliases = {"java.sql.Types.BOOLEAN", "java.lang.Boolean", "bit", "bool"}, minParameters = 0, maxParameters = 0, priority = LiquibaseDataType.PRIORITY_DEFAULT) | |
| 17 | -public class BooleanType extends LiquibaseDataType { | |
| 18 | - | |
| 19 | - @Override | |
| 20 | - public DatabaseDataType toDatabaseDataType(Database database) { | |
| 21 | - String originalDefinition = StringUtil.trimToEmpty(getRawDefinition()); | |
| 22 | - if ((database instanceof Firebird3Database)) { | |
| 23 | - return new DatabaseDataType("BOOLEAN"); | |
| 24 | - } | |
| 25 | - | |
| 26 | - if ((database instanceof Db2zDatabase) || (database instanceof FirebirdDatabase)) { | |
| 27 | - return new DatabaseDataType("SMALLINT"); | |
| 28 | - } else if (database instanceof MSSQLDatabase) { | |
| 29 | - return new DatabaseDataType(database.escapeDataTypeName("bit")); | |
| 30 | - } else if (database instanceof MySQLDatabase) { | |
| 31 | - if (originalDefinition.toLowerCase(Locale.US).startsWith("bit")) { | |
| 32 | - return new DatabaseDataType("BIT", getParameters()); | |
| 33 | - } | |
| 34 | - return new DatabaseDataType("BIT", 1); | |
| 35 | - } else if (database instanceof OracleDatabase) { | |
| 36 | - return new DatabaseDataType("NUMBER", 1); | |
| 37 | - } else if ((database instanceof SybaseASADatabase) || (database instanceof SybaseDatabase)) { | |
| 38 | - return new DatabaseDataType("BIT"); | |
| 39 | - } else if (database instanceof DerbyDatabase) { | |
| 40 | - if (((DerbyDatabase) database).supportsBooleanDataType()) { | |
| 41 | - return new DatabaseDataType("BOOLEAN"); | |
| 42 | - } else { | |
| 43 | - return new DatabaseDataType("SMALLINT"); | |
| 44 | - } | |
| 45 | - } else if (database instanceof DB2Database) { | |
| 46 | - if (((DB2Database) database).supportsBooleanDataType()) | |
| 47 | - return new DatabaseDataType("BOOLEAN"); | |
| 48 | - else | |
| 49 | - return new DatabaseDataType("SMALLINT"); | |
| 50 | - } else if (database instanceof HsqlDatabase) { | |
| 51 | - return new DatabaseDataType("BOOLEAN"); | |
| 52 | - } else if (database instanceof PostgresDatabase) { | |
| 53 | - if (originalDefinition.toLowerCase(Locale.US).startsWith("bit")) { | |
| 54 | - return new DatabaseDataType("BIT", getParameters()); | |
| 55 | - } | |
| 56 | - } else if (database instanceof DmDatabase) { // dhb52: DM Support | |
| 57 | - return new DatabaseDataType("bit"); | |
| 58 | - } | |
| 59 | - | |
| 60 | - return super.toDatabaseDataType(database); | |
| 61 | - } | |
| 62 | - | |
| 63 | - @Override | |
| 64 | - public String objectToSql(Object value, Database database) { | |
| 65 | - if ((value == null) || "null".equals(value.toString().toLowerCase(Locale.US))) { | |
| 66 | - return null; | |
| 67 | - } | |
| 68 | - | |
| 69 | - String returnValue; | |
| 70 | - if (value instanceof String) { | |
| 71 | - value = ((String) value).replaceAll("'", ""); | |
| 72 | - if ("true".equals(((String) value).toLowerCase(Locale.US)) || "1".equals(value) || "b'1'".equals(((String) value).toLowerCase(Locale.US)) || "t".equals(((String) value).toLowerCase(Locale.US)) || ((String) value).toLowerCase(Locale.US).equals(this.getTrueBooleanValue(database).toLowerCase(Locale.US))) { | |
| 73 | - returnValue = this.getTrueBooleanValue(database); | |
| 74 | - } else if ("false".equals(((String) value).toLowerCase(Locale.US)) || "0".equals(value) || "b'0'".equals( | |
| 75 | - ((String) value).toLowerCase(Locale.US)) || "f".equals(((String) value).toLowerCase(Locale.US)) || ((String) value).toLowerCase(Locale.US).equals(this.getFalseBooleanValue(database).toLowerCase(Locale.US))) { | |
| 76 | - returnValue = this.getFalseBooleanValue(database); | |
| 77 | - } else if (database instanceof PostgresDatabase && Pattern.matches("b?([01])\\1*(::bit|::\"bit\")?", (String) value)) { | |
| 78 | - returnValue = "b'" | |
| 79 | - + value.toString() | |
| 80 | - .replace("b", "") | |
| 81 | - .replace("\"", "") | |
| 82 | - .replace("::it", "") | |
| 83 | - + "'::\"bit\""; | |
| 84 | - } else { | |
| 85 | - throw new UnexpectedLiquibaseException("Unknown boolean value: " + value); | |
| 86 | - } | |
| 87 | - } else if (value instanceof Long) { | |
| 88 | - if (Long.valueOf(1).equals(value)) { | |
| 89 | - returnValue = this.getTrueBooleanValue(database); | |
| 90 | - } else { | |
| 91 | - returnValue = this.getFalseBooleanValue(database); | |
| 92 | - } | |
| 93 | - } else if (value instanceof Number) { | |
| 94 | - if (value.equals(1) || "1".equals(value.toString()) || "1.0".equals(value.toString())) { | |
| 95 | - returnValue = this.getTrueBooleanValue(database); | |
| 96 | - } else { | |
| 97 | - returnValue = this.getFalseBooleanValue(database); | |
| 98 | - } | |
| 99 | - } else if (value instanceof DatabaseFunction) { | |
| 100 | - return value.toString(); | |
| 101 | - } else if (value instanceof Boolean) { | |
| 102 | - if (((Boolean) value)) { | |
| 103 | - returnValue = this.getTrueBooleanValue(database); | |
| 104 | - } else { | |
| 105 | - returnValue = this.getFalseBooleanValue(database); | |
| 106 | - } | |
| 107 | - } else { | |
| 108 | - throw new UnexpectedLiquibaseException("Cannot convert type " + value.getClass() + " to a boolean value"); | |
| 109 | - } | |
| 110 | - | |
| 111 | - return returnValue; | |
| 112 | - } | |
| 113 | - | |
| 114 | - protected boolean isNumericBoolean(Database database) { | |
| 115 | - if (database instanceof Firebird3Database) { | |
| 116 | - return false; | |
| 117 | - } | |
| 118 | - if (database instanceof DerbyDatabase) { | |
| 119 | - return !((DerbyDatabase) database).supportsBooleanDataType(); | |
| 120 | - } else if (database instanceof DB2Database) { | |
| 121 | - return !((DB2Database) database).supportsBooleanDataType(); | |
| 122 | - } | |
| 123 | - return (database instanceof Db2zDatabase) | |
| 124 | - || (database instanceof FirebirdDatabase) | |
| 125 | - || (database instanceof MSSQLDatabase) | |
| 126 | - || (database instanceof MySQLDatabase) | |
| 127 | - || (database instanceof OracleDatabase) | |
| 128 | - || (database instanceof SQLiteDatabase) | |
| 129 | - || (database instanceof SybaseASADatabase) | |
| 130 | - || (database instanceof SybaseDatabase) | |
| 131 | - || (database instanceof DmDatabase); // dhb52: DM Support | |
| 132 | - } | |
| 133 | - | |
| 134 | - /** | |
| 135 | - * The database-specific value to use for "false" "boolean" columns. | |
| 136 | - */ | |
| 137 | - public String getFalseBooleanValue(Database database) { | |
| 138 | - if (isNumericBoolean(database)) { | |
| 139 | - return "0"; | |
| 140 | - } | |
| 141 | - if (database instanceof InformixDatabase) { | |
| 142 | - return "'f'"; | |
| 143 | - } | |
| 144 | - return "FALSE"; | |
| 145 | - } | |
| 146 | - | |
| 147 | - /** | |
| 148 | - * The database-specific value to use for "true" "boolean" columns. | |
| 149 | - */ | |
| 150 | - public String getTrueBooleanValue(Database database) { | |
| 151 | - if (isNumericBoolean(database)) { | |
| 152 | - return "1"; | |
| 153 | - } | |
| 154 | - if (database instanceof InformixDatabase) { | |
| 155 | - return "'t'"; | |
| 156 | - } | |
| 157 | - return "TRUE"; | |
| 158 | - } | |
| 159 | - | |
| 160 | - @Override | |
| 161 | - public LoadDataChange.LOAD_DATA_TYPE getLoadTypeName() { | |
| 162 | - return LoadDataChange.LOAD_DATA_TYPE.BOOLEAN; | |
| 163 | - } | |
| 164 | - | |
| 165 | -} |
sql/dm/flowable-patch/src/main/java/org/flowable/common/engine/impl/AbstractEngineConfiguration.java deleted
| 1 | -/* Licensed under the Apache License, Version 2.0 (the "License"); | |
| 2 | - * you may not use this file except in compliance with the License. | |
| 3 | - * You may obtain a copy of the License at | |
| 4 | - * | |
| 5 | - * http://www.apache.org/licenses/LICENSE-2.0 | |
| 6 | - * | |
| 7 | - * Unless required by applicable law or agreed to in writing, software | |
| 8 | - * distributed under the License is distributed on an "AS IS" BASIS, | |
| 9 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 10 | - * See the License for the specific language governing permissions and | |
| 11 | - * limitations under the License. | |
| 12 | - */ | |
| 13 | -package org.flowable.common.engine.impl; | |
| 14 | - | |
| 15 | -import java.io.InputStream; | |
| 16 | -import java.io.InputStreamReader; | |
| 17 | -import java.io.Reader; | |
| 18 | -import java.sql.Connection; | |
| 19 | -import java.sql.DatabaseMetaData; | |
| 20 | -import java.sql.PreparedStatement; | |
| 21 | -import java.sql.ResultSet; | |
| 22 | -import java.sql.SQLException; | |
| 23 | -import java.time.Duration; | |
| 24 | -import java.util.ArrayList; | |
| 25 | -import java.util.Collection; | |
| 26 | -import java.util.Collections; | |
| 27 | -import java.util.Comparator; | |
| 28 | -import java.util.HashMap; | |
| 29 | -import java.util.List; | |
| 30 | -import java.util.Map; | |
| 31 | -import java.util.Properties; | |
| 32 | -import java.util.ServiceLoader; | |
| 33 | -import java.util.Set; | |
| 34 | - | |
| 35 | -import javax.naming.InitialContext; | |
| 36 | -import javax.sql.DataSource; | |
| 37 | - | |
| 38 | -import org.apache.commons.lang3.StringUtils; | |
| 39 | -import org.apache.ibatis.builder.xml.XMLConfigBuilder; | |
| 40 | -import org.apache.ibatis.builder.xml.XMLMapperBuilder; | |
| 41 | -import org.apache.ibatis.datasource.pooled.PooledDataSource; | |
| 42 | -import org.apache.ibatis.mapping.Environment; | |
| 43 | -import org.apache.ibatis.plugin.Interceptor; | |
| 44 | -import org.apache.ibatis.session.Configuration; | |
| 45 | -import org.apache.ibatis.session.SqlSessionFactory; | |
| 46 | -import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory; | |
| 47 | -import org.apache.ibatis.transaction.TransactionFactory; | |
| 48 | -import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; | |
| 49 | -import org.apache.ibatis.transaction.managed.ManagedTransactionFactory; | |
| 50 | -import org.apache.ibatis.type.ArrayTypeHandler; | |
| 51 | -import org.apache.ibatis.type.BigDecimalTypeHandler; | |
| 52 | -import org.apache.ibatis.type.BlobInputStreamTypeHandler; | |
| 53 | -import org.apache.ibatis.type.BlobTypeHandler; | |
| 54 | -import org.apache.ibatis.type.BooleanTypeHandler; | |
| 55 | -import org.apache.ibatis.type.ByteTypeHandler; | |
| 56 | -import org.apache.ibatis.type.ClobTypeHandler; | |
| 57 | -import org.apache.ibatis.type.DateOnlyTypeHandler; | |
| 58 | -import org.apache.ibatis.type.DateTypeHandler; | |
| 59 | -import org.apache.ibatis.type.DoubleTypeHandler; | |
| 60 | -import org.apache.ibatis.type.FloatTypeHandler; | |
| 61 | -import org.apache.ibatis.type.IntegerTypeHandler; | |
| 62 | -import org.apache.ibatis.type.JdbcType; | |
| 63 | -import org.apache.ibatis.type.LongTypeHandler; | |
| 64 | -import org.apache.ibatis.type.NClobTypeHandler; | |
| 65 | -import org.apache.ibatis.type.NStringTypeHandler; | |
| 66 | -import org.apache.ibatis.type.ShortTypeHandler; | |
| 67 | -import org.apache.ibatis.type.SqlxmlTypeHandler; | |
| 68 | -import org.apache.ibatis.type.StringTypeHandler; | |
| 69 | -import org.apache.ibatis.type.TimeOnlyTypeHandler; | |
| 70 | -import org.apache.ibatis.type.TypeHandlerRegistry; | |
| 71 | -import org.flowable.common.engine.api.FlowableException; | |
| 72 | -import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; | |
| 73 | -import org.flowable.common.engine.api.delegate.event.FlowableEventDispatcher; | |
| 74 | -import org.flowable.common.engine.api.delegate.event.FlowableEventListener; | |
| 75 | -import org.flowable.common.engine.api.engine.EngineLifecycleListener; | |
| 76 | -import org.flowable.common.engine.impl.agenda.AgendaOperationRunner; | |
| 77 | -import org.flowable.common.engine.impl.cfg.CommandExecutorImpl; | |
| 78 | -import org.flowable.common.engine.impl.cfg.IdGenerator; | |
| 79 | -import org.flowable.common.engine.impl.cfg.TransactionContextFactory; | |
| 80 | -import org.flowable.common.engine.impl.cfg.standalone.StandaloneMybatisTransactionContextFactory; | |
| 81 | -import org.flowable.common.engine.impl.db.CommonDbSchemaManager; | |
| 82 | -import org.flowable.common.engine.impl.db.DbSqlSessionFactory; | |
| 83 | -import org.flowable.common.engine.impl.db.LogSqlExecutionTimePlugin; | |
| 84 | -import org.flowable.common.engine.impl.db.MybatisTypeAliasConfigurator; | |
| 85 | -import org.flowable.common.engine.impl.db.MybatisTypeHandlerConfigurator; | |
| 86 | -import org.flowable.common.engine.impl.db.SchemaManager; | |
| 87 | -import org.flowable.common.engine.impl.event.EventDispatchAction; | |
| 88 | -import org.flowable.common.engine.impl.event.FlowableEventDispatcherImpl; | |
| 89 | -import org.flowable.common.engine.impl.interceptor.Command; | |
| 90 | -import org.flowable.common.engine.impl.interceptor.CommandConfig; | |
| 91 | -import org.flowable.common.engine.impl.interceptor.CommandContextFactory; | |
| 92 | -import org.flowable.common.engine.impl.interceptor.CommandContextInterceptor; | |
| 93 | -import org.flowable.common.engine.impl.interceptor.CommandExecutor; | |
| 94 | -import org.flowable.common.engine.impl.interceptor.CommandInterceptor; | |
| 95 | -import org.flowable.common.engine.impl.interceptor.CrDbRetryInterceptor; | |
| 96 | -import org.flowable.common.engine.impl.interceptor.DefaultCommandInvoker; | |
| 97 | -import org.flowable.common.engine.impl.interceptor.LogInterceptor; | |
| 98 | -import org.flowable.common.engine.impl.interceptor.SessionFactory; | |
| 99 | -import org.flowable.common.engine.impl.interceptor.TransactionContextInterceptor; | |
| 100 | -import org.flowable.common.engine.impl.lock.LockManager; | |
| 101 | -import org.flowable.common.engine.impl.lock.LockManagerImpl; | |
| 102 | -import org.flowable.common.engine.impl.logging.LoggingListener; | |
| 103 | -import org.flowable.common.engine.impl.logging.LoggingSession; | |
| 104 | -import org.flowable.common.engine.impl.logging.LoggingSessionFactory; | |
| 105 | -import org.flowable.common.engine.impl.persistence.GenericManagerFactory; | |
| 106 | -import org.flowable.common.engine.impl.persistence.StrongUuidGenerator; | |
| 107 | -import org.flowable.common.engine.impl.persistence.cache.EntityCache; | |
| 108 | -import org.flowable.common.engine.impl.persistence.cache.EntityCacheImpl; | |
| 109 | -import org.flowable.common.engine.impl.persistence.entity.ByteArrayEntityManager; | |
| 110 | -import org.flowable.common.engine.impl.persistence.entity.ByteArrayEntityManagerImpl; | |
| 111 | -import org.flowable.common.engine.impl.persistence.entity.Entity; | |
| 112 | -import org.flowable.common.engine.impl.persistence.entity.PropertyEntityManager; | |
| 113 | -import org.flowable.common.engine.impl.persistence.entity.PropertyEntityManagerImpl; | |
| 114 | -import org.flowable.common.engine.impl.persistence.entity.TableDataManager; | |
| 115 | -import org.flowable.common.engine.impl.persistence.entity.TableDataManagerImpl; | |
| 116 | -import org.flowable.common.engine.impl.persistence.entity.data.ByteArrayDataManager; | |
| 117 | -import org.flowable.common.engine.impl.persistence.entity.data.PropertyDataManager; | |
| 118 | -import org.flowable.common.engine.impl.persistence.entity.data.impl.MybatisByteArrayDataManager; | |
| 119 | -import org.flowable.common.engine.impl.persistence.entity.data.impl.MybatisPropertyDataManager; | |
| 120 | -import org.flowable.common.engine.impl.runtime.Clock; | |
| 121 | -import org.flowable.common.engine.impl.service.CommonEngineServiceImpl; | |
| 122 | -import org.flowable.common.engine.impl.util.DefaultClockImpl; | |
| 123 | -import org.flowable.common.engine.impl.util.IoUtil; | |
| 124 | -import org.flowable.common.engine.impl.util.ReflectUtil; | |
| 125 | -import org.flowable.eventregistry.api.EventRegistryEventConsumer; | |
| 126 | -import org.slf4j.Logger; | |
| 127 | -import org.slf4j.LoggerFactory; | |
| 128 | - | |
| 129 | -import com.fasterxml.jackson.databind.ObjectMapper; | |
| 130 | -import com.fasterxml.jackson.databind.SerializationFeature; | |
| 131 | - | |
| 132 | -public abstract class AbstractEngineConfiguration { | |
| 133 | - | |
| 134 | - protected final Logger logger = LoggerFactory.getLogger(getClass()); | |
| 135 | - | |
| 136 | - /** The tenant id indicating 'no tenant' */ | |
| 137 | - public static final String NO_TENANT_ID = ""; | |
| 138 | - | |
| 139 | - /** | |
| 140 | - * Checks the version of the DB schema against the library when the form engine is being created and throws an exception if the versions don't match. | |
| 141 | - */ | |
| 142 | - public static final String DB_SCHEMA_UPDATE_FALSE = "false"; | |
| 143 | - public static final String DB_SCHEMA_UPDATE_CREATE = "create"; | |
| 144 | - public static final String DB_SCHEMA_UPDATE_CREATE_DROP = "create-drop"; | |
| 145 | - | |
| 146 | - /** | |
| 147 | - * Creates the schema when the form engine is being created and drops the schema when the form engine is being closed. | |
| 148 | - */ | |
| 149 | - public static final String DB_SCHEMA_UPDATE_DROP_CREATE = "drop-create"; | |
| 150 | - | |
| 151 | - /** | |
| 152 | - * Upon building of the process engine, a check is performed and an update of the schema is performed if it is necessary. | |
| 153 | - */ | |
| 154 | - public static final String DB_SCHEMA_UPDATE_TRUE = "true"; | |
| 155 | - | |
| 156 | - protected boolean forceCloseMybatisConnectionPool = true; | |
| 157 | - | |
| 158 | - protected String databaseType; | |
| 159 | - protected String jdbcDriver = "org.h2.Driver"; | |
| 160 | - protected String jdbcUrl = "jdbc:h2:tcp://localhost/~/flowable"; | |
| 161 | - protected String jdbcUsername = "sa"; | |
| 162 | - protected String jdbcPassword = ""; | |
| 163 | - protected String dataSourceJndiName; | |
| 164 | - protected int jdbcMaxActiveConnections = 16; | |
| 165 | - protected int jdbcMaxIdleConnections = 8; | |
| 166 | - protected int jdbcMaxCheckoutTime; | |
| 167 | - protected int jdbcMaxWaitTime; | |
| 168 | - protected boolean jdbcPingEnabled; | |
| 169 | - protected String jdbcPingQuery; | |
| 170 | - protected int jdbcPingConnectionNotUsedFor; | |
| 171 | - protected int jdbcDefaultTransactionIsolationLevel; | |
| 172 | - protected DataSource dataSource; | |
| 173 | - protected SchemaManager commonSchemaManager; | |
| 174 | - protected SchemaManager schemaManager; | |
| 175 | - protected Command<Void> schemaManagementCmd; | |
| 176 | - | |
| 177 | - protected String databaseSchemaUpdate = DB_SCHEMA_UPDATE_FALSE; | |
| 178 | - | |
| 179 | - /** | |
| 180 | - * Whether to use a lock when performing the database schema create or update operations. | |
| 181 | - */ | |
| 182 | - protected boolean useLockForDatabaseSchemaUpdate = false; | |
| 183 | - | |
| 184 | - protected String xmlEncoding = "UTF-8"; | |
| 185 | - | |
| 186 | - // COMMAND EXECUTORS /////////////////////////////////////////////// | |
| 187 | - | |
| 188 | - protected CommandExecutor commandExecutor; | |
| 189 | - protected Collection<? extends CommandInterceptor> defaultCommandInterceptors; | |
| 190 | - protected CommandConfig defaultCommandConfig; | |
| 191 | - protected CommandConfig schemaCommandConfig; | |
| 192 | - protected CommandContextFactory commandContextFactory; | |
| 193 | - protected CommandInterceptor commandInvoker; | |
| 194 | - | |
| 195 | - protected AgendaOperationRunner agendaOperationRunner = (commandContext, runnable) -> runnable.run(); | |
| 196 | - | |
| 197 | - protected List<CommandInterceptor> customPreCommandInterceptors; | |
| 198 | - protected List<CommandInterceptor> customPostCommandInterceptors; | |
| 199 | - protected List<CommandInterceptor> commandInterceptors; | |
| 200 | - | |
| 201 | - protected Map<String, AbstractEngineConfiguration> engineConfigurations = new HashMap<>(); | |
| 202 | - protected Map<String, AbstractServiceConfiguration> serviceConfigurations = new HashMap<>(); | |
| 203 | - | |
| 204 | - protected ClassLoader classLoader; | |
| 205 | - /** | |
| 206 | - * Either use Class.forName or ClassLoader.loadClass for class loading. See http://forums.activiti.org/content/reflectutilloadclass-and-custom- classloader | |
| 207 | - */ | |
| 208 | - protected boolean useClassForNameClassLoading = true; | |
| 209 | - | |
| 210 | - protected List<EngineLifecycleListener> engineLifecycleListeners; | |
| 211 | - | |
| 212 | - // Event Registry ////////////////////////////////////////////////// | |
| 213 | - protected Map<String, EventRegistryEventConsumer> eventRegistryEventConsumers = new HashMap<>(); | |
| 214 | - | |
| 215 | - // MYBATIS SQL SESSION FACTORY ///////////////////////////////////// | |
| 216 | - | |
| 217 | - protected boolean isDbHistoryUsed = true; | |
| 218 | - protected DbSqlSessionFactory dbSqlSessionFactory; | |
| 219 | - protected SqlSessionFactory sqlSessionFactory; | |
| 220 | - protected TransactionFactory transactionFactory; | |
| 221 | - protected TransactionContextFactory transactionContextFactory; | |
| 222 | - | |
| 223 | - /** | |
| 224 | - * If set to true, enables bulk insert (grouping sql inserts together). Default true. | |
| 225 | - * For some databases (eg DB2+z/OS) needs to be set to false. | |
| 226 | - */ | |
| 227 | - protected boolean isBulkInsertEnabled = true; | |
| 228 | - | |
| 229 | - /** | |
| 230 | - * Some databases have a limit of how many parameters one sql insert can have (eg SQL Server, 2000 params (!= insert statements) ). Tweak this parameter in case of exceptions indicating too much | |
| 231 | - * is being put into one bulk insert, or make it higher if your database can cope with it and there are inserts with a huge amount of data. | |
| 232 | - * <p> | |
| 233 | - * By default: 100 (55 for mssql server as it has a hard limit of 2000 parameters in a statement) | |
| 234 | - */ | |
| 235 | - protected int maxNrOfStatementsInBulkInsert = 100; | |
| 236 | - | |
| 237 | - public int DEFAULT_MAX_NR_OF_STATEMENTS_BULK_INSERT_SQL_SERVER = 55; // currently Execution has most params (35). 2000 / 35 = 57. | |
| 238 | - | |
| 239 | - protected String mybatisMappingFile; | |
| 240 | - protected Set<Class<?>> customMybatisMappers; | |
| 241 | - protected Set<String> customMybatisXMLMappers; | |
| 242 | - protected List<Interceptor> customMybatisInterceptors; | |
| 243 | - | |
| 244 | - protected Set<String> dependentEngineMyBatisXmlMappers; | |
| 245 | - protected List<MybatisTypeAliasConfigurator> dependentEngineMybatisTypeAliasConfigs; | |
| 246 | - protected List<MybatisTypeHandlerConfigurator> dependentEngineMybatisTypeHandlerConfigs; | |
| 247 | - | |
| 248 | - // SESSION FACTORIES /////////////////////////////////////////////// | |
| 249 | - protected List<SessionFactory> customSessionFactories; | |
| 250 | - protected Map<Class<?>, SessionFactory> sessionFactories; | |
| 251 | - | |
| 252 | - protected boolean enableEventDispatcher = true; | |
| 253 | - protected FlowableEventDispatcher eventDispatcher; | |
| 254 | - protected List<FlowableEventListener> eventListeners; | |
| 255 | - protected Map<String, List<FlowableEventListener>> typedEventListeners; | |
| 256 | - protected List<EventDispatchAction> additionalEventDispatchActions; | |
| 257 | - | |
| 258 | - protected LoggingListener loggingListener; | |
| 259 | - | |
| 260 | - protected boolean transactionsExternallyManaged; | |
| 261 | - | |
| 262 | - /** | |
| 263 | - * Flag that can be set to configure or not a relational database is used. This is useful for custom implementations that do not use relational databases at all. | |
| 264 | - * | |
| 265 | - * If true (default), the {@link AbstractEngineConfiguration#getDatabaseSchemaUpdate()} value will be used to determine what needs to happen wrt the database schema. | |
| 266 | - * | |
| 267 | - * If false, no validation or schema creation will be done. That means that the database schema must have been created 'manually' before but the engine does not validate whether the schema is | |
| 268 | - * correct. The {@link AbstractEngineConfiguration#getDatabaseSchemaUpdate()} value will not be used. | |
| 269 | - */ | |
| 270 | - protected boolean usingRelationalDatabase = true; | |
| 271 | - | |
| 272 | - /** | |
| 273 | - * Flag that can be set to configure whether or not a schema is used. This is useful for custom implementations that do not use relational databases at all. | |
| 274 | - * Setting {@link #usingRelationalDatabase} to true will automatically imply using a schema. | |
| 275 | - */ | |
| 276 | - protected boolean usingSchemaMgmt = true; | |
| 277 | - | |
| 278 | - /** | |
| 279 | - * Allows configuring a database table prefix which is used for all runtime operations of the process engine. For example, if you specify a prefix named 'PRE1.', Flowable will query for executions | |
| 280 | - * in a table named 'PRE1.ACT_RU_EXECUTION_'. | |
| 281 | - * | |
| 282 | - * <p> | |
| 283 | - * <strong>NOTE: the prefix is not respected by automatic database schema management. If you use {@link AbstractEngineConfiguration#DB_SCHEMA_UPDATE_CREATE_DROP} or | |
| 284 | - * {@link AbstractEngineConfiguration#DB_SCHEMA_UPDATE_TRUE}, Flowable will create the database tables using the default names, regardless of the prefix configured here.</strong> | |
| 285 | - */ | |
| 286 | - protected String databaseTablePrefix = ""; | |
| 287 | - | |
| 288 | - /** | |
| 289 | - * Escape character for doing wildcard searches. | |
| 290 | - * | |
| 291 | - * This will be added at then end of queries that include for example a LIKE clause. For example: SELECT * FROM table WHERE column LIKE '%\%%' ESCAPE '\'; | |
| 292 | - */ | |
| 293 | - protected String databaseWildcardEscapeCharacter; | |
| 294 | - | |
| 295 | - /** | |
| 296 | - * database catalog to use | |
| 297 | - */ | |
| 298 | - protected String databaseCatalog = ""; | |
| 299 | - | |
| 300 | - /** | |
| 301 | - * In some situations you want to set the schema to use for table checks / generation if the database metadata doesn't return that correctly, see https://jira.codehaus.org/browse/ACT-1220, | |
| 302 | - * https://jira.codehaus.org/browse/ACT-1062 | |
| 303 | - */ | |
| 304 | - protected String databaseSchema; | |
| 305 | - | |
| 306 | - /** | |
| 307 | - * Set to true in case the defined databaseTablePrefix is a schema-name, instead of an actual table name prefix. This is relevant for checking if Flowable-tables exist, the databaseTablePrefix | |
| 308 | - * will not be used here - since the schema is taken into account already, adding a prefix for the table-check will result in wrong table-names. | |
| 309 | - */ | |
| 310 | - protected boolean tablePrefixIsSchema; | |
| 311 | - | |
| 312 | - /** | |
| 313 | - * Set to true if the latest version of a definition should be retrieved, ignoring a possible parent deployment id value | |
| 314 | - */ | |
| 315 | - protected boolean alwaysLookupLatestDefinitionVersion; | |
| 316 | - | |
| 317 | - /** | |
| 318 | - * Set to true if by default lookups should fallback to the default tenant (an empty string by default or a defined tenant value) | |
| 319 | - */ | |
| 320 | - protected boolean fallbackToDefaultTenant; | |
| 321 | - | |
| 322 | - /** | |
| 323 | - * Default tenant provider that is executed when looking up definitions, in case the global or local fallback to default tenant value is true | |
| 324 | - */ | |
| 325 | - protected DefaultTenantProvider defaultTenantProvider = (tenantId, scope, scopeKey) -> NO_TENANT_ID; | |
| 326 | - | |
| 327 | - /** | |
| 328 | - * Enables the MyBatis plugin that logs the execution time of sql statements. | |
| 329 | - */ | |
| 330 | - protected boolean enableLogSqlExecutionTime; | |
| 331 | - | |
| 332 | - protected Properties databaseTypeMappings = getDefaultDatabaseTypeMappings(); | |
| 333 | - | |
| 334 | - /** | |
| 335 | - * Duration between the checks when acquiring a lock. | |
| 336 | - */ | |
| 337 | - protected Duration lockPollRate = Duration.ofSeconds(10); | |
| 338 | - | |
| 339 | - /** | |
| 340 | - * Duration to wait for the DB Schema lock before giving up. | |
| 341 | - */ | |
| 342 | - protected Duration schemaLockWaitTime = Duration.ofMinutes(5); | |
| 343 | - | |
| 344 | - // DATA MANAGERS ////////////////////////////////////////////////////////////////// | |
| 345 | - | |
| 346 | - protected PropertyDataManager propertyDataManager; | |
| 347 | - protected ByteArrayDataManager byteArrayDataManager; | |
| 348 | - protected TableDataManager tableDataManager; | |
| 349 | - | |
| 350 | - // ENTITY MANAGERS //////////////////////////////////////////////////////////////// | |
| 351 | - | |
| 352 | - protected PropertyEntityManager propertyEntityManager; | |
| 353 | - protected ByteArrayEntityManager byteArrayEntityManager; | |
| 354 | - | |
| 355 | - protected List<EngineDeployer> customPreDeployers; | |
| 356 | - protected List<EngineDeployer> customPostDeployers; | |
| 357 | - protected List<EngineDeployer> deployers; | |
| 358 | - | |
| 359 | - // CONFIGURATORS //////////////////////////////////////////////////////////// | |
| 360 | - | |
| 361 | - protected boolean enableConfiguratorServiceLoader = true; // Enabled by default. In certain environments this should be set to false (eg osgi) | |
| 362 | - protected List<EngineConfigurator> configurators; // The injected configurators | |
| 363 | - protected List<EngineConfigurator> allConfigurators; // Including auto-discovered configurators | |
| 364 | - protected EngineConfigurator idmEngineConfigurator; | |
| 365 | - protected EngineConfigurator eventRegistryConfigurator; | |
| 366 | - | |
| 367 | - public static final String PRODUCT_NAME_POSTGRES = "PostgreSQL"; | |
| 368 | - public static final String PRODUCT_NAME_CRDB = "CockroachDB"; | |
| 369 | - | |
| 370 | - public static final String DATABASE_TYPE_H2 = "h2"; | |
| 371 | - public static final String DATABASE_TYPE_HSQL = "hsql"; | |
| 372 | - public static final String DATABASE_TYPE_MYSQL = "mysql"; | |
| 373 | - public static final String DATABASE_TYPE_ORACLE = "oracle"; | |
| 374 | - public static final String DATABASE_TYPE_POSTGRES = "postgres"; | |
| 375 | - public static final String DATABASE_TYPE_MSSQL = "mssql"; | |
| 376 | - public static final String DATABASE_TYPE_DB2 = "db2"; | |
| 377 | - public static final String DATABASE_TYPE_COCKROACHDB = "cockroachdb"; | |
| 378 | - | |
| 379 | - public static Properties getDefaultDatabaseTypeMappings() { | |
| 380 | - Properties databaseTypeMappings = new Properties(); | |
| 381 | - databaseTypeMappings.setProperty("H2", DATABASE_TYPE_H2); | |
| 382 | - databaseTypeMappings.setProperty("HSQL Database Engine", DATABASE_TYPE_HSQL); | |
| 383 | - databaseTypeMappings.setProperty("MySQL", DATABASE_TYPE_MYSQL); | |
| 384 | - databaseTypeMappings.setProperty("MariaDB", DATABASE_TYPE_MYSQL); | |
| 385 | - databaseTypeMappings.setProperty("Oracle", DATABASE_TYPE_ORACLE); | |
| 386 | - databaseTypeMappings.setProperty(PRODUCT_NAME_POSTGRES, DATABASE_TYPE_POSTGRES); | |
| 387 | - databaseTypeMappings.setProperty("Microsoft SQL Server", DATABASE_TYPE_MSSQL); | |
| 388 | - databaseTypeMappings.setProperty(DATABASE_TYPE_DB2, DATABASE_TYPE_DB2); | |
| 389 | - databaseTypeMappings.setProperty("DB2", DATABASE_TYPE_DB2); | |
| 390 | - databaseTypeMappings.setProperty("DB2/NT", DATABASE_TYPE_DB2); | |
| 391 | - databaseTypeMappings.setProperty("DB2/NT64", DATABASE_TYPE_DB2); | |
| 392 | - databaseTypeMappings.setProperty("DB2 UDP", DATABASE_TYPE_DB2); | |
| 393 | - databaseTypeMappings.setProperty("DB2/LINUX", DATABASE_TYPE_DB2); | |
| 394 | - databaseTypeMappings.setProperty("DB2/LINUX390", DATABASE_TYPE_DB2); | |
| 395 | - databaseTypeMappings.setProperty("DB2/LINUXX8664", DATABASE_TYPE_DB2); | |
| 396 | - databaseTypeMappings.setProperty("DB2/LINUXZ64", DATABASE_TYPE_DB2); | |
| 397 | - databaseTypeMappings.setProperty("DB2/LINUXPPC64", DATABASE_TYPE_DB2); | |
| 398 | - databaseTypeMappings.setProperty("DB2/LINUXPPC64LE", DATABASE_TYPE_DB2); | |
| 399 | - databaseTypeMappings.setProperty("DB2/400 SQL", DATABASE_TYPE_DB2); | |
| 400 | - databaseTypeMappings.setProperty("DB2/6000", DATABASE_TYPE_DB2); | |
| 401 | - databaseTypeMappings.setProperty("DB2 UDB iSeries", DATABASE_TYPE_DB2); | |
| 402 | - databaseTypeMappings.setProperty("DB2/AIX64", DATABASE_TYPE_DB2); | |
| 403 | - databaseTypeMappings.setProperty("DB2/HPUX", DATABASE_TYPE_DB2); | |
| 404 | - databaseTypeMappings.setProperty("DB2/HP64", DATABASE_TYPE_DB2); | |
| 405 | - databaseTypeMappings.setProperty("DB2/SUN", DATABASE_TYPE_DB2); | |
| 406 | - databaseTypeMappings.setProperty("DB2/SUN64", DATABASE_TYPE_DB2); | |
| 407 | - databaseTypeMappings.setProperty("DB2/PTX", DATABASE_TYPE_DB2); | |
| 408 | - databaseTypeMappings.setProperty("DB2/2", DATABASE_TYPE_DB2); | |
| 409 | - databaseTypeMappings.setProperty("DB2 UDB AS400", DATABASE_TYPE_DB2); | |
| 410 | - databaseTypeMappings.setProperty(PRODUCT_NAME_CRDB, DATABASE_TYPE_COCKROACHDB); | |
| 411 | - databaseTypeMappings.setProperty("DM DBMS", DATABASE_TYPE_ORACLE); // dhb52: DM support | |
| 412 | - return databaseTypeMappings; | |
| 413 | - } | |
| 414 | - | |
| 415 | - protected Map<Object, Object> beans; | |
| 416 | - | |
| 417 | - protected IdGenerator idGenerator; | |
| 418 | - protected boolean usePrefixId; | |
| 419 | - | |
| 420 | - protected Clock clock; | |
| 421 | - protected ObjectMapper objectMapper; | |
| 422 | - | |
| 423 | - // Variables | |
| 424 | - | |
| 425 | - public static final int DEFAULT_GENERIC_MAX_LENGTH_STRING = 4000; | |
| 426 | - public static final int DEFAULT_ORACLE_MAX_LENGTH_STRING = 2000; | |
| 427 | - | |
| 428 | - /** | |
| 429 | - * Define a max length for storing String variable types in the database. Mainly used for the Oracle NVARCHAR2 limit of 2000 characters | |
| 430 | - */ | |
| 431 | - protected int maxLengthStringVariableType = -1; | |
| 432 | - | |
| 433 | - protected void initEngineConfigurations() { | |
| 434 | - addEngineConfiguration(getEngineCfgKey(), getEngineScopeType(), this); | |
| 435 | - } | |
| 436 | - | |
| 437 | - // DataSource | |
| 438 | - // /////////////////////////////////////////////////////////////// | |
| 439 | - | |
| 440 | - protected void initDataSource() { | |
| 441 | - if (dataSource == null) { | |
| 442 | - if (dataSourceJndiName != null) { | |
| 443 | - try { | |
| 444 | - dataSource = (DataSource) new InitialContext().lookup(dataSourceJndiName); | |
| 445 | - } catch (Exception e) { | |
| 446 | - throw new FlowableException("couldn't lookup datasource from " + dataSourceJndiName + ": " + e.getMessage(), e); | |
| 447 | - } | |
| 448 | - | |
| 449 | - } else if (jdbcUrl != null) { | |
| 450 | - if ((jdbcDriver == null) || (jdbcUsername == null)) { | |
| 451 | - throw new FlowableException("DataSource or JDBC properties have to be specified in a process engine configuration"); | |
| 452 | - } | |
| 453 | - | |
| 454 | - logger.debug("initializing datasource to db: {}", jdbcUrl); | |
| 455 | - | |
| 456 | - if (logger.isInfoEnabled()) { | |
| 457 | - logger.info("Configuring Datasource with following properties (omitted password for security)"); | |
| 458 | - logger.info("datasource driver : {}", jdbcDriver); | |
| 459 | - logger.info("datasource url : {}", jdbcUrl); | |
| 460 | - logger.info("datasource user name : {}", jdbcUsername); | |
| 461 | - } | |
| 462 | - | |
| 463 | - PooledDataSource pooledDataSource = new PooledDataSource(this.getClass().getClassLoader(), jdbcDriver, jdbcUrl, jdbcUsername, jdbcPassword); | |
| 464 | - | |
| 465 | - if (jdbcMaxActiveConnections > 0) { | |
| 466 | - pooledDataSource.setPoolMaximumActiveConnections(jdbcMaxActiveConnections); | |
| 467 | - } | |
| 468 | - if (jdbcMaxIdleConnections > 0) { | |
| 469 | - pooledDataSource.setPoolMaximumIdleConnections(jdbcMaxIdleConnections); | |
| 470 | - } | |
| 471 | - if (jdbcMaxCheckoutTime > 0) { | |
| 472 | - pooledDataSource.setPoolMaximumCheckoutTime(jdbcMaxCheckoutTime); | |
| 473 | - } | |
| 474 | - if (jdbcMaxWaitTime > 0) { | |
| 475 | - pooledDataSource.setPoolTimeToWait(jdbcMaxWaitTime); | |
| 476 | - } | |
| 477 | - if (jdbcPingEnabled) { | |
| 478 | - pooledDataSource.setPoolPingEnabled(true); | |
| 479 | - if (jdbcPingQuery != null) { | |
| 480 | - pooledDataSource.setPoolPingQuery(jdbcPingQuery); | |
| 481 | - } | |
| 482 | - pooledDataSource.setPoolPingConnectionsNotUsedFor(jdbcPingConnectionNotUsedFor); | |
| 483 | - } | |
| 484 | - if (jdbcDefaultTransactionIsolationLevel > 0) { | |
| 485 | - pooledDataSource.setDefaultTransactionIsolationLevel(jdbcDefaultTransactionIsolationLevel); | |
| 486 | - } | |
| 487 | - dataSource = pooledDataSource; | |
| 488 | - } | |
| 489 | - } | |
| 490 | - | |
| 491 | - if (databaseType == null) { | |
| 492 | - initDatabaseType(); | |
| 493 | - } | |
| 494 | - } | |
| 495 | - | |
| 496 | - public void initDatabaseType() { | |
| 497 | - Connection connection = null; | |
| 498 | - try { | |
| 499 | - connection = dataSource.getConnection(); | |
| 500 | - DatabaseMetaData databaseMetaData = connection.getMetaData(); | |
| 501 | - String databaseProductName = databaseMetaData.getDatabaseProductName(); | |
| 502 | - logger.debug("database product name: '{}'", databaseProductName); | |
| 503 | - | |
| 504 | - // CRDB does not expose the version through the jdbc driver, so we need to fetch it through version(). | |
| 505 | - if (PRODUCT_NAME_POSTGRES.equalsIgnoreCase(databaseProductName)) { | |
| 506 | - try (PreparedStatement preparedStatement = connection.prepareStatement("select version() as version;"); | |
| 507 | - ResultSet resultSet = preparedStatement.executeQuery()) { | |
| 508 | - String version = null; | |
| 509 | - if (resultSet.next()) { | |
| 510 | - version = resultSet.getString("version"); | |
| 511 | - } | |
| 512 | - | |
| 513 | - if (StringUtils.isNotEmpty(version) && version.toLowerCase().startsWith(PRODUCT_NAME_CRDB.toLowerCase())) { | |
| 514 | - databaseProductName = PRODUCT_NAME_CRDB; | |
| 515 | - logger.info("CockroachDB version '{}' detected", version); | |
| 516 | - } | |
| 517 | - } | |
| 518 | - } | |
| 519 | - | |
| 520 | - databaseType = databaseTypeMappings.getProperty(databaseProductName); | |
| 521 | - if (databaseType == null) { | |
| 522 | - throw new FlowableException("couldn't deduct database type from database product name '" + databaseProductName + "'"); | |
| 523 | - } | |
| 524 | - logger.debug("using database type: {}", databaseType); | |
| 525 | - | |
| 526 | - } catch (SQLException e) { | |
| 527 | - throw new RuntimeException("Exception while initializing Database connection", e); | |
| 528 | - } finally { | |
| 529 | - try { | |
| 530 | - if (connection != null) { | |
| 531 | - connection.close(); | |
| 532 | - } | |
| 533 | - } catch (SQLException e) { | |
| 534 | - logger.error("Exception while closing the Database connection", e); | |
| 535 | - } | |
| 536 | - } | |
| 537 | - | |
| 538 | - // Special care for MSSQL, as it has a hard limit of 2000 params per statement (incl bulk statement). | |
| 539 | - // Especially with executions, with 100 as default, this limit is passed. | |
| 540 | - if (DATABASE_TYPE_MSSQL.equals(databaseType)) { | |
| 541 | - maxNrOfStatementsInBulkInsert = DEFAULT_MAX_NR_OF_STATEMENTS_BULK_INSERT_SQL_SERVER; | |
| 542 | - } | |
| 543 | - } | |
| 544 | - | |
| 545 | - public void initSchemaManager() { | |
| 546 | - if (this.commonSchemaManager == null) { | |
| 547 | - this.commonSchemaManager = new CommonDbSchemaManager(); | |
| 548 | - } | |
| 549 | - } | |
| 550 | - | |
| 551 | - // session factories //////////////////////////////////////////////////////// | |
| 552 | - | |
| 553 | - public void addSessionFactory(SessionFactory sessionFactory) { | |
| 554 | - sessionFactories.put(sessionFactory.getSessionType(), sessionFactory); | |
| 555 | - } | |
| 556 | - | |
| 557 | - public void initCommandContextFactory() { | |
| 558 | - if (commandContextFactory == null) { | |
| 559 | - commandContextFactory = new CommandContextFactory(); | |
| 560 | - } | |
| 561 | - } | |
| 562 | - | |
| 563 | - public void initTransactionContextFactory() { | |
| 564 | - if (transactionContextFactory == null) { | |
| 565 | - transactionContextFactory = new StandaloneMybatisTransactionContextFactory(); | |
| 566 | - } | |
| 567 | - } | |
| 568 | - | |
| 569 | - public void initCommandExecutors() { | |
| 570 | - initDefaultCommandConfig(); | |
| 571 | - initSchemaCommandConfig(); | |
| 572 | - initCommandInvoker(); | |
| 573 | - initCommandInterceptors(); | |
| 574 | - initCommandExecutor(); | |
| 575 | - } | |
| 576 | - | |
| 577 | - | |
| 578 | - public void initDefaultCommandConfig() { | |
| 579 | - if (defaultCommandConfig == null) { | |
| 580 | - defaultCommandConfig = new CommandConfig(); | |
| 581 | - } | |
| 582 | - } | |
| 583 | - | |
| 584 | - public void initSchemaCommandConfig() { | |
| 585 | - if (schemaCommandConfig == null) { | |
| 586 | - schemaCommandConfig = new CommandConfig(); | |
| 587 | - } | |
| 588 | - } | |
| 589 | - | |
| 590 | - public void initCommandInvoker() { | |
| 591 | - if (commandInvoker == null) { | |
| 592 | - commandInvoker = new DefaultCommandInvoker(); | |
| 593 | - } | |
| 594 | - } | |
| 595 | - | |
| 596 | - public void initCommandInterceptors() { | |
| 597 | - if (commandInterceptors == null) { | |
| 598 | - commandInterceptors = new ArrayList<>(); | |
| 599 | - if (customPreCommandInterceptors != null) { | |
| 600 | - commandInterceptors.addAll(customPreCommandInterceptors); | |
| 601 | - } | |
| 602 | - commandInterceptors.addAll(getDefaultCommandInterceptors()); | |
| 603 | - if (customPostCommandInterceptors != null) { | |
| 604 | - commandInterceptors.addAll(customPostCommandInterceptors); | |
| 605 | - } | |
| 606 | - commandInterceptors.add(commandInvoker); | |
| 607 | - } | |
| 608 | - } | |
| 609 | - | |
| 610 | - public Collection<? extends CommandInterceptor> getDefaultCommandInterceptors() { | |
| 611 | - if (defaultCommandInterceptors == null) { | |
| 612 | - List<CommandInterceptor> interceptors = new ArrayList<>(); | |
| 613 | - interceptors.add(new LogInterceptor()); | |
| 614 | - | |
| 615 | - if (DATABASE_TYPE_COCKROACHDB.equals(databaseType)) { | |
| 616 | - interceptors.add(new CrDbRetryInterceptor()); | |
| 617 | - } | |
| 618 | - | |
| 619 | - CommandInterceptor transactionInterceptor = createTransactionInterceptor(); | |
| 620 | - if (transactionInterceptor != null) { | |
| 621 | - interceptors.add(transactionInterceptor); | |
| 622 | - } | |
| 623 | - | |
| 624 | - if (commandContextFactory != null) { | |
| 625 | - String engineCfgKey = getEngineCfgKey(); | |
| 626 | - CommandContextInterceptor commandContextInterceptor = new CommandContextInterceptor(commandContextFactory, | |
| 627 | - classLoader, useClassForNameClassLoading, clock, objectMapper); | |
| 628 | - engineConfigurations.put(engineCfgKey, this); | |
| 629 | - commandContextInterceptor.setEngineCfgKey(engineCfgKey); | |
| 630 | - commandContextInterceptor.setEngineConfigurations(engineConfigurations); | |
| 631 | - interceptors.add(commandContextInterceptor); | |
| 632 | - } | |
| 633 | - | |
| 634 | - if (transactionContextFactory != null) { | |
| 635 | - interceptors.add(new TransactionContextInterceptor(transactionContextFactory)); | |
| 636 | - } | |
| 637 | - | |
| 638 | - List<CommandInterceptor> additionalCommandInterceptors = getAdditionalDefaultCommandInterceptors(); | |
| 639 | - if (additionalCommandInterceptors != null) { | |
| 640 | - interceptors.addAll(additionalCommandInterceptors); | |
| 641 | - } | |
| 642 | - | |
| 643 | - defaultCommandInterceptors = interceptors; | |
| 644 | - } | |
| 645 | - return defaultCommandInterceptors; | |
| 646 | - } | |
| 647 | - | |
| 648 | - public abstract String getEngineCfgKey(); | |
| 649 | - | |
| 650 | - public abstract String getEngineScopeType(); | |
| 651 | - | |
| 652 | - public List<CommandInterceptor> getAdditionalDefaultCommandInterceptors() { | |
| 653 | - return null; | |
| 654 | - } | |
| 655 | - | |
| 656 | - public void initCommandExecutor() { | |
| 657 | - if (commandExecutor == null) { | |
| 658 | - CommandInterceptor first = initInterceptorChain(commandInterceptors); | |
| 659 | - commandExecutor = new CommandExecutorImpl(getDefaultCommandConfig(), first); | |
| 660 | - } | |
| 661 | - } | |
| 662 | - | |
| 663 | - public CommandInterceptor initInterceptorChain(List<CommandInterceptor> chain) { | |
| 664 | - if (chain == null || chain.isEmpty()) { | |
| 665 | - throw new FlowableException("invalid command interceptor chain configuration: " + chain); | |
| 666 | - } | |
| 667 | - for (int i = 0; i < chain.size() - 1; i++) { | |
| 668 | - chain.get(i).setNext(chain.get(i + 1)); | |
| 669 | - } | |
| 670 | - return chain.get(0); | |
| 671 | - } | |
| 672 | - | |
| 673 | - public abstract CommandInterceptor createTransactionInterceptor(); | |
| 674 | - | |
| 675 | - | |
| 676 | - public void initBeans() { | |
| 677 | - if (beans == null) { | |
| 678 | - beans = new HashMap<>(); | |
| 679 | - } | |
| 680 | - } | |
| 681 | - | |
| 682 | - // id generator | |
| 683 | - // ///////////////////////////////////////////////////////////// | |
| 684 | - | |
| 685 | - public void initIdGenerator() { | |
| 686 | - if (idGenerator == null) { | |
| 687 | - idGenerator = new StrongUuidGenerator(); | |
| 688 | - } | |
| 689 | - } | |
| 690 | - | |
| 691 | - public void initObjectMapper() { | |
| 692 | - if (objectMapper == null) { | |
| 693 | - objectMapper = new ObjectMapper(); | |
| 694 | - objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); | |
| 695 | - } | |
| 696 | - } | |
| 697 | - | |
| 698 | - public void initClock() { | |
| 699 | - if (clock == null) { | |
| 700 | - clock = new DefaultClockImpl(); | |
| 701 | - } | |
| 702 | - } | |
| 703 | - | |
| 704 | - // Data managers /////////////////////////////////////////////////////////// | |
| 705 | - | |
| 706 | - public void initDataManagers() { | |
| 707 | - if (propertyDataManager == null) { | |
| 708 | - propertyDataManager = new MybatisPropertyDataManager(idGenerator); | |
| 709 | - } | |
| 710 | - | |
| 711 | - if (byteArrayDataManager == null) { | |
| 712 | - byteArrayDataManager = new MybatisByteArrayDataManager(idGenerator); | |
| 713 | - } | |
| 714 | - } | |
| 715 | - | |
| 716 | - // Entity managers ////////////////////////////////////////////////////////// | |
| 717 | - | |
| 718 | - public void initEntityManagers() { | |
| 719 | - if (propertyEntityManager == null) { | |
| 720 | - propertyEntityManager = new PropertyEntityManagerImpl(this, propertyDataManager); | |
| 721 | - } | |
| 722 | - | |
| 723 | - if (byteArrayEntityManager == null) { | |
| 724 | - byteArrayEntityManager = new ByteArrayEntityManagerImpl(byteArrayDataManager, getEngineCfgKey(), this::getEventDispatcher); | |
| 725 | - } | |
| 726 | - | |
| 727 | - if (tableDataManager == null) { | |
| 728 | - tableDataManager = new TableDataManagerImpl(this); | |
| 729 | - } | |
| 730 | - } | |
| 731 | - | |
| 732 | - // services | |
| 733 | - // ///////////////////////////////////////////////////////////////// | |
| 734 | - | |
| 735 | - protected void initService(Object service) { | |
| 736 | - if (service instanceof CommonEngineServiceImpl) { | |
| 737 | - ((CommonEngineServiceImpl) service).setCommandExecutor(commandExecutor); | |
| 738 | - } | |
| 739 | - } | |
| 740 | - | |
| 741 | - // myBatis SqlSessionFactory | |
| 742 | - // //////////////////////////////////////////////// | |
| 743 | - | |
| 744 | - public void initSessionFactories() { | |
| 745 | - if (sessionFactories == null) { | |
| 746 | - sessionFactories = new HashMap<>(); | |
| 747 | - | |
| 748 | - if (usingRelationalDatabase) { | |
| 749 | - initDbSqlSessionFactory(); | |
| 750 | - } | |
| 751 | - | |
| 752 | - addSessionFactory(new GenericManagerFactory(EntityCache.class, EntityCacheImpl.class)); | |
| 753 | - | |
| 754 | - if (isLoggingSessionEnabled()) { | |
| 755 | - if (!sessionFactories.containsKey(LoggingSession.class)) { | |
| 756 | - LoggingSessionFactory loggingSessionFactory = new LoggingSessionFactory(); | |
| 757 | - loggingSessionFactory.setLoggingListener(loggingListener); | |
| 758 | - loggingSessionFactory.setObjectMapper(objectMapper); | |
| 759 | - sessionFactories.put(LoggingSession.class, loggingSessionFactory); | |
| 760 | - } | |
| 761 | - } | |
| 762 | - | |
| 763 | - commandContextFactory.setSessionFactories(sessionFactories); | |
| 764 | - | |
| 765 | - } else { | |
| 766 | - if (usingRelationalDatabase) { | |
| 767 | - initDbSqlSessionFactoryEntitySettings(); | |
| 768 | - } | |
| 769 | - } | |
| 770 | - | |
| 771 | - if (customSessionFactories != null) { | |
| 772 | - for (SessionFactory sessionFactory : customSessionFactories) { | |
| 773 | - addSessionFactory(sessionFactory); | |
| 774 | - } | |
| 775 | - } | |
| 776 | - } | |
| 777 | - | |
| 778 | - public void initDbSqlSessionFactory() { | |
| 779 | - if (dbSqlSessionFactory == null) { | |
| 780 | - dbSqlSessionFactory = createDbSqlSessionFactory(); | |
| 781 | - } | |
| 782 | - dbSqlSessionFactory.setDatabaseType(databaseType); | |
| 783 | - dbSqlSessionFactory.setSqlSessionFactory(sqlSessionFactory); | |
| 784 | - dbSqlSessionFactory.setDbHistoryUsed(isDbHistoryUsed); | |
| 785 | - dbSqlSessionFactory.setDatabaseTablePrefix(databaseTablePrefix); | |
| 786 | - dbSqlSessionFactory.setTablePrefixIsSchema(tablePrefixIsSchema); | |
| 787 | - dbSqlSessionFactory.setDatabaseCatalog(databaseCatalog); | |
| 788 | - dbSqlSessionFactory.setDatabaseSchema(databaseSchema); | |
| 789 | - dbSqlSessionFactory.setMaxNrOfStatementsInBulkInsert(maxNrOfStatementsInBulkInsert); | |
| 790 | - | |
| 791 | - initDbSqlSessionFactoryEntitySettings(); | |
| 792 | - | |
| 793 | - addSessionFactory(dbSqlSessionFactory); | |
| 794 | - } | |
| 795 | - | |
| 796 | - public DbSqlSessionFactory createDbSqlSessionFactory() { | |
| 797 | - return new DbSqlSessionFactory(usePrefixId); | |
| 798 | - } | |
| 799 | - | |
| 800 | - protected abstract void initDbSqlSessionFactoryEntitySettings(); | |
| 801 | - | |
| 802 | - protected void defaultInitDbSqlSessionFactoryEntitySettings(List<Class<? extends Entity>> insertOrder, List<Class<? extends Entity>> deleteOrder) { | |
| 803 | - if (insertOrder != null) { | |
| 804 | - for (Class<? extends Entity> clazz : insertOrder) { | |
| 805 | - dbSqlSessionFactory.getInsertionOrder().add(clazz); | |
| 806 | - | |
| 807 | - if (isBulkInsertEnabled) { | |
| 808 | - dbSqlSessionFactory.getBulkInserteableEntityClasses().add(clazz); | |
| 809 | - } | |
| 810 | - } | |
| 811 | - } | |
| 812 | - | |
| 813 | - if (deleteOrder != null) { | |
| 814 | - for (Class<? extends Entity> clazz : deleteOrder) { | |
| 815 | - dbSqlSessionFactory.getDeletionOrder().add(clazz); | |
| 816 | - } | |
| 817 | - } | |
| 818 | - } | |
| 819 | - | |
| 820 | - public void initTransactionFactory() { | |
| 821 | - if (transactionFactory == null) { | |
| 822 | - if (transactionsExternallyManaged) { | |
| 823 | - transactionFactory = new ManagedTransactionFactory(); | |
| 824 | - Properties properties = new Properties(); | |
| 825 | - properties.put("closeConnection", "false"); | |
| 826 | - this.transactionFactory.setProperties(properties); | |
| 827 | - } else { | |
| 828 | - transactionFactory = new JdbcTransactionFactory(); | |
| 829 | - } | |
| 830 | - } | |
| 831 | - } | |
| 832 | - | |
| 833 | - public void initSqlSessionFactory() { | |
| 834 | - if (sqlSessionFactory == null) { | |
| 835 | - InputStream inputStream = null; | |
| 836 | - try { | |
| 837 | - inputStream = getMyBatisXmlConfigurationStream(); | |
| 838 | - | |
| 839 | - Environment environment = new Environment("default", transactionFactory, dataSource); | |
| 840 | - Reader reader = new InputStreamReader(inputStream); | |
| 841 | - Properties properties = new Properties(); | |
| 842 | - properties.put("prefix", databaseTablePrefix); | |
| 843 | - | |
| 844 | - String wildcardEscapeClause = ""; | |
| 845 | - if ((databaseWildcardEscapeCharacter != null) && (databaseWildcardEscapeCharacter.length() != 0)) { | |
| 846 | - wildcardEscapeClause = " escape '" + databaseWildcardEscapeCharacter + "'"; | |
| 847 | - } | |
| 848 | - properties.put("wildcardEscapeClause", wildcardEscapeClause); | |
| 849 | - | |
| 850 | - // set default properties | |
| 851 | - properties.put("limitBefore", ""); | |
| 852 | - properties.put("limitAfter", ""); | |
| 853 | - properties.put("limitBetween", ""); | |
| 854 | - properties.put("limitBeforeNativeQuery", ""); | |
| 855 | - properties.put("limitAfterNativeQuery", ""); | |
| 856 | - properties.put("blobType", "BLOB"); | |
| 857 | - properties.put("boolValue", "TRUE"); | |
| 858 | - | |
| 859 | - if (databaseType != null) { | |
| 860 | - properties.load(getResourceAsStream(pathToEngineDbProperties())); | |
| 861 | - } | |
| 862 | - | |
| 863 | - Configuration configuration = initMybatisConfiguration(environment, reader, properties); | |
| 864 | - sqlSessionFactory = new DefaultSqlSessionFactory(configuration); | |
| 865 | - | |
| 866 | - } catch (Exception e) { | |
| 867 | - throw new FlowableException("Error while building ibatis SqlSessionFactory: " + e.getMessage(), e); | |
| 868 | - } finally { | |
| 869 | - IoUtil.closeSilently(inputStream); | |
| 870 | - } | |
| 871 | - } else { | |
| 872 | - // This is needed when the SQL Session Factory is created by another engine. | |
| 873 | - // When custom XML Mappers are registered with this engine they need to be loaded in the configuration as well | |
| 874 | - applyCustomMybatisCustomizations(sqlSessionFactory.getConfiguration()); | |
| 875 | - } | |
| 876 | - } | |
| 877 | - | |
| 878 | - public String pathToEngineDbProperties() { | |
| 879 | - return "org/flowable/common/db/properties/" + databaseType + ".properties"; | |
| 880 | - } | |
| 881 | - | |
| 882 | - public Configuration initMybatisConfiguration(Environment environment, Reader reader, Properties properties) { | |
| 883 | - XMLConfigBuilder parser = new XMLConfigBuilder(reader, "", properties); | |
| 884 | - Configuration configuration = parser.getConfiguration(); | |
| 885 | - | |
| 886 | - if (databaseType != null) { | |
| 887 | - configuration.setDatabaseId(databaseType); | |
| 888 | - } | |
| 889 | - | |
| 890 | - configuration.setEnvironment(environment); | |
| 891 | - | |
| 892 | - initMybatisTypeHandlers(configuration); | |
| 893 | - initCustomMybatisInterceptors(configuration); | |
| 894 | - if (isEnableLogSqlExecutionTime()) { | |
| 895 | - initMyBatisLogSqlExecutionTimePlugin(configuration); | |
| 896 | - } | |
| 897 | - | |
| 898 | - configuration = parseMybatisConfiguration(parser); | |
| 899 | - return configuration; | |
| 900 | - } | |
| 901 | - | |
| 902 | - public void initCustomMybatisMappers(Configuration configuration) { | |
| 903 | - if (getCustomMybatisMappers() != null) { | |
| 904 | - for (Class<?> clazz : getCustomMybatisMappers()) { | |
| 905 | - if (!configuration.hasMapper(clazz)) { | |
| 906 | - configuration.addMapper(clazz); | |
| 907 | - } | |
| 908 | - } | |
| 909 | - } | |
| 910 | - } | |
| 911 | - | |
| 912 | - public void initMybatisTypeHandlers(Configuration configuration) { | |
| 913 | - // When mapping into Map<String, Object> there is currently a problem with MyBatis. | |
| 914 | - // It will return objects which are driver specific. | |
| 915 | - // Therefore we are registering the mappings between Object.class and the specific jdbc type here. | |
| 916 | - // see https://github.com/mybatis/mybatis-3/issues/2216 for more info | |
| 917 | - TypeHandlerRegistry handlerRegistry = configuration.getTypeHandlerRegistry(); | |
| 918 | - | |
| 919 | - handlerRegistry.register(Object.class, JdbcType.BOOLEAN, new BooleanTypeHandler()); | |
| 920 | - handlerRegistry.register(Object.class, JdbcType.BIT, new BooleanTypeHandler()); | |
| 921 | - | |
| 922 | - handlerRegistry.register(Object.class, JdbcType.TINYINT, new ByteTypeHandler()); | |
| 923 | - | |
| 924 | - handlerRegistry.register(Object.class, JdbcType.SMALLINT, new ShortTypeHandler()); | |
| 925 | - | |
| 926 | - handlerRegistry.register(Object.class, JdbcType.INTEGER, new IntegerTypeHandler()); | |
| 927 | - | |
| 928 | - handlerRegistry.register(Object.class, JdbcType.FLOAT, new FloatTypeHandler()); | |
| 929 | - | |
| 930 | - handlerRegistry.register(Object.class, JdbcType.DOUBLE, new DoubleTypeHandler()); | |
| 931 | - | |
| 932 | - handlerRegistry.register(Object.class, JdbcType.CHAR, new StringTypeHandler()); | |
| 933 | - handlerRegistry.register(Object.class, JdbcType.CLOB, new ClobTypeHandler()); | |
| 934 | - handlerRegistry.register(Object.class, JdbcType.VARCHAR, new StringTypeHandler()); | |
| 935 | - handlerRegistry.register(Object.class, JdbcType.LONGVARCHAR, new StringTypeHandler()); | |
| 936 | - handlerRegistry.register(Object.class, JdbcType.NVARCHAR, new NStringTypeHandler()); | |
| 937 | - handlerRegistry.register(Object.class, JdbcType.NCHAR, new NStringTypeHandler()); | |
| 938 | - handlerRegistry.register(Object.class, JdbcType.NCLOB, new NClobTypeHandler()); | |
| 939 | - | |
| 940 | - handlerRegistry.register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler()); | |
| 941 | - | |
| 942 | - handlerRegistry.register(Object.class, JdbcType.BIGINT, new LongTypeHandler()); | |
| 943 | - | |
| 944 | - handlerRegistry.register(Object.class, JdbcType.REAL, new BigDecimalTypeHandler()); | |
| 945 | - handlerRegistry.register(Object.class, JdbcType.DECIMAL, new BigDecimalTypeHandler()); | |
| 946 | - handlerRegistry.register(Object.class, JdbcType.NUMERIC, new BigDecimalTypeHandler()); | |
| 947 | - | |
| 948 | - handlerRegistry.register(Object.class, JdbcType.BLOB, new BlobInputStreamTypeHandler()); | |
| 949 | - handlerRegistry.register(Object.class, JdbcType.LONGVARBINARY, new BlobTypeHandler()); | |
| 950 | - | |
| 951 | - handlerRegistry.register(Object.class, JdbcType.DATE, new DateOnlyTypeHandler()); | |
| 952 | - handlerRegistry.register(Object.class, JdbcType.TIME, new TimeOnlyTypeHandler()); | |
| 953 | - handlerRegistry.register(Object.class, JdbcType.TIMESTAMP, new DateTypeHandler()); | |
| 954 | - | |
| 955 | - handlerRegistry.register(Object.class, JdbcType.SQLXML, new SqlxmlTypeHandler()); | |
| 956 | - } | |
| 957 | - | |
| 958 | - public void initCustomMybatisInterceptors(Configuration configuration) { | |
| 959 | - if (customMybatisInterceptors!=null){ | |
| 960 | - for (Interceptor interceptor :customMybatisInterceptors){ | |
| 961 | - configuration.addInterceptor(interceptor); | |
| 962 | - } | |
| 963 | - } | |
| 964 | - } | |
| 965 | - | |
| 966 | - public void initMyBatisLogSqlExecutionTimePlugin(Configuration configuration) { | |
| 967 | - configuration.addInterceptor(new LogSqlExecutionTimePlugin()); | |
| 968 | - } | |
| 969 | - | |
| 970 | - public Configuration parseMybatisConfiguration(XMLConfigBuilder parser) { | |
| 971 | - Configuration configuration = parser.parse(); | |
| 972 | - | |
| 973 | - applyCustomMybatisCustomizations(configuration); | |
| 974 | - return configuration; | |
| 975 | - } | |
| 976 | - | |
| 977 | - protected void applyCustomMybatisCustomizations(Configuration configuration) { | |
| 978 | - initCustomMybatisMappers(configuration); | |
| 979 | - | |
| 980 | - if (dependentEngineMybatisTypeAliasConfigs != null) { | |
| 981 | - for (MybatisTypeAliasConfigurator typeAliasConfig : dependentEngineMybatisTypeAliasConfigs) { | |
| 982 | - typeAliasConfig.configure(configuration.getTypeAliasRegistry()); | |
| 983 | - } | |
| 984 | - } | |
| 985 | - if (dependentEngineMybatisTypeHandlerConfigs != null) { | |
| 986 | - for (MybatisTypeHandlerConfigurator typeHandlerConfig : dependentEngineMybatisTypeHandlerConfigs) { | |
| 987 | - typeHandlerConfig.configure(configuration.getTypeHandlerRegistry()); | |
| 988 | - } | |
| 989 | - } | |
| 990 | - | |
| 991 | - parseDependentEngineMybatisXMLMappers(configuration); | |
| 992 | - parseCustomMybatisXMLMappers(configuration); | |
| 993 | - } | |
| 994 | - | |
| 995 | - public void parseCustomMybatisXMLMappers(Configuration configuration) { | |
| 996 | - if (getCustomMybatisXMLMappers() != null) { | |
| 997 | - for (String resource : getCustomMybatisXMLMappers()) { | |
| 998 | - parseMybatisXmlMapping(configuration, resource); | |
| 999 | - } | |
| 1000 | - } | |
| 1001 | - } | |
| 1002 | - | |
| 1003 | - public void parseDependentEngineMybatisXMLMappers(Configuration configuration) { | |
| 1004 | - if (getDependentEngineMyBatisXmlMappers() != null) { | |
| 1005 | - for (String resource : getDependentEngineMyBatisXmlMappers()) { | |
| 1006 | - parseMybatisXmlMapping(configuration, resource); | |
| 1007 | - } | |
| 1008 | - } | |
| 1009 | - } | |
| 1010 | - | |
| 1011 | - protected void parseMybatisXmlMapping(Configuration configuration, String resource) { | |
| 1012 | - // see XMLConfigBuilder.mapperElement() | |
| 1013 | - XMLMapperBuilder mapperParser = new XMLMapperBuilder(getResourceAsStream(resource), configuration, resource, configuration.getSqlFragments()); | |
| 1014 | - mapperParser.parse(); | |
| 1015 | - } | |
| 1016 | - | |
| 1017 | - protected InputStream getResourceAsStream(String resource) { | |
| 1018 | - ClassLoader classLoader = getClassLoader(); | |
| 1019 | - if (classLoader != null) { | |
| 1020 | - return getClassLoader().getResourceAsStream(resource); | |
| 1021 | - } else { | |
| 1022 | - return this.getClass().getClassLoader().getResourceAsStream(resource); | |
| 1023 | - } | |
| 1024 | - } | |
| 1025 | - | |
| 1026 | - public void setMybatisMappingFile(String file) { | |
| 1027 | - this.mybatisMappingFile = file; | |
| 1028 | - } | |
| 1029 | - | |
| 1030 | - public String getMybatisMappingFile() { | |
| 1031 | - return mybatisMappingFile; | |
| 1032 | - } | |
| 1033 | - | |
| 1034 | - public abstract InputStream getMyBatisXmlConfigurationStream(); | |
| 1035 | - | |
| 1036 | - public void initConfigurators() { | |
| 1037 | - | |
| 1038 | - allConfigurators = new ArrayList<>(); | |
| 1039 | - allConfigurators.addAll(getEngineSpecificEngineConfigurators()); | |
| 1040 | - | |
| 1041 | - // Configurators that are explicitly added to the config | |
| 1042 | - if (configurators != null) { | |
| 1043 | - allConfigurators.addAll(configurators); | |
| 1044 | - } | |
| 1045 | - | |
| 1046 | - // Auto discovery through ServiceLoader | |
| 1047 | - if (enableConfiguratorServiceLoader) { | |
| 1048 | - ClassLoader classLoader = getClassLoader(); | |
| 1049 | - if (classLoader == null) { | |
| 1050 | - classLoader = ReflectUtil.getClassLoader(); | |
| 1051 | - } | |
| 1052 | - | |
| 1053 | - ServiceLoader<EngineConfigurator> configuratorServiceLoader = ServiceLoader.load(EngineConfigurator.class, classLoader); | |
| 1054 | - int nrOfServiceLoadedConfigurators = 0; | |
| 1055 | - for (EngineConfigurator configurator : configuratorServiceLoader) { | |
| 1056 | - allConfigurators.add(configurator); | |
| 1057 | - nrOfServiceLoadedConfigurators++; | |
| 1058 | - } | |
| 1059 | - | |
| 1060 | - if (nrOfServiceLoadedConfigurators > 0) { | |
| 1061 | - logger.info("Found {} auto-discoverable Process Engine Configurator{}", nrOfServiceLoadedConfigurators, nrOfServiceLoadedConfigurators > 1 ? "s" : ""); | |
| 1062 | - } | |
| 1063 | - | |
| 1064 | - if (!allConfigurators.isEmpty()) { | |
| 1065 | - | |
| 1066 | - // Order them according to the priorities (useful for dependent | |
| 1067 | - // configurator) | |
| 1068 | - allConfigurators.sort(new Comparator<EngineConfigurator>() { | |
| 1069 | - | |
| 1070 | - @Override | |
| 1071 | - public int compare(EngineConfigurator configurator1, EngineConfigurator configurator2) { | |
| 1072 | - int priority1 = configurator1.getPriority(); | |
| 1073 | - int priority2 = configurator2.getPriority(); | |
| 1074 | - | |
| 1075 | - if (priority1 < priority2) { | |
| 1076 | - return -1; | |
| 1077 | - } else if (priority1 > priority2) { | |
| 1078 | - return 1; | |
| 1079 | - } | |
| 1080 | - return 0; | |
| 1081 | - } | |
| 1082 | - }); | |
| 1083 | - | |
| 1084 | - // Execute the configurators | |
| 1085 | - logger.info("Found {} Engine Configurators in total:", allConfigurators.size()); | |
| 1086 | - for (EngineConfigurator configurator : allConfigurators) { | |
| 1087 | - logger.info("{} (priority:{})", configurator.getClass(), configurator.getPriority()); | |
| 1088 | - } | |
| 1089 | - | |
| 1090 | - } | |
| 1091 | - | |
| 1092 | - } | |
| 1093 | - } | |
| 1094 | - | |
| 1095 | - public void close() { | |
| 1096 | - if (forceCloseMybatisConnectionPool && dataSource instanceof PooledDataSource) { | |
| 1097 | - /* | |
| 1098 | - * When the datasource is created by a Flowable engine (i.e. it's an instance of PooledDataSource), | |
| 1099 | - * the connection pool needs to be closed when closing the engine. | |
| 1100 | - * Note that calling forceCloseAll() multiple times (as is the case when running with multiple engine) is ok. | |
| 1101 | - */ | |
| 1102 | - ((PooledDataSource) dataSource).forceCloseAll(); | |
| 1103 | - } | |
| 1104 | - } | |
| 1105 | - | |
| 1106 | - protected List<EngineConfigurator> getEngineSpecificEngineConfigurators() { | |
| 1107 | - // meant to be overridden if needed | |
| 1108 | - return Collections.emptyList(); | |
| 1109 | - } | |
| 1110 | - | |
| 1111 | - public void configuratorsBeforeInit() { | |
| 1112 | - for (EngineConfigurator configurator : allConfigurators) { | |
| 1113 | - logger.info("Executing beforeInit() of {} (priority:{})", configurator.getClass(), configurator.getPriority()); | |
| 1114 | - configurator.beforeInit(this); | |
| 1115 | - } | |
| 1116 | - } | |
| 1117 | - | |
| 1118 | - public void configuratorsAfterInit() { | |
| 1119 | - for (EngineConfigurator configurator : allConfigurators) { | |
| 1120 | - logger.info("Executing configure() of {} (priority:{})", configurator.getClass(), configurator.getPriority()); | |
| 1121 | - configurator.configure(this); | |
| 1122 | - } | |
| 1123 | - } | |
| 1124 | - | |
| 1125 | - public LockManager getLockManager(String lockName) { | |
| 1126 | - return new LockManagerImpl(commandExecutor, lockName, getLockPollRate(), getEngineCfgKey()); | |
| 1127 | - } | |
| 1128 | - | |
| 1129 | - // getters and setters | |
| 1130 | - // ////////////////////////////////////////////////////// | |
| 1131 | - | |
| 1132 | - public abstract String getEngineName(); | |
| 1133 | - | |
| 1134 | - public ClassLoader getClassLoader() { | |
| 1135 | - return classLoader; | |
| 1136 | - } | |
| 1137 | - | |
| 1138 | - public AbstractEngineConfiguration setClassLoader(ClassLoader classLoader) { | |
| 1139 | - this.classLoader = classLoader; | |
| 1140 | - return this; | |
| 1141 | - } | |
| 1142 | - | |
| 1143 | - public boolean isUseClassForNameClassLoading() { | |
| 1144 | - return useClassForNameClassLoading; | |
| 1145 | - } | |
| 1146 | - | |
| 1147 | - public AbstractEngineConfiguration setUseClassForNameClassLoading(boolean useClassForNameClassLoading) { | |
| 1148 | - this.useClassForNameClassLoading = useClassForNameClassLoading; | |
| 1149 | - return this; | |
| 1150 | - } | |
| 1151 | - | |
| 1152 | - public void addEngineLifecycleListener(EngineLifecycleListener engineLifecycleListener) { | |
| 1153 | - if (this.engineLifecycleListeners == null) { | |
| 1154 | - this.engineLifecycleListeners = new ArrayList<>(); | |
| 1155 | - } | |
| 1156 | - this.engineLifecycleListeners.add(engineLifecycleListener); | |
| 1157 | - } | |
| 1158 | - | |
| 1159 | - public List<EngineLifecycleListener> getEngineLifecycleListeners() { | |
| 1160 | - return engineLifecycleListeners; | |
| 1161 | - } | |
| 1162 | - | |
| 1163 | - public AbstractEngineConfiguration setEngineLifecycleListeners(List<EngineLifecycleListener> engineLifecycleListeners) { | |
| 1164 | - this.engineLifecycleListeners = engineLifecycleListeners; | |
| 1165 | - return this; | |
| 1166 | - } | |
| 1167 | - | |
| 1168 | - public String getDatabaseType() { | |
| 1169 | - return databaseType; | |
| 1170 | - } | |
| 1171 | - | |
| 1172 | - public AbstractEngineConfiguration setDatabaseType(String databaseType) { | |
| 1173 | - this.databaseType = databaseType; | |
| 1174 | - return this; | |
| 1175 | - } | |
| 1176 | - | |
| 1177 | - public DataSource getDataSource() { | |
| 1178 | - return dataSource; | |
| 1179 | - } | |
| 1180 | - | |
| 1181 | - public AbstractEngineConfiguration setDataSource(DataSource dataSource) { | |
| 1182 | - this.dataSource = dataSource; | |
| 1183 | - return this; | |
| 1184 | - } | |
| 1185 | - | |
| 1186 | - public SchemaManager getSchemaManager() { | |
| 1187 | - return schemaManager; | |
| 1188 | - } | |
| 1189 | - | |
| 1190 | - public AbstractEngineConfiguration setSchemaManager(SchemaManager schemaManager) { | |
| 1191 | - this.schemaManager = schemaManager; | |
| 1192 | - return this; | |
| 1193 | - } | |
| 1194 | - | |
| 1195 | - public SchemaManager getCommonSchemaManager() { | |
| 1196 | - return commonSchemaManager; | |
| 1197 | - } | |
| 1198 | - | |
| 1199 | - public AbstractEngineConfiguration setCommonSchemaManager(SchemaManager commonSchemaManager) { | |
| 1200 | - this.commonSchemaManager = commonSchemaManager; | |
| 1201 | - return this; | |
| 1202 | - } | |
| 1203 | - | |
| 1204 | - public Command<Void> getSchemaManagementCmd() { | |
| 1205 | - return schemaManagementCmd; | |
| 1206 | - } | |
| 1207 | - | |
| 1208 | - public AbstractEngineConfiguration setSchemaManagementCmd(Command<Void> schemaManagementCmd) { | |
| 1209 | - this.schemaManagementCmd = schemaManagementCmd; | |
| 1210 | - return this; | |
| 1211 | - } | |
| 1212 | - | |
| 1213 | - public String getJdbcDriver() { | |
| 1214 | - return jdbcDriver; | |
| 1215 | - } | |
| 1216 | - | |
| 1217 | - public AbstractEngineConfiguration setJdbcDriver(String jdbcDriver) { | |
| 1218 | - this.jdbcDriver = jdbcDriver; | |
| 1219 | - return this; | |
| 1220 | - } | |
| 1221 | - | |
| 1222 | - public String getJdbcUrl() { | |
| 1223 | - return jdbcUrl; | |
| 1224 | - } | |
| 1225 | - | |
| 1226 | - public AbstractEngineConfiguration setJdbcUrl(String jdbcUrl) { | |
| 1227 | - this.jdbcUrl = jdbcUrl; | |
| 1228 | - return this; | |
| 1229 | - } | |
| 1230 | - | |
| 1231 | - public String getJdbcUsername() { | |
| 1232 | - return jdbcUsername; | |
| 1233 | - } | |
| 1234 | - | |
| 1235 | - public AbstractEngineConfiguration setJdbcUsername(String jdbcUsername) { | |
| 1236 | - this.jdbcUsername = jdbcUsername; | |
| 1237 | - return this; | |
| 1238 | - } | |
| 1239 | - | |
| 1240 | - public String getJdbcPassword() { | |
| 1241 | - return jdbcPassword; | |
| 1242 | - } | |
| 1243 | - | |
| 1244 | - public AbstractEngineConfiguration setJdbcPassword(String jdbcPassword) { | |
| 1245 | - this.jdbcPassword = jdbcPassword; | |
| 1246 | - return this; | |
| 1247 | - } | |
| 1248 | - | |
| 1249 | - public int getJdbcMaxActiveConnections() { | |
| 1250 | - return jdbcMaxActiveConnections; | |
| 1251 | - } | |
| 1252 | - | |
| 1253 | - public AbstractEngineConfiguration setJdbcMaxActiveConnections(int jdbcMaxActiveConnections) { | |
| 1254 | - this.jdbcMaxActiveConnections = jdbcMaxActiveConnections; | |
| 1255 | - return this; | |
| 1256 | - } | |
| 1257 | - | |
| 1258 | - public int getJdbcMaxIdleConnections() { | |
| 1259 | - return jdbcMaxIdleConnections; | |
| 1260 | - } | |
| 1261 | - | |
| 1262 | - public AbstractEngineConfiguration setJdbcMaxIdleConnections(int jdbcMaxIdleConnections) { | |
| 1263 | - this.jdbcMaxIdleConnections = jdbcMaxIdleConnections; | |
| 1264 | - return this; | |
| 1265 | - } | |
| 1266 | - | |
| 1267 | - public int getJdbcMaxCheckoutTime() { | |
| 1268 | - return jdbcMaxCheckoutTime; | |
| 1269 | - } | |
| 1270 | - | |
| 1271 | - public AbstractEngineConfiguration setJdbcMaxCheckoutTime(int jdbcMaxCheckoutTime) { | |
| 1272 | - this.jdbcMaxCheckoutTime = jdbcMaxCheckoutTime; | |
| 1273 | - return this; | |
| 1274 | - } | |
| 1275 | - | |
| 1276 | - public int getJdbcMaxWaitTime() { | |
| 1277 | - return jdbcMaxWaitTime; | |
| 1278 | - } | |
| 1279 | - | |
| 1280 | - public AbstractEngineConfiguration setJdbcMaxWaitTime(int jdbcMaxWaitTime) { | |
| 1281 | - this.jdbcMaxWaitTime = jdbcMaxWaitTime; | |
| 1282 | - return this; | |
| 1283 | - } | |
| 1284 | - | |
| 1285 | - public boolean isJdbcPingEnabled() { | |
| 1286 | - return jdbcPingEnabled; | |
| 1287 | - } | |
| 1288 | - | |
| 1289 | - public AbstractEngineConfiguration setJdbcPingEnabled(boolean jdbcPingEnabled) { | |
| 1290 | - this.jdbcPingEnabled = jdbcPingEnabled; | |
| 1291 | - return this; | |
| 1292 | - } | |
| 1293 | - | |
| 1294 | - public int getJdbcPingConnectionNotUsedFor() { | |
| 1295 | - return jdbcPingConnectionNotUsedFor; | |
| 1296 | - } | |
| 1297 | - | |
| 1298 | - public AbstractEngineConfiguration setJdbcPingConnectionNotUsedFor(int jdbcPingConnectionNotUsedFor) { | |
| 1299 | - this.jdbcPingConnectionNotUsedFor = jdbcPingConnectionNotUsedFor; | |
| 1300 | - return this; | |
| 1301 | - } | |
| 1302 | - | |
| 1303 | - public int getJdbcDefaultTransactionIsolationLevel() { | |
| 1304 | - return jdbcDefaultTransactionIsolationLevel; | |
| 1305 | - } | |
| 1306 | - | |
| 1307 | - public AbstractEngineConfiguration setJdbcDefaultTransactionIsolationLevel(int jdbcDefaultTransactionIsolationLevel) { | |
| 1308 | - this.jdbcDefaultTransactionIsolationLevel = jdbcDefaultTransactionIsolationLevel; | |
| 1309 | - return this; | |
| 1310 | - } | |
| 1311 | - | |
| 1312 | - public String getJdbcPingQuery() { | |
| 1313 | - return jdbcPingQuery; | |
| 1314 | - } | |
| 1315 | - | |
| 1316 | - public AbstractEngineConfiguration setJdbcPingQuery(String jdbcPingQuery) { | |
| 1317 | - this.jdbcPingQuery = jdbcPingQuery; | |
| 1318 | - return this; | |
| 1319 | - } | |
| 1320 | - | |
| 1321 | - public String getDataSourceJndiName() { | |
| 1322 | - return dataSourceJndiName; | |
| 1323 | - } | |
| 1324 | - | |
| 1325 | - public AbstractEngineConfiguration setDataSourceJndiName(String dataSourceJndiName) { | |
| 1326 | - this.dataSourceJndiName = dataSourceJndiName; | |
| 1327 | - return this; | |
| 1328 | - } | |
| 1329 | - | |
| 1330 | - public CommandConfig getSchemaCommandConfig() { | |
| 1331 | - return schemaCommandConfig; | |
| 1332 | - } | |
| 1333 | - | |
| 1334 | - public AbstractEngineConfiguration setSchemaCommandConfig(CommandConfig schemaCommandConfig) { | |
| 1335 | - this.schemaCommandConfig = schemaCommandConfig; | |
| 1336 | - return this; | |
| 1337 | - } | |
| 1338 | - | |
| 1339 | - public boolean isTransactionsExternallyManaged() { | |
| 1340 | - return transactionsExternallyManaged; | |
| 1341 | - } | |
| 1342 | - | |
| 1343 | - public AbstractEngineConfiguration setTransactionsExternallyManaged(boolean transactionsExternallyManaged) { | |
| 1344 | - this.transactionsExternallyManaged = transactionsExternallyManaged; | |
| 1345 | - return this; | |
| 1346 | - } | |
| 1347 | - | |
| 1348 | - public Map<Object, Object> getBeans() { | |
| 1349 | - return beans; | |
| 1350 | - } | |
| 1351 | - | |
| 1352 | - public AbstractEngineConfiguration setBeans(Map<Object, Object> beans) { | |
| 1353 | - this.beans = beans; | |
| 1354 | - return this; | |
| 1355 | - } | |
| 1356 | - | |
| 1357 | - public IdGenerator getIdGenerator() { | |
| 1358 | - return idGenerator; | |
| 1359 | - } | |
| 1360 | - | |
| 1361 | - public AbstractEngineConfiguration setIdGenerator(IdGenerator idGenerator) { | |
| 1362 | - this.idGenerator = idGenerator; | |
| 1363 | - return this; | |
| 1364 | - } | |
| 1365 | - | |
| 1366 | - public boolean isUsePrefixId() { | |
| 1367 | - return usePrefixId; | |
| 1368 | - } | |
| 1369 | - | |
| 1370 | - public AbstractEngineConfiguration setUsePrefixId(boolean usePrefixId) { | |
| 1371 | - this.usePrefixId = usePrefixId; | |
| 1372 | - return this; | |
| 1373 | - } | |
| 1374 | - | |
| 1375 | - public String getXmlEncoding() { | |
| 1376 | - return xmlEncoding; | |
| 1377 | - } | |
| 1378 | - | |
| 1379 | - public AbstractEngineConfiguration setXmlEncoding(String xmlEncoding) { | |
| 1380 | - this.xmlEncoding = xmlEncoding; | |
| 1381 | - return this; | |
| 1382 | - } | |
| 1383 | - | |
| 1384 | - public CommandConfig getDefaultCommandConfig() { | |
| 1385 | - return defaultCommandConfig; | |
| 1386 | - } | |
| 1387 | - | |
| 1388 | - public AbstractEngineConfiguration setDefaultCommandConfig(CommandConfig defaultCommandConfig) { | |
| 1389 | - this.defaultCommandConfig = defaultCommandConfig; | |
| 1390 | - return this; | |
| 1391 | - } | |
| 1392 | - | |
| 1393 | - public CommandExecutor getCommandExecutor() { | |
| 1394 | - return commandExecutor; | |
| 1395 | - } | |
| 1396 | - | |
| 1397 | - public AbstractEngineConfiguration setCommandExecutor(CommandExecutor commandExecutor) { | |
| 1398 | - this.commandExecutor = commandExecutor; | |
| 1399 | - return this; | |
| 1400 | - } | |
| 1401 | - | |
| 1402 | - public CommandContextFactory getCommandContextFactory() { | |
| 1403 | - return commandContextFactory; | |
| 1404 | - } | |
| 1405 | - | |
| 1406 | - public AbstractEngineConfiguration setCommandContextFactory(CommandContextFactory commandContextFactory) { | |
| 1407 | - this.commandContextFactory = commandContextFactory; | |
| 1408 | - return this; | |
| 1409 | - } | |
| 1410 | - | |
| 1411 | - public CommandInterceptor getCommandInvoker() { | |
| 1412 | - return commandInvoker; | |
| 1413 | - } | |
| 1414 | - | |
| 1415 | - public AbstractEngineConfiguration setCommandInvoker(CommandInterceptor commandInvoker) { | |
| 1416 | - this.commandInvoker = commandInvoker; | |
| 1417 | - return this; | |
| 1418 | - } | |
| 1419 | - | |
| 1420 | - public AgendaOperationRunner getAgendaOperationRunner() { | |
| 1421 | - return agendaOperationRunner; | |
| 1422 | - } | |
| 1423 | - | |
| 1424 | - public AbstractEngineConfiguration setAgendaOperationRunner(AgendaOperationRunner agendaOperationRunner) { | |
| 1425 | - this.agendaOperationRunner = agendaOperationRunner; | |
| 1426 | - return this; | |
| 1427 | - } | |
| 1428 | - | |
| 1429 | - public List<CommandInterceptor> getCustomPreCommandInterceptors() { | |
| 1430 | - return customPreCommandInterceptors; | |
| 1431 | - } | |
| 1432 | - | |
| 1433 | - public AbstractEngineConfiguration setCustomPreCommandInterceptors(List<CommandInterceptor> customPreCommandInterceptors) { | |
| 1434 | - this.customPreCommandInterceptors = customPreCommandInterceptors; | |
| 1435 | - return this; | |
| 1436 | - } | |
| 1437 | - | |
| 1438 | - public List<CommandInterceptor> getCustomPostCommandInterceptors() { | |
| 1439 | - return customPostCommandInterceptors; | |
| 1440 | - } | |
| 1441 | - | |
| 1442 | - public AbstractEngineConfiguration setCustomPostCommandInterceptors(List<CommandInterceptor> customPostCommandInterceptors) { | |
| 1443 | - this.customPostCommandInterceptors = customPostCommandInterceptors; | |
| 1444 | - return this; | |
| 1445 | - } | |
| 1446 | - | |
| 1447 | - public List<CommandInterceptor> getCommandInterceptors() { | |
| 1448 | - return commandInterceptors; | |
| 1449 | - } | |
| 1450 | - | |
| 1451 | - public AbstractEngineConfiguration setCommandInterceptors(List<CommandInterceptor> commandInterceptors) { | |
| 1452 | - this.commandInterceptors = commandInterceptors; | |
| 1453 | - return this; | |
| 1454 | - } | |
| 1455 | - | |
| 1456 | - public Map<String, AbstractEngineConfiguration> getEngineConfigurations() { | |
| 1457 | - return engineConfigurations; | |
| 1458 | - } | |
| 1459 | - | |
| 1460 | - public AbstractEngineConfiguration setEngineConfigurations(Map<String, AbstractEngineConfiguration> engineConfigurations) { | |
| 1461 | - this.engineConfigurations = engineConfigurations; | |
| 1462 | - return this; | |
| 1463 | - } | |
| 1464 | - | |
| 1465 | - public void addEngineConfiguration(String key, String scopeType, AbstractEngineConfiguration engineConfiguration) { | |
| 1466 | - if (engineConfigurations == null) { | |
| 1467 | - engineConfigurations = new HashMap<>(); | |
| 1468 | - } | |
| 1469 | - engineConfigurations.put(key, engineConfiguration); | |
| 1470 | - engineConfigurations.put(scopeType, engineConfiguration); | |
| 1471 | - } | |
| 1472 | - | |
| 1473 | - public Map<String, AbstractServiceConfiguration> getServiceConfigurations() { | |
| 1474 | - return serviceConfigurations; | |
| 1475 | - } | |
| 1476 | - | |
| 1477 | - public AbstractEngineConfiguration setServiceConfigurations(Map<String, AbstractServiceConfiguration> serviceConfigurations) { | |
| 1478 | - this.serviceConfigurations = serviceConfigurations; | |
| 1479 | - return this; | |
| 1480 | - } | |
| 1481 | - | |
| 1482 | - public void addServiceConfiguration(String key, AbstractServiceConfiguration serviceConfiguration) { | |
| 1483 | - if (serviceConfigurations == null) { | |
| 1484 | - serviceConfigurations = new HashMap<>(); | |
| 1485 | - } | |
| 1486 | - serviceConfigurations.put(key, serviceConfiguration); | |
| 1487 | - } | |
| 1488 | - | |
| 1489 | - public Map<String, EventRegistryEventConsumer> getEventRegistryEventConsumers() { | |
| 1490 | - return eventRegistryEventConsumers; | |
| 1491 | - } | |
| 1492 | - | |
| 1493 | - public AbstractEngineConfiguration setEventRegistryEventConsumers(Map<String, EventRegistryEventConsumer> eventRegistryEventConsumers) { | |
| 1494 | - this.eventRegistryEventConsumers = eventRegistryEventConsumers; | |
| 1495 | - return this; | |
| 1496 | - } | |
| 1497 | - | |
| 1498 | - public void addEventRegistryEventConsumer(String key, EventRegistryEventConsumer eventRegistryEventConsumer) { | |
| 1499 | - if (eventRegistryEventConsumers == null) { | |
| 1500 | - eventRegistryEventConsumers = new HashMap<>(); | |
| 1501 | - } | |
| 1502 | - eventRegistryEventConsumers.put(key, eventRegistryEventConsumer); | |
| 1503 | - } | |
| 1504 | - | |
| 1505 | - public AbstractEngineConfiguration setDefaultCommandInterceptors(Collection<? extends CommandInterceptor> defaultCommandInterceptors) { | |
| 1506 | - this.defaultCommandInterceptors = defaultCommandInterceptors; | |
| 1507 | - return this; | |
| 1508 | - } | |
| 1509 | - | |
| 1510 | - public SqlSessionFactory getSqlSessionFactory() { | |
| 1511 | - return sqlSessionFactory; | |
| 1512 | - } | |
| 1513 | - | |
| 1514 | - public AbstractEngineConfiguration setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { | |
| 1515 | - this.sqlSessionFactory = sqlSessionFactory; | |
| 1516 | - return this; | |
| 1517 | - } | |
| 1518 | - | |
| 1519 | - public boolean isDbHistoryUsed() { | |
| 1520 | - return isDbHistoryUsed; | |
| 1521 | - } | |
| 1522 | - | |
| 1523 | - public AbstractEngineConfiguration setDbHistoryUsed(boolean isDbHistoryUsed) { | |
| 1524 | - this.isDbHistoryUsed = isDbHistoryUsed; | |
| 1525 | - return this; | |
| 1526 | - } | |
| 1527 | - | |
| 1528 | - public DbSqlSessionFactory getDbSqlSessionFactory() { | |
| 1529 | - return dbSqlSessionFactory; | |
| 1530 | - } | |
| 1531 | - | |
| 1532 | - public AbstractEngineConfiguration setDbSqlSessionFactory(DbSqlSessionFactory dbSqlSessionFactory) { | |
| 1533 | - this.dbSqlSessionFactory = dbSqlSessionFactory; | |
| 1534 | - return this; | |
| 1535 | - } | |
| 1536 | - | |
| 1537 | - public TransactionFactory getTransactionFactory() { | |
| 1538 | - return transactionFactory; | |
| 1539 | - } | |
| 1540 | - | |
| 1541 | - public AbstractEngineConfiguration setTransactionFactory(TransactionFactory transactionFactory) { | |
| 1542 | - this.transactionFactory = transactionFactory; | |
| 1543 | - return this; | |
| 1544 | - } | |
| 1545 | - | |
| 1546 | - public TransactionContextFactory getTransactionContextFactory() { | |
| 1547 | - return transactionContextFactory; | |
| 1548 | - } | |
| 1549 | - | |
| 1550 | - public AbstractEngineConfiguration setTransactionContextFactory(TransactionContextFactory transactionContextFactory) { | |
| 1551 | - this.transactionContextFactory = transactionContextFactory; | |
| 1552 | - return this; | |
| 1553 | - } | |
| 1554 | - | |
| 1555 | - public int getMaxNrOfStatementsInBulkInsert() { | |
| 1556 | - return maxNrOfStatementsInBulkInsert; | |
| 1557 | - } | |
| 1558 | - | |
| 1559 | - public AbstractEngineConfiguration setMaxNrOfStatementsInBulkInsert(int maxNrOfStatementsInBulkInsert) { | |
| 1560 | - this.maxNrOfStatementsInBulkInsert = maxNrOfStatementsInBulkInsert; | |
| 1561 | - return this; | |
| 1562 | - } | |
| 1563 | - | |
| 1564 | - public boolean isBulkInsertEnabled() { | |
| 1565 | - return isBulkInsertEnabled; | |
| 1566 | - } | |
| 1567 | - | |
| 1568 | - public AbstractEngineConfiguration setBulkInsertEnabled(boolean isBulkInsertEnabled) { | |
| 1569 | - this.isBulkInsertEnabled = isBulkInsertEnabled; | |
| 1570 | - return this; | |
| 1571 | - } | |
| 1572 | - | |
| 1573 | - public Set<Class<?>> getCustomMybatisMappers() { | |
| 1574 | - return customMybatisMappers; | |
| 1575 | - } | |
| 1576 | - | |
| 1577 | - public AbstractEngineConfiguration setCustomMybatisMappers(Set<Class<?>> customMybatisMappers) { | |
| 1578 | - this.customMybatisMappers = customMybatisMappers; | |
| 1579 | - return this; | |
| 1580 | - } | |
| 1581 | - | |
| 1582 | - public Set<String> getCustomMybatisXMLMappers() { | |
| 1583 | - return customMybatisXMLMappers; | |
| 1584 | - } | |
| 1585 | - | |
| 1586 | - public AbstractEngineConfiguration setCustomMybatisXMLMappers(Set<String> customMybatisXMLMappers) { | |
| 1587 | - this.customMybatisXMLMappers = customMybatisXMLMappers; | |
| 1588 | - return this; | |
| 1589 | - } | |
| 1590 | - | |
| 1591 | - public Set<String> getDependentEngineMyBatisXmlMappers() { | |
| 1592 | - return dependentEngineMyBatisXmlMappers; | |
| 1593 | - } | |
| 1594 | - | |
| 1595 | - public AbstractEngineConfiguration setCustomMybatisInterceptors(List<Interceptor> customMybatisInterceptors) { | |
| 1596 | - this.customMybatisInterceptors = customMybatisInterceptors; | |
| 1597 | - return this; | |
| 1598 | - } | |
| 1599 | - | |
| 1600 | - public List<Interceptor> getCustomMybatisInterceptors() { | |
| 1601 | - return customMybatisInterceptors; | |
| 1602 | - } | |
| 1603 | - | |
| 1604 | - public AbstractEngineConfiguration setDependentEngineMyBatisXmlMappers(Set<String> dependentEngineMyBatisXmlMappers) { | |
| 1605 | - this.dependentEngineMyBatisXmlMappers = dependentEngineMyBatisXmlMappers; | |
| 1606 | - return this; | |
| 1607 | - } | |
| 1608 | - | |
| 1609 | - public List<MybatisTypeAliasConfigurator> getDependentEngineMybatisTypeAliasConfigs() { | |
| 1610 | - return dependentEngineMybatisTypeAliasConfigs; | |
| 1611 | - } | |
| 1612 | - | |
| 1613 | - public AbstractEngineConfiguration setDependentEngineMybatisTypeAliasConfigs(List<MybatisTypeAliasConfigurator> dependentEngineMybatisTypeAliasConfigs) { | |
| 1614 | - this.dependentEngineMybatisTypeAliasConfigs = dependentEngineMybatisTypeAliasConfigs; | |
| 1615 | - return this; | |
| 1616 | - } | |
| 1617 | - | |
| 1618 | - public List<MybatisTypeHandlerConfigurator> getDependentEngineMybatisTypeHandlerConfigs() { | |
| 1619 | - return dependentEngineMybatisTypeHandlerConfigs; | |
| 1620 | - } | |
| 1621 | - | |
| 1622 | - public AbstractEngineConfiguration setDependentEngineMybatisTypeHandlerConfigs(List<MybatisTypeHandlerConfigurator> dependentEngineMybatisTypeHandlerConfigs) { | |
| 1623 | - this.dependentEngineMybatisTypeHandlerConfigs = dependentEngineMybatisTypeHandlerConfigs; | |
| 1624 | - return this; | |
| 1625 | - } | |
| 1626 | - | |
| 1627 | - public List<SessionFactory> getCustomSessionFactories() { | |
| 1628 | - return customSessionFactories; | |
| 1629 | - } | |
| 1630 | - | |
| 1631 | - public AbstractEngineConfiguration addCustomSessionFactory(SessionFactory sessionFactory) { | |
| 1632 | - if (customSessionFactories == null) { | |
| 1633 | - customSessionFactories = new ArrayList<>(); | |
| 1634 | - } | |
| 1635 | - customSessionFactories.add(sessionFactory); | |
| 1636 | - return this; | |
| 1637 | - } | |
| 1638 | - | |
| 1639 | - public AbstractEngineConfiguration setCustomSessionFactories(List<SessionFactory> customSessionFactories) { | |
| 1640 | - this.customSessionFactories = customSessionFactories; | |
| 1641 | - return this; | |
| 1642 | - } | |
| 1643 | - | |
| 1644 | - public boolean isUsingRelationalDatabase() { | |
| 1645 | - return usingRelationalDatabase; | |
| 1646 | - } | |
| 1647 | - | |
| 1648 | - public AbstractEngineConfiguration setUsingRelationalDatabase(boolean usingRelationalDatabase) { | |
| 1649 | - this.usingRelationalDatabase = usingRelationalDatabase; | |
| 1650 | - return this; | |
| 1651 | - } | |
| 1652 | - | |
| 1653 | - public boolean isUsingSchemaMgmt() { | |
| 1654 | - return usingSchemaMgmt; | |
| 1655 | - } | |
| 1656 | - | |
| 1657 | - public AbstractEngineConfiguration setUsingSchemaMgmt(boolean usingSchema) { | |
| 1658 | - this.usingSchemaMgmt = usingSchema; | |
| 1659 | - return this; | |
| 1660 | - } | |
| 1661 | - | |
| 1662 | - public String getDatabaseTablePrefix() { | |
| 1663 | - return databaseTablePrefix; | |
| 1664 | - } | |
| 1665 | - | |
| 1666 | - public AbstractEngineConfiguration setDatabaseTablePrefix(String databaseTablePrefix) { | |
| 1667 | - this.databaseTablePrefix = databaseTablePrefix; | |
| 1668 | - return this; | |
| 1669 | - } | |
| 1670 | - | |
| 1671 | - public String getDatabaseWildcardEscapeCharacter() { | |
| 1672 | - return databaseWildcardEscapeCharacter; | |
| 1673 | - } | |
| 1674 | - | |
| 1675 | - public AbstractEngineConfiguration setDatabaseWildcardEscapeCharacter(String databaseWildcardEscapeCharacter) { | |
| 1676 | - this.databaseWildcardEscapeCharacter = databaseWildcardEscapeCharacter; | |
| 1677 | - return this; | |
| 1678 | - } | |
| 1679 | - | |
| 1680 | - public String getDatabaseCatalog() { | |
| 1681 | - return databaseCatalog; | |
| 1682 | - } | |
| 1683 | - | |
| 1684 | - public AbstractEngineConfiguration setDatabaseCatalog(String databaseCatalog) { | |
| 1685 | - this.databaseCatalog = databaseCatalog; | |
| 1686 | - return this; | |
| 1687 | - } | |
| 1688 | - | |
| 1689 | - public String getDatabaseSchema() { | |
| 1690 | - return databaseSchema; | |
| 1691 | - } | |
| 1692 | - | |
| 1693 | - public AbstractEngineConfiguration setDatabaseSchema(String databaseSchema) { | |
| 1694 | - this.databaseSchema = databaseSchema; | |
| 1695 | - return this; | |
| 1696 | - } | |
| 1697 | - | |
| 1698 | - public boolean isTablePrefixIsSchema() { | |
| 1699 | - return tablePrefixIsSchema; | |
| 1700 | - } | |
| 1701 | - | |
| 1702 | - public AbstractEngineConfiguration setTablePrefixIsSchema(boolean tablePrefixIsSchema) { | |
| 1703 | - this.tablePrefixIsSchema = tablePrefixIsSchema; | |
| 1704 | - return this; | |
| 1705 | - } | |
| 1706 | - | |
| 1707 | - public boolean isAlwaysLookupLatestDefinitionVersion() { | |
| 1708 | - return alwaysLookupLatestDefinitionVersion; | |
| 1709 | - } | |
| 1710 | - | |
| 1711 | - public AbstractEngineConfiguration setAlwaysLookupLatestDefinitionVersion(boolean alwaysLookupLatestDefinitionVersion) { | |
| 1712 | - this.alwaysLookupLatestDefinitionVersion = alwaysLookupLatestDefinitionVersion; | |
| 1713 | - return this; | |
| 1714 | - } | |
| 1715 | - | |
| 1716 | - public boolean isFallbackToDefaultTenant() { | |
| 1717 | - return fallbackToDefaultTenant; | |
| 1718 | - } | |
| 1719 | - | |
| 1720 | - public AbstractEngineConfiguration setFallbackToDefaultTenant(boolean fallbackToDefaultTenant) { | |
| 1721 | - this.fallbackToDefaultTenant = fallbackToDefaultTenant; | |
| 1722 | - return this; | |
| 1723 | - } | |
| 1724 | - | |
| 1725 | - /** | |
| 1726 | - * @return name of the default tenant | |
| 1727 | - * @deprecated use {@link AbstractEngineConfiguration#getDefaultTenantProvider()} instead | |
| 1728 | - */ | |
| 1729 | - @Deprecated | |
| 1730 | - public String getDefaultTenantValue() { | |
| 1731 | - return getDefaultTenantProvider().getDefaultTenant(null, null, null); | |
| 1732 | - } | |
| 1733 | - | |
| 1734 | - public AbstractEngineConfiguration setDefaultTenantValue(String defaultTenantValue) { | |
| 1735 | - this.defaultTenantProvider = (tenantId, scope, scopeKey) -> defaultTenantValue; | |
| 1736 | - return this; | |
| 1737 | - } | |
| 1738 | - | |
| 1739 | - public DefaultTenantProvider getDefaultTenantProvider() { | |
| 1740 | - return defaultTenantProvider; | |
| 1741 | - } | |
| 1742 | - | |
| 1743 | - public AbstractEngineConfiguration setDefaultTenantProvider(DefaultTenantProvider defaultTenantProvider) { | |
| 1744 | - this.defaultTenantProvider = defaultTenantProvider; | |
| 1745 | - return this; | |
| 1746 | - } | |
| 1747 | - | |
| 1748 | - public boolean isEnableLogSqlExecutionTime() { | |
| 1749 | - return enableLogSqlExecutionTime; | |
| 1750 | - } | |
| 1751 | - | |
| 1752 | - public void setEnableLogSqlExecutionTime(boolean enableLogSqlExecutionTime) { | |
| 1753 | - this.enableLogSqlExecutionTime = enableLogSqlExecutionTime; | |
| 1754 | - } | |
| 1755 | - | |
| 1756 | - public Map<Class<?>, SessionFactory> getSessionFactories() { | |
| 1757 | - return sessionFactories; | |
| 1758 | - } | |
| 1759 | - | |
| 1760 | - public AbstractEngineConfiguration setSessionFactories(Map<Class<?>, SessionFactory> sessionFactories) { | |
| 1761 | - this.sessionFactories = sessionFactories; | |
| 1762 | - return this; | |
| 1763 | - } | |
| 1764 | - | |
| 1765 | - public String getDatabaseSchemaUpdate() { | |
| 1766 | - return databaseSchemaUpdate; | |
| 1767 | - } | |
| 1768 | - | |
| 1769 | - public AbstractEngineConfiguration setDatabaseSchemaUpdate(String databaseSchemaUpdate) { | |
| 1770 | - this.databaseSchemaUpdate = databaseSchemaUpdate; | |
| 1771 | - return this; | |
| 1772 | - } | |
| 1773 | - | |
| 1774 | - public boolean isUseLockForDatabaseSchemaUpdate() { | |
| 1775 | - return useLockForDatabaseSchemaUpdate; | |
| 1776 | - } | |
| 1777 | - | |
| 1778 | - public AbstractEngineConfiguration setUseLockForDatabaseSchemaUpdate(boolean useLockForDatabaseSchemaUpdate) { | |
| 1779 | - this.useLockForDatabaseSchemaUpdate = useLockForDatabaseSchemaUpdate; | |
| 1780 | - return this; | |
| 1781 | - } | |
| 1782 | - | |
| 1783 | - public boolean isEnableEventDispatcher() { | |
| 1784 | - return enableEventDispatcher; | |
| 1785 | - } | |
| 1786 | - | |
| 1787 | - public AbstractEngineConfiguration setEnableEventDispatcher(boolean enableEventDispatcher) { | |
| 1788 | - this.enableEventDispatcher = enableEventDispatcher; | |
| 1789 | - return this; | |
| 1790 | - } | |
| 1791 | - | |
| 1792 | - public FlowableEventDispatcher getEventDispatcher() { | |
| 1793 | - return eventDispatcher; | |
| 1794 | - } | |
| 1795 | - | |
| 1796 | - public AbstractEngineConfiguration setEventDispatcher(FlowableEventDispatcher eventDispatcher) { | |
| 1797 | - this.eventDispatcher = eventDispatcher; | |
| 1798 | - return this; | |
| 1799 | - } | |
| 1800 | - | |
| 1801 | - public List<FlowableEventListener> getEventListeners() { | |
| 1802 | - return eventListeners; | |
| 1803 | - } | |
| 1804 | - | |
| 1805 | - public AbstractEngineConfiguration setEventListeners(List<FlowableEventListener> eventListeners) { | |
| 1806 | - this.eventListeners = eventListeners; | |
| 1807 | - return this; | |
| 1808 | - } | |
| 1809 | - | |
| 1810 | - public Map<String, List<FlowableEventListener>> getTypedEventListeners() { | |
| 1811 | - return typedEventListeners; | |
| 1812 | - } | |
| 1813 | - | |
| 1814 | - public AbstractEngineConfiguration setTypedEventListeners(Map<String, List<FlowableEventListener>> typedEventListeners) { | |
| 1815 | - this.typedEventListeners = typedEventListeners; | |
| 1816 | - return this; | |
| 1817 | - } | |
| 1818 | - | |
| 1819 | - public List<EventDispatchAction> getAdditionalEventDispatchActions() { | |
| 1820 | - return additionalEventDispatchActions; | |
| 1821 | - } | |
| 1822 | - | |
| 1823 | - public AbstractEngineConfiguration setAdditionalEventDispatchActions(List<EventDispatchAction> additionalEventDispatchActions) { | |
| 1824 | - this.additionalEventDispatchActions = additionalEventDispatchActions; | |
| 1825 | - return this; | |
| 1826 | - } | |
| 1827 | - | |
| 1828 | - public void initEventDispatcher() { | |
| 1829 | - if (this.eventDispatcher == null) { | |
| 1830 | - this.eventDispatcher = new FlowableEventDispatcherImpl(); | |
| 1831 | - } | |
| 1832 | - | |
| 1833 | - initAdditionalEventDispatchActions(); | |
| 1834 | - | |
| 1835 | - this.eventDispatcher.setEnabled(enableEventDispatcher); | |
| 1836 | - | |
| 1837 | - initEventListeners(); | |
| 1838 | - initTypedEventListeners(); | |
| 1839 | - } | |
| 1840 | - | |
| 1841 | - protected void initEventListeners() { | |
| 1842 | - if (eventListeners != null) { | |
| 1843 | - for (FlowableEventListener listenerToAdd : eventListeners) { | |
| 1844 | - this.eventDispatcher.addEventListener(listenerToAdd); | |
| 1845 | - } | |
| 1846 | - } | |
| 1847 | - } | |
| 1848 | - | |
| 1849 | - protected void initAdditionalEventDispatchActions() { | |
| 1850 | - if (this.additionalEventDispatchActions == null) { | |
| 1851 | - this.additionalEventDispatchActions = new ArrayList<>(); | |
| 1852 | - } | |
| 1853 | - } | |
| 1854 | - | |
| 1855 | - protected void initTypedEventListeners() { | |
| 1856 | - if (typedEventListeners != null) { | |
| 1857 | - for (Map.Entry<String, List<FlowableEventListener>> listenersToAdd : typedEventListeners.entrySet()) { | |
| 1858 | - // Extract types from the given string | |
| 1859 | - FlowableEngineEventType[] types = FlowableEngineEventType.getTypesFromString(listenersToAdd.getKey()); | |
| 1860 | - | |
| 1861 | - for (FlowableEventListener listenerToAdd : listenersToAdd.getValue()) { | |
| 1862 | - this.eventDispatcher.addEventListener(listenerToAdd, types); | |
| 1863 | - } | |
| 1864 | - } | |
| 1865 | - } | |
| 1866 | - } | |
| 1867 | - | |
| 1868 | - public boolean isLoggingSessionEnabled() { | |
| 1869 | - return loggingListener != null; | |
| 1870 | - } | |
| 1871 | - | |
| 1872 | - public LoggingListener getLoggingListener() { | |
| 1873 | - return loggingListener; | |
| 1874 | - } | |
| 1875 | - | |
| 1876 | - public void setLoggingListener(LoggingListener loggingListener) { | |
| 1877 | - this.loggingListener = loggingListener; | |
| 1878 | - } | |
| 1879 | - | |
| 1880 | - public Clock getClock() { | |
| 1881 | - return clock; | |
| 1882 | - } | |
| 1883 | - | |
| 1884 | - public AbstractEngineConfiguration setClock(Clock clock) { | |
| 1885 | - this.clock = clock; | |
| 1886 | - return this; | |
| 1887 | - } | |
| 1888 | - | |
| 1889 | - public ObjectMapper getObjectMapper() { | |
| 1890 | - return objectMapper; | |
| 1891 | - } | |
| 1892 | - | |
| 1893 | - public AbstractEngineConfiguration setObjectMapper(ObjectMapper objectMapper) { | |
| 1894 | - this.objectMapper = objectMapper; | |
| 1895 | - return this; | |
| 1896 | - } | |
| 1897 | - | |
| 1898 | - public int getMaxLengthString() { | |
| 1899 | - if (maxLengthStringVariableType == -1) { | |
| 1900 | - if ("oracle".equalsIgnoreCase(databaseType)) { | |
| 1901 | - return DEFAULT_ORACLE_MAX_LENGTH_STRING; | |
| 1902 | - } else { | |
| 1903 | - return DEFAULT_GENERIC_MAX_LENGTH_STRING; | |
| 1904 | - } | |
| 1905 | - } else { | |
| 1906 | - return maxLengthStringVariableType; | |
| 1907 | - } | |
| 1908 | - } | |
| 1909 | - | |
| 1910 | - public int getMaxLengthStringVariableType() { | |
| 1911 | - return maxLengthStringVariableType; | |
| 1912 | - } | |
| 1913 | - | |
| 1914 | - public AbstractEngineConfiguration setMaxLengthStringVariableType(int maxLengthStringVariableType) { | |
| 1915 | - this.maxLengthStringVariableType = maxLengthStringVariableType; | |
| 1916 | - return this; | |
| 1917 | - } | |
| 1918 | - | |
| 1919 | - public PropertyDataManager getPropertyDataManager() { | |
| 1920 | - return propertyDataManager; | |
| 1921 | - } | |
| 1922 | - | |
| 1923 | - public Duration getLockPollRate() { | |
| 1924 | - return lockPollRate; | |
| 1925 | - } | |
| 1926 | - | |
| 1927 | - public AbstractEngineConfiguration setLockPollRate(Duration lockPollRate) { | |
| 1928 | - this.lockPollRate = lockPollRate; | |
| 1929 | - return this; | |
| 1930 | - } | |
| 1931 | - | |
| 1932 | - public Duration getSchemaLockWaitTime() { | |
| 1933 | - return schemaLockWaitTime; | |
| 1934 | - } | |
| 1935 | - | |
| 1936 | - public void setSchemaLockWaitTime(Duration schemaLockWaitTime) { | |
| 1937 | - this.schemaLockWaitTime = schemaLockWaitTime; | |
| 1938 | - } | |
| 1939 | - | |
| 1940 | - public AbstractEngineConfiguration setPropertyDataManager(PropertyDataManager propertyDataManager) { | |
| 1941 | - this.propertyDataManager = propertyDataManager; | |
| 1942 | - return this; | |
| 1943 | - } | |
| 1944 | - | |
| 1945 | - public PropertyEntityManager getPropertyEntityManager() { | |
| 1946 | - return propertyEntityManager; | |
| 1947 | - } | |
| 1948 | - | |
| 1949 | - public AbstractEngineConfiguration setPropertyEntityManager(PropertyEntityManager propertyEntityManager) { | |
| 1950 | - this.propertyEntityManager = propertyEntityManager; | |
| 1951 | - return this; | |
| 1952 | - } | |
| 1953 | - | |
| 1954 | - public ByteArrayDataManager getByteArrayDataManager() { | |
| 1955 | - return byteArrayDataManager; | |
| 1956 | - } | |
| 1957 | - | |
| 1958 | - public AbstractEngineConfiguration setByteArrayDataManager(ByteArrayDataManager byteArrayDataManager) { | |
| 1959 | - this.byteArrayDataManager = byteArrayDataManager; | |
| 1960 | - return this; | |
| 1961 | - } | |
| 1962 | - | |
| 1963 | - public ByteArrayEntityManager getByteArrayEntityManager() { | |
| 1964 | - return byteArrayEntityManager; | |
| 1965 | - } | |
| 1966 | - | |
| 1967 | - public AbstractEngineConfiguration setByteArrayEntityManager(ByteArrayEntityManager byteArrayEntityManager) { | |
| 1968 | - this.byteArrayEntityManager = byteArrayEntityManager; | |
| 1969 | - return this; | |
| 1970 | - } | |
| 1971 | - | |
| 1972 | - public TableDataManager getTableDataManager() { | |
| 1973 | - return tableDataManager; | |
| 1974 | - } | |
| 1975 | - | |
| 1976 | - public AbstractEngineConfiguration setTableDataManager(TableDataManager tableDataManager) { | |
| 1977 | - this.tableDataManager = tableDataManager; | |
| 1978 | - return this; | |
| 1979 | - } | |
| 1980 | - | |
| 1981 | - public List<EngineDeployer> getDeployers() { | |
| 1982 | - return deployers; | |
| 1983 | - } | |
| 1984 | - | |
| 1985 | - public AbstractEngineConfiguration setDeployers(List<EngineDeployer> deployers) { | |
| 1986 | - this.deployers = deployers; | |
| 1987 | - return this; | |
| 1988 | - } | |
| 1989 | - | |
| 1990 | - public List<EngineDeployer> getCustomPreDeployers() { | |
| 1991 | - return customPreDeployers; | |
| 1992 | - } | |
| 1993 | - | |
| 1994 | - public AbstractEngineConfiguration setCustomPreDeployers(List<EngineDeployer> customPreDeployers) { | |
| 1995 | - this.customPreDeployers = customPreDeployers; | |
| 1996 | - return this; | |
| 1997 | - } | |
| 1998 | - | |
| 1999 | - public List<EngineDeployer> getCustomPostDeployers() { | |
| 2000 | - return customPostDeployers; | |
| 2001 | - } | |
| 2002 | - | |
| 2003 | - public AbstractEngineConfiguration setCustomPostDeployers(List<EngineDeployer> customPostDeployers) { | |
| 2004 | - this.customPostDeployers = customPostDeployers; | |
| 2005 | - return this; | |
| 2006 | - } | |
| 2007 | - | |
| 2008 | - public boolean isEnableConfiguratorServiceLoader() { | |
| 2009 | - return enableConfiguratorServiceLoader; | |
| 2010 | - } | |
| 2011 | - | |
| 2012 | - public AbstractEngineConfiguration setEnableConfiguratorServiceLoader(boolean enableConfiguratorServiceLoader) { | |
| 2013 | - this.enableConfiguratorServiceLoader = enableConfiguratorServiceLoader; | |
| 2014 | - return this; | |
| 2015 | - } | |
| 2016 | - | |
| 2017 | - public List<EngineConfigurator> getConfigurators() { | |
| 2018 | - return configurators; | |
| 2019 | - } | |
| 2020 | - | |
| 2021 | - public AbstractEngineConfiguration addConfigurator(EngineConfigurator configurator) { | |
| 2022 | - if (configurators == null) { | |
| 2023 | - configurators = new ArrayList<>(); | |
| 2024 | - } | |
| 2025 | - configurators.add(configurator); | |
| 2026 | - return this; | |
| 2027 | - } | |
| 2028 | - | |
| 2029 | - /** | |
| 2030 | - * @return All {@link EngineConfigurator} instances. Will only contain values after init of the engine. | |
| 2031 | - * Use the {@link #getConfigurators()} or {@link #addConfigurator(EngineConfigurator)} methods otherwise. | |
| 2032 | - */ | |
| 2033 | - public List<EngineConfigurator> getAllConfigurators() { | |
| 2034 | - return allConfigurators; | |
| 2035 | - } | |
| 2036 | - | |
| 2037 | - public AbstractEngineConfiguration setConfigurators(List<EngineConfigurator> configurators) { | |
| 2038 | - this.configurators = configurators; | |
| 2039 | - return this; | |
| 2040 | - } | |
| 2041 | - | |
| 2042 | - public EngineConfigurator getIdmEngineConfigurator() { | |
| 2043 | - return idmEngineConfigurator; | |
| 2044 | - } | |
| 2045 | - | |
| 2046 | - public AbstractEngineConfiguration setIdmEngineConfigurator(EngineConfigurator idmEngineConfigurator) { | |
| 2047 | - this.idmEngineConfigurator = idmEngineConfigurator; | |
| 2048 | - return this; | |
| 2049 | - } | |
| 2050 | - | |
| 2051 | - public EngineConfigurator getEventRegistryConfigurator() { | |
| 2052 | - return eventRegistryConfigurator; | |
| 2053 | - } | |
| 2054 | - | |
| 2055 | - public AbstractEngineConfiguration setEventRegistryConfigurator(EngineConfigurator eventRegistryConfigurator) { | |
| 2056 | - this.eventRegistryConfigurator = eventRegistryConfigurator; | |
| 2057 | - return this; | |
| 2058 | - } | |
| 2059 | - | |
| 2060 | - public AbstractEngineConfiguration setForceCloseMybatisConnectionPool(boolean forceCloseMybatisConnectionPool) { | |
| 2061 | - this.forceCloseMybatisConnectionPool = forceCloseMybatisConnectionPool; | |
| 2062 | - return this; | |
| 2063 | - } | |
| 2064 | - | |
| 2065 | - public boolean isForceCloseMybatisConnectionPool() { | |
| 2066 | - return forceCloseMybatisConnectionPool; | |
| 2067 | - } | |
| 2068 | -} |
sql/dm/flowable-patch/src/main/resources/META-INF/package-info.md deleted
sql/dm/flowable-patch/src/main/resources/META-INF/services/liquibase.database.Database deleted
| 1 | -liquibase.database.core.CockroachDatabase | |
| 2 | -liquibase.database.core.DB2Database | |
| 3 | -liquibase.database.core.Db2zDatabase | |
| 4 | -liquibase.database.core.DerbyDatabase | |
| 5 | -liquibase.database.core.Firebird3Database | |
| 6 | -liquibase.database.core.FirebirdDatabase | |
| 7 | -liquibase.database.core.H2Database | |
| 8 | -liquibase.database.core.HsqlDatabase | |
| 9 | -liquibase.database.core.InformixDatabase | |
| 10 | -liquibase.database.core.Ingres9Database | |
| 11 | -liquibase.database.core.MSSQLDatabase | |
| 12 | -liquibase.database.core.MariaDBDatabase | |
| 13 | -liquibase.database.core.MockDatabase | |
| 14 | -liquibase.database.core.MySQLDatabase | |
| 15 | -liquibase.database.core.OracleDatabase | |
| 16 | -liquibase.database.core.PostgresDatabase | |
| 17 | -liquibase.database.core.SQLiteDatabase | |
| 18 | -liquibase.database.core.SybaseASADatabase | |
| 19 | -liquibase.database.core.SybaseDatabase | |
| 20 | -liquibase.database.core.DmDatabase | |
| 21 | -liquibase.database.core.UnsupportedDatabase |
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/YudaoMybatisAutoConfiguration.java
| 1 | 1 | package cn.iocoder.yudao.framework.mybatis.config; |
| 2 | 2 | |
| 3 | -import cn.hutool.core.collection.CollUtil; | |
| 4 | 3 | import cn.hutool.core.util.StrUtil; |
| 5 | 4 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 6 | 5 | import cn.iocoder.yudao.framework.mybatis.core.handler.DefaultDBFieldHandler; |
| ... | ... | @@ -23,6 +22,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; |
| 23 | 22 | import org.apache.ibatis.annotations.Mapper; |
| 24 | 23 | import org.apache.ibatis.mapping.ResultMap; |
| 25 | 24 | import org.mybatis.spring.annotation.MapperScan; |
| 25 | +import org.springframework.beans.factory.ObjectProvider; | |
| 26 | +import org.springframework.beans.factory.SmartInitializingSingleton; | |
| 26 | 27 | import org.springframework.boot.autoconfigure.AutoConfiguration; |
| 27 | 28 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
| 28 | 29 | import org.springframework.context.annotation.Bean; |
| ... | ... | @@ -30,7 +31,6 @@ import org.springframework.context.annotation.Primary; |
| 30 | 31 | import org.springframework.core.env.ConfigurableEnvironment; |
| 31 | 32 | |
| 32 | 33 | import java.util.ArrayList; |
| 33 | -import java.util.List; | |
| 34 | 34 | import java.util.concurrent.TimeUnit; |
| 35 | 35 | |
| 36 | 36 | /** |
| ... | ... | @@ -49,6 +49,8 @@ public class YudaoMybatisAutoConfiguration { |
| 49 | 49 | (cache) -> cache.maximumSize(1024) |
| 50 | 50 | .expireAfterWrite(5, TimeUnit.SECONDS)) |
| 51 | 51 | ); |
| 52 | + // 特殊:JacksonTypeHandler 先使用 JsonUtils 的 ObjectMapper(支持 LocalDateTime),避免使用 MyBatis Plus 默认的 ObjectMapper | |
| 53 | + JacksonTypeHandler.setObjectMapper(JsonUtils.getObjectMapper()); | |
| 52 | 54 | } |
| 53 | 55 | |
| 54 | 56 | @Bean |
| ... | ... | @@ -101,15 +103,11 @@ public class YudaoMybatisAutoConfiguration { |
| 101 | 103 | throw new IllegalArgumentException(StrUtil.format("DbType{} 找不到合适的 IKeyGenerator 实现类", dbType)); |
| 102 | 104 | } |
| 103 | 105 | |
| 104 | - @Bean // 特殊:返回结果使用 Object 而不用 JacksonTypeHandler 的原因,避免因为 JacksonTypeHandler 被 mybatis 全局使用! | |
| 105 | - public Object jacksonTypeHandler(List<ObjectMapper> objectMappers) { | |
| 106 | - // 特殊:设置 JacksonTypeHandler 的 ObjectMapper! | |
| 107 | - ObjectMapper objectMapper = CollUtil.getFirst(objectMappers); | |
| 108 | - if (objectMapper == null) { | |
| 109 | - objectMapper = JsonUtils.getObjectMapper(); | |
| 110 | - } | |
| 111 | - JacksonTypeHandler.setObjectMapper(objectMapper); | |
| 112 | - return new JacksonTypeHandler(Object.class); | |
| 106 | + @Bean | |
| 107 | + public SmartInitializingSingleton jacksonTypeHandlerInitializer(ObjectProvider<ObjectMapper> objectMappers) { | |
| 108 | + // 特殊:所有单例初始化后,设置 JacksonTypeHandler 的 ObjectMapper,优先唯一或 @Primary 的 ObjectMapper,否则使用 JsonUtils 的 | |
| 109 | + // 原因:避免延迟初始化未设置、多个 ObjectMapper 时取到不支持 LocalDateTime 的,且不在创建 SqlSessionFactory 时获取 ObjectMapper。https://t.zsxq.com/KmPNU | |
| 110 | + return () -> JacksonTypeHandler.setObjectMapper(objectMappers.getIfUnique(JsonUtils::getObjectMapper)); | |
| 113 | 111 | } |
| 114 | 112 | |
| 115 | 113 | } | ... | ... |
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClient.java
| ... | ... | @@ -6,6 +6,7 @@ import cn.hutool.core.util.StrUtil; |
| 6 | 6 | import cn.hutool.http.HttpUtil; |
| 7 | 7 | import cn.iocoder.yudao.framework.common.util.http.HttpUtils; |
| 8 | 8 | import cn.iocoder.yudao.module.infra.framework.file.core.client.AbstractFileClient; |
| 9 | +import cn.iocoder.yudao.module.infra.framework.file.core.utils.FileTypeUtils; | |
| 9 | 10 | import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; |
| 10 | 11 | import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; |
| 11 | 12 | import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; |
| ... | ... | @@ -33,6 +34,8 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> { |
| 33 | 34 | |
| 34 | 35 | private static final Duration EXPIRATION_DEFAULT = Duration.ofHours(24); |
| 35 | 36 | |
| 37 | + private static final String CONTENT_DISPOSITION_ATTACHMENT = "attachment"; | |
| 38 | + | |
| 36 | 39 | private S3Client client; |
| 37 | 40 | private S3Presigner presigner; |
| 38 | 41 | |
| ... | ... | @@ -79,6 +82,8 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> { |
| 79 | 82 | .bucket(config.getBucket()) |
| 80 | 83 | .key(path) |
| 81 | 84 | .contentType(type) |
| 85 | + // SVG 等可携带脚本的文件:直链直接打开时按附件下载;页面中作为图片引用时不受影响 | |
| 86 | + .contentDisposition(FileTypeUtils.isScriptable(type) ? CONTENT_DISPOSITION_ATTACHMENT : null) | |
| 82 | 87 | .contentLength((long) content.length) |
| 83 | 88 | .build(); |
| 84 | 89 | // 上传文件 |
| ... | ... | @@ -133,9 +138,14 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> { |
| 133 | 138 | // 2.2 情况二:私有访问:生成 GET 预签名 URL |
| 134 | 139 | String finalPath = path; |
| 135 | 140 | Duration expiration = expirationSeconds != null ? Duration.ofSeconds(expirationSeconds) : EXPIRATION_DEFAULT; |
| 141 | + // 新签发的私有 URL:对象 key 扩展名为 SVG 等可携带脚本的类型时,要求按附件下载 | |
| 142 | + // 注意:不覆盖公开访问、已签发的 URL,以及扩展名与 Content-Type 不一致的直传对象 | |
| 143 | + String contentDisposition = FileTypeUtils.isScriptable(FileTypeUtils.getMineType(finalPath)) | |
| 144 | + ? CONTENT_DISPOSITION_ATTACHMENT : null; | |
| 136 | 145 | URL signedUrl = presigner.presignGetObject(GetObjectPresignRequest.builder() |
| 137 | 146 | .signatureDuration(expiration) |
| 138 | - .getObjectRequest(b -> b.bucket(config.getBucket()).key(finalPath)).build()) | |
| 147 | + .getObjectRequest(b -> b.bucket(config.getBucket()).key(finalPath) | |
| 148 | + .responseContentDisposition(contentDisposition)).build()) | |
| 139 | 149 | .url(); |
| 140 | 150 | return signedUrl.toString(); |
| 141 | 151 | } | ... | ... |
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/utils/FileTypeUtils.java
| ... | ... | @@ -81,13 +81,24 @@ public class FileTypeUtils { |
| 81 | 81 | public static void writeAttachment(HttpServletResponse response, String filename, byte[] content) throws IOException { |
| 82 | 82 | // 设置 header 和 contentType |
| 83 | 83 | String mineType = getMineType(content, filename); |
| 84 | + // 文件名可能伪装,按内容识别为 SVG、HTML、XML 等可携带脚本的类型时,以内容为准 | |
| 85 | + String contentMineType = getMineType(content); | |
| 86 | + if (!isScriptable(mineType) && isScriptable(contentMineType)) { | |
| 87 | + mineType = contentMineType; | |
| 88 | + } | |
| 84 | 89 | response.setContentType(mineType); |
| 90 | + response.setHeader("X-Content-Type-Options", "nosniff"); | |
| 85 | 91 | // 设置内容显示、下载文件名:https://www.cnblogs.com/wq-9/articles/12165056.html |
| 86 | - if (isImage(mineType)) { | |
| 92 | + if (isImage(mineType) && !isScriptable(mineType)) { | |
| 87 | 93 | // 参见 https://github.com/YunaiV/ruoyi-vue-pro/issues/692 讨论 |
| 88 | 94 | response.setHeader("Content-Disposition", buildContentDisposition("inline", filename)); |
| 89 | 95 | } else { |
| 96 | + // SVG 等可携带脚本的文件:直接打开时按附件下载;保留原类型,页面中作为图片引用时仍可显示 | |
| 90 | 97 | response.setHeader("Content-Disposition", buildContentDisposition("attachment", filename)); |
| 98 | + // 被 object、embed 等嵌入时,按沙箱执行,禁止脚本、隔离来源 | |
| 99 | + if (isScriptable(mineType)) { | |
| 100 | + response.setHeader("Content-Security-Policy", "sandbox"); | |
| 101 | + } | |
| 91 | 102 | } |
| 92 | 103 | // 针对 video 的特殊处理,解决视频地址在移动端播放的兼容性问题 |
| 93 | 104 | if (StrUtil.containsIgnoreCase(mineType, "video")) { |
| ... | ... | @@ -131,4 +142,16 @@ public class FileTypeUtils { |
| 131 | 142 | return StrUtil.startWith(mineType, "image/"); |
| 132 | 143 | } |
| 133 | 144 | |
| 145 | + /** | |
| 146 | + * 判断是否是可携带脚本的类型,例如说 SVG、HTML、XHTML、XML,同源直接打开时会执行其中的脚本 | |
| 147 | + * | |
| 148 | + * 注意:Office 的 openxmlformats 类型以 document、sheet 等结尾,不属于此类 | |
| 149 | + * | |
| 150 | + * @param mineType 类型 | |
| 151 | + * @return 是否可携带脚本 | |
| 152 | + */ | |
| 153 | + public static boolean isScriptable(String mineType) { | |
| 154 | + return StrUtil.containsAnyIgnoreCase(mineType, "svg", "html") || StrUtil.endWithIgnoreCase(mineType, "xml"); | |
| 155 | + } | |
| 156 | + | |
| 134 | 157 | } | ... | ... |
yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/s3/S3FileClientTest.java
| ... | ... | @@ -2,16 +2,27 @@ package cn.iocoder.yudao.module.infra.framework.file.core.s3; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.io.resource.ResourceUtil; |
| 4 | 4 | import cn.hutool.core.util.IdUtil; |
| 5 | +import cn.hutool.core.util.ReflectUtil; | |
| 6 | +import cn.hutool.core.util.StrUtil; | |
| 5 | 7 | import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; |
| 6 | 8 | import cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClient; |
| 7 | 9 | import cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig; |
| 8 | 10 | import jakarta.validation.Validation; |
| 9 | 11 | import org.junit.jupiter.api.Disabled; |
| 10 | 12 | import org.junit.jupiter.api.Test; |
| 13 | +import org.mockito.ArgumentCaptor; | |
| 14 | +import software.amazon.awssdk.core.sync.RequestBody; | |
| 15 | +import software.amazon.awssdk.services.s3.S3Client; | |
| 16 | +import software.amazon.awssdk.services.s3.model.PutObjectRequest; | |
| 11 | 17 | |
| 12 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; |
| 13 | 19 | import static org.junit.jupiter.api.Assertions.assertFalse; |
| 20 | +import static org.junit.jupiter.api.Assertions.assertNull; | |
| 14 | 21 | import static org.junit.jupiter.api.Assertions.assertTrue; |
| 22 | +import static org.mockito.ArgumentMatchers.any; | |
| 23 | +import static org.mockito.Mockito.mock; | |
| 24 | +import static org.mockito.Mockito.times; | |
| 25 | +import static org.mockito.Mockito.verify; | |
| 15 | 26 | |
| 16 | 27 | @SuppressWarnings("resource") |
| 17 | 28 | public class S3FileClientTest { |
| ... | ... | @@ -67,6 +78,68 @@ public class S3FileClientTest { |
| 67 | 78 | assertTrue(result.contains("/yudao/avatar/%E4%B8%AD%E6%96%87%20100%25")); |
| 68 | 79 | assertTrue(result.contains("%E6%96%87%E4%BB%B6.jpg")); |
| 69 | 80 | assertFalse(result.contains("%25E4%25B8%25AD")); |
| 81 | + assertFalse(result.contains("response-content-disposition")); | |
| 82 | + } | |
| 83 | + | |
| 84 | + @Test | |
| 85 | + public void testPresignUrl_privateAccess_scriptableAttachment() { | |
| 86 | + // 准备参数 | |
| 87 | + S3FileClientConfig config = new S3FileClientConfig(); | |
| 88 | + config.setAccessKey("admin"); | |
| 89 | + config.setAccessSecret("password"); | |
| 90 | + config.setBucket("yudao"); | |
| 91 | + config.setDomain("http://127.0.0.1:9000/yudao"); | |
| 92 | + config.setEndpoint("http://127.0.0.1:9000"); | |
| 93 | + config.setEnablePathStyleAccess(true); | |
| 94 | + config.setEnablePublicAccess(false); | |
| 95 | + S3FileClient client = new S3FileClient(0L, config); | |
| 96 | + client.init(); | |
| 97 | + | |
| 98 | + // 调用:新上传的 SVG、已保存的历史签名 URL 重新签名 | |
| 99 | + String result = client.presignGetUrl("avatar/图标.svg", 300); | |
| 100 | + String historyResult = client.presignGetUrl( | |
| 101 | + "http://127.0.0.1:9000/yudao/avatar/%E5%9B%BE%E6%A0%87.svg?X-Amz-Signature=old", 300); | |
| 102 | + String putResult = client.presignPutUrl("avatar/图标.svg"); | |
| 103 | + | |
| 104 | + // 断言:GET 签名要求按附件下载;PUT 签名不额外约束请求头,兼容前端直传 | |
| 105 | + assertTrue(result.contains("/yudao/avatar/%E5%9B%BE%E6%A0%87.svg")); | |
| 106 | + assertTrue(result.contains("response-content-disposition=attachment")); | |
| 107 | + assertTrue(historyResult.contains("/yudao/avatar/%E5%9B%BE%E6%A0%87.svg")); | |
| 108 | + assertTrue(historyResult.contains("response-content-disposition=attachment")); | |
| 109 | + assertFalse(historyResult.contains("X-Amz-Signature=old")); | |
| 110 | + assertFalse(StrUtil.containsIgnoreCase(putResult, "content-disposition")); | |
| 111 | + } | |
| 112 | + | |
| 113 | + @Test | |
| 114 | + public void testUpload_scriptableContentDisposition() { | |
| 115 | + // 准备参数 | |
| 116 | + S3FileClientConfig config = new S3FileClientConfig(); | |
| 117 | + config.setAccessKey("admin"); | |
| 118 | + config.setAccessSecret("password"); | |
| 119 | + config.setBucket("yudao"); | |
| 120 | + config.setDomain("http://127.0.0.1:9000/yudao"); | |
| 121 | + config.setEndpoint("http://127.0.0.1:9000"); | |
| 122 | + config.setEnablePathStyleAccess(true); | |
| 123 | + config.setEnablePublicAccess(true); | |
| 124 | + S3FileClient client = new S3FileClient(0L, config); | |
| 125 | + client.init(); | |
| 126 | + // mock S3 客户端 | |
| 127 | + S3Client s3Client = mock(S3Client.class); | |
| 128 | + ReflectUtil.setFieldValue(client, "client", s3Client); | |
| 129 | + | |
| 130 | + // 调用 | |
| 131 | + client.upload(new byte[]{1}, "avatar/图标.svg", "image/svg+xml"); | |
| 132 | + client.upload(new byte[]{1}, "avatar/页面.html", "text/html"); | |
| 133 | + client.upload(new byte[]{1}, "avatar/头像.jpg", "image/jpeg"); | |
| 134 | + | |
| 135 | + // 断言:可携带脚本的对象按附件下载,普通图片不设置 | |
| 136 | + ArgumentCaptor<PutObjectRequest> requestCaptor = ArgumentCaptor.forClass(PutObjectRequest.class); | |
| 137 | + verify(s3Client, times(3)).putObject(requestCaptor.capture(), any(RequestBody.class)); | |
| 138 | + assertEquals("image/svg+xml", requestCaptor.getAllValues().get(0).contentType()); | |
| 139 | + assertEquals("attachment", requestCaptor.getAllValues().get(0).contentDisposition()); | |
| 140 | + assertEquals("attachment", requestCaptor.getAllValues().get(1).contentDisposition()); | |
| 141 | + assertEquals("image/jpeg", requestCaptor.getAllValues().get(2).contentType()); | |
| 142 | + assertNull(requestCaptor.getAllValues().get(2).contentDisposition()); | |
| 70 | 143 | } |
| 71 | 144 | |
| 72 | 145 | @Test | ... | ... |
yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/utils/FileTypeUtilsTest.java
| 1 | 1 | package cn.iocoder.yudao.module.infra.framework.file.core.utils; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.io.resource.ResourceUtil; | |
| 3 | 4 | import org.junit.jupiter.api.Test; |
| 4 | 5 | import org.springframework.mock.web.MockHttpServletResponse; |
| 5 | 6 | |
| 6 | 7 | import java.nio.charset.StandardCharsets; |
| 7 | 8 | |
| 8 | -import static org.junit.jupiter.api.Assertions.assertArrayEquals; | |
| 9 | -import static org.junit.jupiter.api.Assertions.assertEquals; | |
| 9 | +import static org.junit.jupiter.api.Assertions.*; | |
| 10 | 10 | |
| 11 | 11 | /** |
| 12 | 12 | * {@link FileTypeUtils} 的单元测试 |
| ... | ... | @@ -26,7 +26,102 @@ public class FileTypeUtilsTest { |
| 26 | 26 | assertEquals("attachment;filename=\"__ 100%+__.txt\";" |
| 27 | 27 | + "filename*=UTF-8''%E4%B8%AD%E6%96%87%20100%25+%E6%96%87%E4%BB%B6.txt", |
| 28 | 28 | response.getHeader("Content-Disposition")); |
| 29 | + assertEquals("nosniff", response.getHeader("X-Content-Type-Options")); | |
| 30 | + assertNull(response.getHeader("Content-Security-Policy")); | |
| 29 | 31 | assertArrayEquals(content, response.getContentAsByteArray()); |
| 30 | 32 | } |
| 31 | 33 | |
| 34 | + @Test | |
| 35 | + public void testWriteAttachment_imageInline() throws Exception { | |
| 36 | + // 准备参数 | |
| 37 | + MockHttpServletResponse response = new MockHttpServletResponse(); | |
| 38 | + byte[] content = ResourceUtil.readBytes("file/erweima.jpg"); | |
| 39 | + | |
| 40 | + // 调用 | |
| 41 | + FileTypeUtils.writeAttachment(response, "头像.jpg", content); | |
| 42 | + | |
| 43 | + // 断言:普通位图保持 inline,可直接预览 | |
| 44 | + assertEquals("image/jpeg", response.getContentType()); | |
| 45 | + assertEquals("inline;filename=\"__.jpg\";filename*=UTF-8''%E5%A4%B4%E5%83%8F.jpg", | |
| 46 | + response.getHeader("Content-Disposition")); | |
| 47 | + assertEquals("nosniff", response.getHeader("X-Content-Type-Options")); | |
| 48 | + assertNull(response.getHeader("Content-Security-Policy")); | |
| 49 | + assertArrayEquals(content, response.getContentAsByteArray()); | |
| 50 | + } | |
| 51 | + | |
| 52 | + @Test | |
| 53 | + public void testWriteAttachment_scriptableAttachment() throws Exception { | |
| 54 | + // 历史已上传的 SVG、HTML、XML 文件,没有文件记录时按路径中的文件名下载 | |
| 55 | + for (String[] document : new String[][]{ | |
| 56 | + {"图标.svg", "<svg xmlns=\"http://www.w3.org/2000/svg\"><script>alert(1)</script></svg>", | |
| 57 | + "image/svg+xml", "attachment;filename=\"__.svg\";filename*=UTF-8''%E5%9B%BE%E6%A0%87.svg"}, | |
| 58 | + {"页面.html", "<html><script>alert(1)</script></html>", | |
| 59 | + "text/html", "attachment;filename=\"__.html\";filename*=UTF-8''%E9%A1%B5%E9%9D%A2.html"}, | |
| 60 | + {"数据.xml", "<?xml version=\"1.0\"?><root>test</root>", | |
| 61 | + "application/xml", "attachment;filename=\"__.xml\";filename*=UTF-8''%E6%95%B0%E6%8D%AE.xml"}}) { | |
| 62 | + // 准备参数 | |
| 63 | + MockHttpServletResponse response = new MockHttpServletResponse(); | |
| 64 | + byte[] content = document[1].getBytes(StandardCharsets.UTF_8); | |
| 65 | + | |
| 66 | + // 调用 | |
| 67 | + FileTypeUtils.writeAttachment(response, document[0], content); | |
| 68 | + | |
| 69 | + // 断言:保留原类型,直接打开时按附件下载,嵌入时按沙箱执行 | |
| 70 | + assertEquals(document[2], response.getContentType()); | |
| 71 | + assertEquals(document[3], response.getHeader("Content-Disposition")); | |
| 72 | + assertEquals("nosniff", response.getHeader("X-Content-Type-Options")); | |
| 73 | + assertEquals("sandbox", response.getHeader("Content-Security-Policy")); | |
| 74 | + assertArrayEquals(content, response.getContentAsByteArray()); | |
| 75 | + } | |
| 76 | + } | |
| 77 | + | |
| 78 | + @Test | |
| 79 | + public void testWriteAttachment_fakeImageNameDetectContent() throws Exception { | |
| 80 | + // 准备参数:SVG 内容伪装成 png 文件名 | |
| 81 | + MockHttpServletResponse response = new MockHttpServletResponse(); | |
| 82 | + byte[] content = "<svg xmlns=\"http://www.w3.org/2000/svg\"><script>alert(1)</script></svg>" | |
| 83 | + .getBytes(StandardCharsets.UTF_8); | |
| 84 | + | |
| 85 | + // 调用 | |
| 86 | + FileTypeUtils.writeAttachment(response, "图标.png", content); | |
| 87 | + | |
| 88 | + // 断言:以内容识别的 SVG 类型为准,按附件下载 | |
| 89 | + assertEquals("image/svg+xml", response.getContentType()); | |
| 90 | + assertEquals("attachment;filename=\"__.png\";filename*=UTF-8''%E5%9B%BE%E6%A0%87.png", | |
| 91 | + response.getHeader("Content-Disposition")); | |
| 92 | + assertEquals("nosniff", response.getHeader("X-Content-Type-Options")); | |
| 93 | + assertEquals("sandbox", response.getHeader("Content-Security-Policy")); | |
| 94 | + } | |
| 95 | + | |
| 96 | + @Test | |
| 97 | + public void testWriteAttachment_imageNameHintDetectContent() throws Exception { | |
| 98 | + // 准备参数:XML 内容,文件名提示会把类型识别为图片(image/vnd.adobe.premiere) | |
| 99 | + MockHttpServletResponse response = new MockHttpServletResponse(); | |
| 100 | + byte[] content = "<?xml version=\"1.0\"?><root>test</root>".getBytes(StandardCharsets.UTF_8); | |
| 101 | + | |
| 102 | + // 调用 | |
| 103 | + FileTypeUtils.writeAttachment(response, "test.ppj", content); | |
| 104 | + | |
| 105 | + // 断言:仅按内容识别为 XML 时以内容为准,不按图片 inline | |
| 106 | + assertEquals("application/xml", response.getContentType()); | |
| 107 | + assertEquals("attachment;filename=\"test.ppj\";filename*=UTF-8''test.ppj", | |
| 108 | + response.getHeader("Content-Disposition")); | |
| 109 | + assertEquals("nosniff", response.getHeader("X-Content-Type-Options")); | |
| 110 | + assertEquals("sandbox", response.getHeader("Content-Security-Policy")); | |
| 111 | + assertArrayEquals(content, response.getContentAsByteArray()); | |
| 112 | + } | |
| 113 | + | |
| 114 | + @Test | |
| 115 | + public void testIsScriptable() { | |
| 116 | + assertTrue(FileTypeUtils.isScriptable("image/svg+xml")); | |
| 117 | + assertTrue(FileTypeUtils.isScriptable("text/html")); | |
| 118 | + assertTrue(FileTypeUtils.isScriptable("application/xhtml+xml")); | |
| 119 | + assertTrue(FileTypeUtils.isScriptable("application/xml")); | |
| 120 | + assertTrue(FileTypeUtils.isScriptable("text/xml")); | |
| 121 | + assertFalse(FileTypeUtils.isScriptable("image/png")); | |
| 122 | + assertFalse(FileTypeUtils.isScriptable("application/pdf")); | |
| 123 | + assertFalse(FileTypeUtils.isScriptable("application/vnd.openxmlformats-officedocument.wordprocessingml.document")); | |
| 124 | + assertFalse(FileTypeUtils.isScriptable(null)); | |
| 125 | + } | |
| 126 | + | |
| 32 | 127 | } | ... | ... |
yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImplTest.java
| ... | ... | @@ -252,6 +252,28 @@ public class FileServiceImplTest extends BaseDbUnitTest { |
| 252 | 252 | } |
| 253 | 253 | |
| 254 | 254 | @Test |
| 255 | + public void testCreateFile_success_fakeExtensionDetectContent() throws Exception { | |
| 256 | + for (String name : new String[]{"图标.xml", "图标.txt"}) { | |
| 257 | + // 准备参数:SVG 内容伪装成其他扩展名 | |
| 258 | + byte[] content = "<svg xmlns=\"http://www.w3.org/2000/svg\"><script>alert(1)</script></svg>" | |
| 259 | + .getBytes(StandardCharsets.UTF_8); | |
| 260 | + // mock 方法 | |
| 261 | + FileClient client = mock(FileClient.class); | |
| 262 | + when(fileConfigService.getMasterFileClient()).thenReturn(client); | |
| 263 | + String url = randomString(); | |
| 264 | + when(client.upload(same(content), anyString(), eq("image/svg+xml"))).thenReturn(url); | |
| 265 | + | |
| 266 | + // 调用 | |
| 267 | + String result = fileService.createFile(content, name, null, "text/plain"); | |
| 268 | + // 断言:以内容识别的类型上传,文件存储器据此按附件下载 | |
| 269 | + assertEquals(url, result); | |
| 270 | + FileDO file = fileMapper.selectOne(FileDO::getUrl, url); | |
| 271 | + assertEquals(name, file.getName()); | |
| 272 | + assertEquals("image/svg+xml", file.getType()); | |
| 273 | + } | |
| 274 | + } | |
| 275 | + | |
| 276 | + @Test | |
| 255 | 277 | public void testCreateFile_success_opus() throws Exception { |
| 256 | 278 | // 准备参数 |
| 257 | 279 | byte[] content = new byte[64]; | ... | ... |
yudao-module-oa/src/test/java/cn/iocoder/yudao/module/oa/service/reimbursement/OaReimbursementServiceImplTest.java
| 1 | 1 | package cn.iocoder.yudao.module.oa.service.reimbursement; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | +import cn.hutool.core.date.LocalDateTimeUtil; | |
| 4 | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | 6 | import cn.iocoder.yudao.framework.jackson.config.YudaoJacksonAutoConfiguration; |
| 6 | 7 | import cn.iocoder.yudao.framework.security.core.LoginUser; |
| ... | ... | @@ -14,6 +15,8 @@ import cn.iocoder.yudao.module.oa.controller.admin.reimbursement.vo.OaReimbursem |
| 14 | 15 | import cn.iocoder.yudao.module.oa.dal.dataobject.reimbursement.OaReimbursementDO; |
| 15 | 16 | import cn.iocoder.yudao.module.oa.dal.mysql.reimbursement.OaReimbursementMapper; |
| 16 | 17 | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; |
| 18 | +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | |
| 19 | +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; | |
| 17 | 20 | import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; |
| 18 | 21 | import com.fasterxml.jackson.databind.ObjectMapper; |
| 19 | 22 | import jakarta.annotation.Resource; |
| ... | ... | @@ -30,6 +33,7 @@ import java.math.BigDecimal; |
| 30 | 33 | import java.time.LocalDateTime; |
| 31 | 34 | import java.util.Arrays; |
| 32 | 35 | import java.util.Collections; |
| 36 | +import java.util.List; | |
| 33 | 37 | |
| 34 | 38 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; |
| 35 | 39 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; |
| ... | ... | @@ -61,8 +65,6 @@ public class OaReimbursementServiceImplTest extends BaseDbUnitTest { |
| 61 | 65 | |
| 62 | 66 | @BeforeEach |
| 63 | 67 | public void before() { |
| 64 | - // JSON 处理器为静态共享配置,每个用例恢复当前上下文的生产日期序列化配置 | |
| 65 | - JacksonTypeHandler.setObjectMapper(objectMapper); | |
| 66 | 68 | SecurityFrameworkUtils.setLoginUser(new LoginUser().setId(10L).setUserType(2), new MockHttpServletRequest()); |
| 67 | 69 | } |
| 68 | 70 | |
| ... | ... | @@ -126,6 +128,7 @@ public class OaReimbursementServiceImplTest extends BaseDbUnitTest { |
| 126 | 128 | assertEquals(new BigDecimal("12.34"), application.getTotalPrice()); |
| 127 | 129 | assertEquals(2, application.getInvoiceCount()); |
| 128 | 130 | assertEquals(1, CollUtil.getFirst(application.getItems()).getExpenseType()); |
| 131 | + assertEquals(LocalDateTime.of(2026, 9, 13, 8, 0), CollUtil.getFirst(application.getItems()).getExpenseTime()); | |
| 129 | 132 | verify(processInstanceApi).createProcessInstance(eq(10L), argThat(dto -> |
| 130 | 133 | "oa_reimbursement".equals(dto.getProcessDefinitionKey()) && id.toString().equals(dto.getBusinessKey()) |
| 131 | 134 | && !dto.getVariables().containsKey("title"))); |
| ... | ... | @@ -214,6 +217,42 @@ public class OaReimbursementServiceImplTest extends BaseDbUnitTest { |
| 214 | 217 | } |
| 215 | 218 | |
| 216 | 219 | @Test |
| 220 | + public void testGetReimbursementAndPage_itemsExpenseTime() { | |
| 221 | + // mock 数据 | |
| 222 | + Long id = reimbursementService.createReimbursement(randomReimbursementSaveReqVO()); | |
| 223 | + LocalDateTime expenseTime = LocalDateTime.of(2026, 9, 13, 8, 0); | |
| 224 | + | |
| 225 | + // 调用 | |
| 226 | + OaReimbursementDO reimbursement = reimbursementService.getReimbursement(id); | |
| 227 | + PageResult<OaReimbursementDO> page = reimbursementService.getReimbursementPage(10L, new OaReimbursementPageReqVO()); | |
| 228 | + | |
| 229 | + // 断言:使用 Spring 主 ObjectMapper,详情、列表读取明细时间,库中保持毫秒时间戳 | |
| 230 | + assertSame(objectMapper, JacksonTypeHandler.getObjectMapper()); | |
| 231 | + assertEquals(expenseTime, CollUtil.getFirst(reimbursement.getItems()).getExpenseTime()); | |
| 232 | + assertEquals(1L, page.getTotal()); | |
| 233 | + assertEquals(expenseTime, CollUtil.getFirst(CollUtil.getFirst(page.getList()).getItems()).getExpenseTime()); | |
| 234 | + List<String> items = reimbursementMapper.selectObjs(new LambdaQueryWrapper<OaReimbursementDO>() | |
| 235 | + .select(OaReimbursementDO::getItems).eq(OaReimbursementDO::getId, id)); | |
| 236 | + assertTrue(CollUtil.getFirst(items).contains("\"expenseTime\":" + LocalDateTimeUtil.toEpochMilli(expenseTime))); | |
| 237 | + } | |
| 238 | + | |
| 239 | + @Test | |
| 240 | + public void testGetReimbursementPage_itemsExpenseTimeString() { | |
| 241 | + // mock 数据:库中明细时间为可读字符串 | |
| 242 | + Long id = reimbursementService.createReimbursement(randomReimbursementSaveReqVO()); | |
| 243 | + reimbursementMapper.update(new LambdaUpdateWrapper<OaReimbursementDO>().eq(OaReimbursementDO::getId, id) | |
| 244 | + .setSql("items = {0}", "[{\"expenseTime\":\"2026-09-13 08:00:00\",\"expenseType\":1,\"price\":12.34}]")); | |
| 245 | + | |
| 246 | + // 调用 | |
| 247 | + PageResult<OaReimbursementDO> page = reimbursementService.getReimbursementPage(10L, new OaReimbursementPageReqVO()); | |
| 248 | + | |
| 249 | + // 断言 | |
| 250 | + assertEquals(1L, page.getTotal()); | |
| 251 | + assertEquals(LocalDateTime.of(2026, 9, 13, 8, 0), | |
| 252 | + CollUtil.getFirst(CollUtil.getFirst(page.getList()).getItems()).getExpenseTime()); | |
| 253 | + } | |
| 254 | + | |
| 255 | + @Test | |
| 217 | 256 | public void testUpdateReimbursementStatus_successAndRepeated() { |
| 218 | 257 | // mock 数据 |
| 219 | 258 | OaReimbursementDO application = randomReimbursementDO(); | ... | ... |
yudao-module-oa/src/test/java/cn/iocoder/yudao/module/oa/service/travel/OaTravelReimbursementServiceImplTest.java
| ... | ... | @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.oa.service.travel; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | |
| 5 | 6 | import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils; |
| 6 | 7 | import cn.iocoder.yudao.framework.security.core.LoginUser; |
| 7 | 8 | import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; |
| ... | ... | @@ -16,6 +17,7 @@ import cn.iocoder.yudao.module.oa.dal.redis.no.OaNoRedisDAO; |
| 16 | 17 | import cn.iocoder.yudao.module.system.api.dict.DictDataApi; |
| 17 | 18 | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; |
| 18 | 19 | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; |
| 20 | +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; | |
| 19 | 21 | import jakarta.annotation.Resource; |
| 20 | 22 | import jakarta.validation.ConstraintViolationException; |
| 21 | 23 | import org.junit.jupiter.api.AfterEach; |
| ... | ... | @@ -150,6 +152,7 @@ public class OaTravelReimbursementServiceImplTest extends BaseDbUnitTest { |
| 150 | 152 | OaTravelReimbursementDO record = travelReimbursementMapper.selectById(id); |
| 151 | 153 | assertEquals(1, record.getDays()); |
| 152 | 154 | assertEquals(reqVO.getItems().size(), record.getItems().size()); |
| 155 | + assertEquals(LocalDateTime.of(2026, 9, 14, 10, 0), CollUtil.getFirst(record.getItems()).getExpenseTime()); | |
| 153 | 156 | assertEquals(reqVO.getFileUrls(), record.getFileUrls()); |
| 154 | 157 | assertEquals(new BigDecimal("0.00"), record.getTotalPrice()); |
| 155 | 158 | assertEquals("TRAVEL-NEW", record.getNo()); |
| ... | ... | @@ -423,6 +426,25 @@ public class OaTravelReimbursementServiceImplTest extends BaseDbUnitTest { |
| 423 | 426 | } |
| 424 | 427 | |
| 425 | 428 | @Test |
| 429 | + public void testGetTravelReimbursementAndPage_itemsExpenseTime() { | |
| 430 | + // mock 数据 | |
| 431 | + OaTravelReimbursementDO record = randomTravelReimbursementDO(); | |
| 432 | + travelReimbursementMapper.insert(record); | |
| 433 | + LocalDateTime expenseTime = LocalDateTime.of(2026, 9, 14, 10, 0); | |
| 434 | + | |
| 435 | + // 调用 | |
| 436 | + OaTravelReimbursementDO detail = travelReimbursementService.getTravelReimbursement(record.getId()); | |
| 437 | + PageResult<OaTravelReimbursementDO> page = travelReimbursementService.getTravelReimbursementPage(10L, | |
| 438 | + new OaTravelReimbursementPageReqVO()); | |
| 439 | + | |
| 440 | + // 断言:未声明 ObjectMapper Bean 时使用 JsonUtils 的 ObjectMapper,详情、列表读取明细时间 | |
| 441 | + assertSame(JsonUtils.getObjectMapper(), JacksonTypeHandler.getObjectMapper()); | |
| 442 | + assertEquals(expenseTime, CollUtil.getFirst(detail.getItems()).getExpenseTime()); | |
| 443 | + assertEquals(1L, page.getTotal()); | |
| 444 | + assertEquals(expenseTime, CollUtil.getFirst(CollUtil.getFirst(page.getList()).getItems()).getExpenseTime()); | |
| 445 | + } | |
| 446 | + | |
| 447 | + @Test | |
| 426 | 448 | public void testUpdateTravelReimbursementStatus_notExists() { |
| 427 | 449 | |
| 428 | 450 | // 调用,并断言异常 |
| ... | ... | @@ -593,7 +615,8 @@ public class OaTravelReimbursementServiceImplTest extends BaseDbUnitTest { |
| 593 | 615 | .setStartTime(LocalDateTime.of(2026, 9, 14, 0, 0)).setEndTime(LocalDateTime.of(2026, 9, 15, 0, 0)) |
| 594 | 616 | .setStatus(-1).setProcessInstanceId(null).setPayStatus(false) |
| 595 | 617 | .setTravelApplyId(null).setTotalPrice(BigDecimal.ZERO) |
| 596 | - .setItems(Collections.singletonList(new OaTravelReimbursementDO.Item().setPrice(BigDecimal.ZERO))) | |
| 618 | + .setItems(Collections.singletonList(new OaTravelReimbursementDO.Item().setPrice(BigDecimal.ZERO) | |
| 619 | + .setExpenseTime(LocalDateTime.of(2026, 9, 14, 10, 0)))) | |
| 597 | 620 | .setFileUrls(Collections.singletonList("https://example.com/receipt.pdf"))); |
| 598 | 621 | record.setCreator("10"); |
| 599 | 622 | return record; | ... | ... |