!112 feature #I5ROOR 增加关系型数据库EL存储插件

Merge pull request !112 from 与或非/issues/I5ROOR
This commit is contained in:
铂赛东 2022-09-18 14:43:58 +00:00 committed by Gitee
commit 59f694bb46
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
18 changed files with 641 additions and 0 deletions

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>liteflow</artifactId>
<groupId>com.yomahub</groupId>
<version>${revision}</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>liteflow-rule-sql</artifactId>
<dependencies>
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-core</artifactId>
<version>${revision}</version>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,19 @@
package com.yomahub.liteflow.parser.spi.sql;
import com.yomahub.liteflow.parser.spi.ParserClassNameSpi;
import com.yomahub.liteflow.parser.sql.SQLXmlELParser;
/**
* SQL 解析器 SPI 实现
*
* @author tangkc
* @since 2.9.0
*/
public class SQLParserClassNameSpi implements ParserClassNameSpi {
@Override
public String getSpiClassName() {
return SQLXmlELParser.class.getName();
}
}

View File

@ -0,0 +1,84 @@
package com.yomahub.liteflow.parser.sql;
import cn.hutool.core.text.StrFormatter;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.parser.el.ClassXmlFlowELParser;
import com.yomahub.liteflow.parser.sql.exception.ELSQLException;
import com.yomahub.liteflow.parser.sql.util.JDBCHelper;
import com.yomahub.liteflow.parser.sql.vo.SQLParserVO;
import com.yomahub.liteflow.property.LiteflowConfig;
import com.yomahub.liteflow.property.LiteflowConfigGetter;
import com.yomahub.liteflow.util.JsonUtil;
import java.util.Objects;
/**
* SQL 解析器实现只支持 EL 形式的 XML不支持其他的形式
*
* @author tangkc
* @since 2.9.0
*/
public class SQLXmlELParser extends ClassXmlFlowELParser {
private static final String ERROR_MSG_PATTERN = "rule-source-ext-data {} is blank";
private static final String ERROR_COMMON_MSG = "rule-source-ext-data is empty";
/**
* 构造函数
*/
public SQLXmlELParser() {
LiteflowConfig liteflowConfig = LiteflowConfigGetter.get();
if (StrUtil.isBlank(liteflowConfig.getRuleSourceExtData())) {
throw new ELSQLException(ERROR_COMMON_MSG);
}
try {
SQLParserVO sqlParserVO = JsonUtil.parseObject(liteflowConfig.getRuleSourceExtData(), SQLParserVO.class);
if (Objects.isNull(sqlParserVO)) {
throw new ELSQLException(ERROR_COMMON_MSG);
}
// 检查配置文件
checkParserVO(sqlParserVO);
// 初始化 JDBCHelper
JDBCHelper.init(sqlParserVO);
} catch (ELSQLException elsqlException) {
throw elsqlException;
} catch (Exception ex) {
throw new ELSQLException(ex.getMessage());
}
}
@Override
public String parseCustom() {
return JDBCHelper.getInstance().getElDataContent();
}
/**
* 检查配置文件并设置默认值
*
* @param sqlParserVO sqlParserVO
*/
private void checkParserVO(SQLParserVO sqlParserVO) {
if (StrUtil.isEmpty(sqlParserVO.getUrl())) {
throw new ELSQLException(StrFormatter.format(ERROR_MSG_PATTERN, "url"));
}
if (StrUtil.isEmpty(sqlParserVO.getDriverClassName())) {
throw new ELSQLException(StrFormatter.format(ERROR_MSG_PATTERN, "driverClassName"));
}
if (Objects.isNull(sqlParserVO.getUsername())) {
throw new ELSQLException(StrFormatter.format(ERROR_MSG_PATTERN, "username"));
}
if (Objects.isNull(sqlParserVO.getPassword())) {
throw new ELSQLException(StrFormatter.format(ERROR_MSG_PATTERN, "password"));
}
if (Objects.isNull(sqlParserVO.getElTable())) {
sqlParserVO.setElTable(new SQLParserVO.ElTable());
}
}
}

View File

@ -0,0 +1,30 @@
package com.yomahub.liteflow.parser.sql.exception;
/**
* SQL 相关业务异常
*
* @author tangkc
* @since 2.9.0
*/
public class ELSQLException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* 异常信息
*/
private String message;
public ELSQLException(String message) {
this.message = message;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@ -0,0 +1,157 @@
package com.yomahub.liteflow.parser.sql.util;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.StrFormatter;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.parser.sql.exception.ELSQLException;
import com.yomahub.liteflow.parser.sql.vo.SQLParserVO;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* jdbc 工具类
*
* @author tangkc
* @since 2.9.0
*/
public class JDBCHelper {
private static final String SQL_PATTERN = "SELECT {},{} FROM {} ";
private static final String CHAIN_XML_PATTERN = "<chain name=\"{}\">{}</chain>";
private static final String XML_PATTERN = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><flow>{}</flow>";
private static final Integer FETCH_SIZE_MAX = 1000;
private SQLParserVO sqlParserVO;
private static JDBCHelper INSTANCE;
/**
* 初始化 INSTANCE
*/
public static void init(SQLParserVO sqlParserVO) {
try {
INSTANCE = new JDBCHelper();
Class.forName(sqlParserVO.getDriverClassName());
INSTANCE.setSqlParserVO(sqlParserVO);
} catch (ClassNotFoundException e) {
throw new ELSQLException(e.getMessage());
}
}
/**
* 获取 INSTANCE
*/
public static JDBCHelper getInstance() {
return INSTANCE;
}
/**
* 获取链接
*/
public Connection getConn() {
Connection connection = null;
try {
connection = DriverManager.getConnection(sqlParserVO.getUrl(), sqlParserVO.getUsername(), sqlParserVO.getPassword());
} catch (SQLException e) {
throw new ELSQLException(e.getMessage());
}
return connection;
}
/**
* 获取 ElData 数据内容
*/
public String getElDataContent() {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
String elDataField = sqlParserVO.getElTable().getElDataField();
String chainNameField = sqlParserVO.getElTable().getChainNameField();
String tableName = sqlParserVO.getElTable().getTableName();
String sqlCmd = StrFormatter.format(SQL_PATTERN, chainNameField, elDataField, tableName);
List<String> result = new ArrayList<>();
try {
conn = getConn();
stmt = conn.prepareStatement(sqlCmd, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
// 设置游标拉取数量
stmt.setFetchSize(FETCH_SIZE_MAX);
rs = stmt.executeQuery();
while (rs.next()) {
String elData = rs.getString(elDataField);
if (StrUtil.isBlank(elData)) {
throw new ELSQLException(StrFormatter.format("{} table exist {} field value is empty", tableName, elDataField));
}
String chainName = rs.getString(chainNameField);
if (StrUtil.isBlank(elData)) {
throw new ELSQLException(StrFormatter.format("{} table exist {} field value is empty", tableName, elDataField));
}
result.add(StrFormatter.format(CHAIN_XML_PATTERN, chainName, elData));
}
} catch (Exception e) {
throw new ELSQLException(e.getMessage());
} finally {
// 关闭连接
close(conn, stmt, rs);
}
String chains = CollUtil.join(result, StrUtil.CRLF);
return StrFormatter.format(XML_PATTERN, chains);
}
/**
* 关闭连接
*
* @param conn conn
* @param stmt stmt
* @param rs rs
*/
private void close(Connection conn, PreparedStatement stmt, ResultSet rs) {
// 关闭连接
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
throw new ELSQLException(e.getMessage());
}
}
// 关闭 statement
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
throw new ELSQLException(e.getMessage());
}
}
//关闭结果集
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
throw new ELSQLException(e.getMessage());
}
}
}
//#region get set method
private SQLParserVO getSqlParserVO() {
return sqlParserVO;
}
private JDBCHelper setSqlParserVO(SQLParserVO sqlParserVO) {
this.sqlParserVO = sqlParserVO;
return this;
}
//#endregion
}

View File

@ -0,0 +1,116 @@
package com.yomahub.liteflow.parser.sql.vo;
/**
* 用于解析 RuleSourceExtData VO 用于 sql 模式中
*
* @author tangkc
* @since 2.9.0
*/
public class SQLParserVO {
/**
* 连接地址
*/
private String url;
/**
* 驱动
*/
private String driverClassName;
/**
* 账号名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* EL 表相关配置
*/
private ElTable elTable;
public static class ElTable {
/**
* 表名
*/
private String tableName = "el_table";
/**
* chainName
*/
private String chainNameField = "chain_name";
/**
* el 表达式相关数据
*/
private String elDataField = "el_data";
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public String getChainNameField() {
return chainNameField;
}
public void setChainNameField(String chainNameField) {
this.chainNameField = chainNameField;
}
public String getElDataField() {
return elDataField;
}
public void setElDataField(String elDataField) {
this.elDataField = elDataField;
}
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getDriverClassName() {
return driverClassName;
}
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public ElTable getElTable() {
return elTable;
}
public void setElTable(ElTable elTable) {
this.elTable = elTable;
}
}

View File

@ -0,0 +1 @@
com.yomahub.liteflow.parser.spi.sql.SQLParserClassNameSpi

View File

@ -12,6 +12,7 @@
<packaging>pom</packaging>
<modules>
<module>liteflow-rule-zk</module>
<module>liteflow-rule-sql</module>
</modules>
<artifactId>liteflow-rule-plugin</artifactId>

View File

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>liteflow-testcase-el</artifactId>
<groupId>com.yomahub</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>liteflow-testcase-el-sql-springboot</artifactId>
<properties>
<h2.version>2.1.214</h2.version>
</properties>
<dependencies>
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-spring-boot-starter</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-rule-sql</artifactId>
<version>${revision}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.0.5.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>${h2.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${springboot.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,23 @@
package com.yomahub.liteflow.test;
import com.yomahub.liteflow.flow.FlowBus;
import com.yomahub.liteflow.property.LiteflowConfigGetter;
import com.yomahub.liteflow.spi.holder.SpiFactoryCleaner;
import com.yomahub.liteflow.spring.ComponentScanner;
import com.yomahub.liteflow.thread.ExecutorHelper;
import org.junit.AfterClass;
/**
* @author tangkc
* @since 2.8.6
*/
public class BaseTest {
@AfterClass
public static void cleanScanCache(){
ComponentScanner.cleanCache();
FlowBus.cleanCache();
ExecutorHelper.loadInstance().clearExecutorServiceMap();
SpiFactoryCleaner.clean();
LiteflowConfigGetter.clean();
}
}

View File

@ -0,0 +1,35 @@
package com.yomahub.liteflow.test.sql;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.test.BaseTest;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
/**
* @author tangkc
* @since 2.9.0
*/
@RunWith(SpringRunner.class)
@TestPropertySource(value = "classpath:/application-xml.properties")
@SpringBootTest(classes = SQLWithXmlELSpringbootTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.sql.cmp"})
public class SQLWithXmlELSpringbootTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
@Test
public void testSQLWithXml() {
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
Assert.assertEquals("a==>b==>c", response.getExecuteStepStr());
}
}

View File

@ -0,0 +1,20 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.sql.cmp;
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
@Component("a")
public class ACmp extends NodeComponent {
@Override
public void process() {
System.out.println("ACmp executed!");
}
}

View File

@ -0,0 +1,21 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.sql.cmp;
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
@Component("b")
public class BCmp extends NodeComponent {
@Override
public void process() {
System.out.println("BCmp executed!");
}
}

View File

@ -0,0 +1,21 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.sql.cmp;
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
@Component("c")
public class CCmp extends NodeComponent {
@Override
public void process() {
System.out.println("CCmp executed!");
}
}

View File

@ -0,0 +1,9 @@
liteflow.rule-source-ext-data={"url":"jdbc:h2:mem:test_db;MODE=MySQL","driverClassName":"org.h2.Driver","username":"root","password":"123456","elTable":{"tableName":"EL_TABLE","elDataField":"EL_DATA"}}
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:test_db;MODE=MySQL
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.schema=classpath:/sql/schema.sql
spring.datasource.data=classpath:/sql/data.sql
spring.datasource.platform=h2

View File

@ -0,0 +1,4 @@
DELETE FROM EL_TABLE;
INSERT INTO EL_TABLE (CHAIN_NAME,EL_DATA) values ('chain1','THEN(a, b, c);');
INSERT INTO EL_TABLE (CHAIN_NAME,EL_DATA) values ('chain2','THEN(a, b, c);');

View File

@ -0,0 +1,6 @@
create table `EL_TABLE` (
`id` bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
`chain_name` varchar(32) NOT NULL,
`el_data` varchar(1024) NOT NULL,
PRIMARY KEY (`id`)
) ;

View File

@ -23,5 +23,6 @@
<module>liteflow-testcase-el-script-groovy-springboot</module>
<module>liteflow-testcase-el-script-qlexpress-springboot</module>
<module>liteflow-testcase-el-zk-springboot</module>
<module>liteflow-testcase-el-sql-springboot</module>
</modules>
</project>