From f6a1343a60852f66a006dfa280b071416fc90aad Mon Sep 17 00:00:00 2001 From: nipeixuan <34534268+nipeixuan@users.noreply.github.com> Date: Wed, 12 Jan 2022 15:14:36 +0800 Subject: [PATCH 01/11] =?UTF-8?q?=E5=8A=A8=E6=80=81=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=BA=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- framework/modules/dbutils-starter/pom.xml | 128 +++++ .../dbutils/DbutilsAutoConfiguration.java | 23 + .../idealworld/dew/core/dbutils/DewDB.java | 447 ++++++++++++++++++ .../dew/core/dbutils/DewDBUtils.java | 83 ++++ .../dew/core/dbutils/dialect/Dialect.java | 40 ++ .../core/dbutils/dialect/DialectFactory.java | 35 ++ .../dew/core/dbutils/dialect/DialectType.java | 23 + .../dew/core/dbutils/dialect/H2Dialect.java | 116 +++++ .../dew/core/dbutils/dialect/HiveDialect.java | 104 ++++ .../core/dbutils/dialect/MySQLDialect.java | 116 +++++ .../core/dbutils/dialect/PostgresDialect.java | 128 +++++ .../dew/core/dbutils/dto/DBUtilsConfig.java | 46 ++ .../dew/core/dbutils/dto/DSConfig.java | 59 +++ .../idealworld/dew/core/dbutils/dto/Meta.java | 33 ++ .../idealworld/dew/core/dbutils/dto/Page.java | 36 ++ .../dew/core/dbutils/process/DBExecutor.java | 386 +++++++++++++++ .../dew/core/dbutils/process/DSLoader.java | 169 +++++++ .../dew/core/dbutils/utils/YamlHelper.java | 92 ++++ .../main/resources/META-INF/spring.factories | 2 + .../src/main/resources/application.yml | 19 + .../src/main/resources/config-dynamic.yml | 22 + .../src/main/resources/config.yml | 17 + .../dew/core/dbutils/DbutilsTest.java | 263 +++++++++++ .../idealworld/dew/core/dbutils/User.java | 38 ++ framework/modules/hi-starter/pom.xml | 52 ++ .../java/group/idealworld/dew/core/hi/Hi.java | 30 ++ .../dew/core/hi/api/cluster/HiClusterAPI.java | 28 ++ .../hi/exception/RTAPINotFoundException.java | 55 +++ .../dew/core/hi/exception/RTException.java | 55 +++ .../exception/RTGeneralSecurityException.java | 55 +++ .../dew/core/hi/exception/RTIOException.java | 55 +++ .../RTReflectiveOperationException.java | 56 +++ .../core/hi/exception/RTScriptException.java | 55 +++ .../RTUnsupportedEncodingException.java | 55 +++ .../dew/core/hi/ext/HiAutoConfiguration.java | 32 ++ .../idealworld/dew/core/hi/ext/HiConfig.java | 34 ++ .../dew/core/hi/ext/SpringBeanDetector.java | 35 ++ .../dew/core/hi/helper/ScanHelper.java | 113 +++++ .../idealworld/dew/core/hi/process/Hi.java | 17 + .../dew/core/hi/process/HiBuilder.java | 311 ++++++++++++ .../idealworld/dew/core/hi/process/HiRef.java | 24 + .../src/main/resources/broker.conf | 8 + 42 files changed, 3495 insertions(+) create mode 100644 framework/modules/dbutils-starter/pom.xml create mode 100644 framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/DbutilsAutoConfiguration.java create mode 100644 framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/DewDB.java create mode 100644 framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/DewDBUtils.java create mode 100644 framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dialect/Dialect.java create mode 100644 framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dialect/DialectFactory.java create mode 100644 framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dialect/DialectType.java create mode 100644 framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dialect/H2Dialect.java create mode 100644 framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dialect/HiveDialect.java create mode 100644 framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dialect/MySQLDialect.java create mode 100644 framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dialect/PostgresDialect.java create mode 100644 framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dto/DBUtilsConfig.java create mode 100644 framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dto/DSConfig.java create mode 100644 framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dto/Meta.java create mode 100644 framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dto/Page.java create mode 100644 framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/process/DBExecutor.java create mode 100644 framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/process/DSLoader.java create mode 100644 framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/utils/YamlHelper.java create mode 100644 framework/modules/dbutils-starter/src/main/resources/META-INF/spring.factories create mode 100644 framework/modules/dbutils-starter/src/main/resources/application.yml create mode 100644 framework/modules/dbutils-starter/src/main/resources/config-dynamic.yml create mode 100644 framework/modules/dbutils-starter/src/main/resources/config.yml create mode 100644 framework/modules/dbutils-starter/src/test/java/group/idealworld/dew/core/dbutils/DbutilsTest.java create mode 100644 framework/modules/dbutils-starter/src/test/java/group/idealworld/dew/core/dbutils/User.java create mode 100644 framework/modules/hi-starter/pom.xml create mode 100644 framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/Hi.java create mode 100644 framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/api/cluster/HiClusterAPI.java create mode 100644 framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/exception/RTAPINotFoundException.java create mode 100644 framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/exception/RTException.java create mode 100644 framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/exception/RTGeneralSecurityException.java create mode 100644 framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/exception/RTIOException.java create mode 100644 framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/exception/RTReflectiveOperationException.java create mode 100644 framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/exception/RTScriptException.java create mode 100644 framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/exception/RTUnsupportedEncodingException.java create mode 100644 framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/ext/HiAutoConfiguration.java create mode 100644 framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/ext/HiConfig.java create mode 100644 framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/ext/SpringBeanDetector.java create mode 100644 framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/helper/ScanHelper.java create mode 100644 framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/process/Hi.java create mode 100644 framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/process/HiBuilder.java create mode 100644 framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/process/HiRef.java create mode 100644 framework/modules/test-starter/src/main/resources/broker.conf diff --git a/framework/modules/dbutils-starter/pom.xml b/framework/modules/dbutils-starter/pom.xml new file mode 100644 index 00000000..77383e72 --- /dev/null +++ b/framework/modules/dbutils-starter/pom.xml @@ -0,0 +1,128 @@ + + + + + 4.0.0 + + group.idealworld.dew + parent-starter + 3.0.0-Beta3 + ../parent-starter + + + dbutils-starter + 1.1.4 Dew dbutils- + Dew 集群 动态数据源实现 + jar + + + 11 + 11 + UTF-8 + ${project.build.sourceEncoding} + ${project.build.sourceEncoding} + ${java.version} + ${java.version} + ${java.version} + 1.7 + 1.1.23 + 8.0.21 + 42.2.14 + 1.4.200 + 1.26 + 1.18.12 + 4.13 + 1.7.30 + + + + + group.idealworld.dew + cluster-common + + + group.idealworld.dew + cluster-common-test + + + org.springframework.boot + spring-boot-autoconfigure + + + group.idealworld.dew + test-starter + + + org.springframework.boot + spring-boot-configuration-processor + + + commons-dbutils + commons-dbutils + ${commons-dbutils.version} + + + com.alibaba + druid + ${druid.version} + + + org.yaml + snakeyaml + ${snakeyaml.version} + + + org.projectlombok + lombok + ${lombok.version} + provided + + + com.h2database + h2 + ${h2.version} + true + + + mysql + mysql-connector-java + ${mysql.version} + true + + + org.postgresql + postgresql + ${postgresql.version} + true + + + org.slf4j + slf4j-api + ${slf4j.version} + provided + + + junit + junit + ${junit.version} + test + + + + diff --git a/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/DbutilsAutoConfiguration.java b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/DbutilsAutoConfiguration.java new file mode 100644 index 00000000..13284fde --- /dev/null +++ b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/DbutilsAutoConfiguration.java @@ -0,0 +1,23 @@ +package group.idealworld.dew.core.dbutils; + + +import group.idealworld.dew.core.dbutils.dto.DBUtilsConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableConfigurationProperties(DBUtilsConfig.class) +public class DbutilsAutoConfiguration { + + @Bean + public DewDB dewDB(DBUtilsConfig DBUtilsConfig) { + DewDBUtils.init2(DBUtilsConfig); + DewDB dewDB = DewDBUtils.use("default"); + return dewDB; + } + +} diff --git a/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/DewDB.java b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/DewDB.java new file mode 100644 index 00000000..24bf9026 --- /dev/null +++ b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/DewDB.java @@ -0,0 +1,447 @@ +/* + * Copyright 2020. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package group.idealworld.dew.core.dbutils; + +import group.idealworld.dew.core.dbutils.dto.Meta; +import group.idealworld.dew.core.dbutils.dto.Page; +import group.idealworld.dew.core.dbutils.process.DBExecutor; +import group.idealworld.dew.core.dbutils.process.DSLoader; +import lombok.SneakyThrows; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 数据操作类. + * + * @author gudaoxuri + */ +public class DewDB { + + private static final Logger logger = LoggerFactory.getLogger(DewDB.class); + + private static final Map DBS = new HashMap<>(); + private static final ThreadLocal threadLocalConnection = new ThreadLocal<>(); + + private final DSLoader.DSInfo dsInfo; + + public static DewDB pick(String dsCode) { + if (!DBS.containsKey(dsCode)) { + synchronized (DBS) { + if (!DBS.containsKey(dsCode)) { + DBS.put(dsCode, new DewDB(DSLoader.getDSInfo(dsCode))); + } + } + } + return DBS.get(dsCode); + } + + DewDB(DSLoader.DSInfo dsInfo) { + this.dsInfo = dsInfo; + } + + public DSLoader.DSInfo getDsInfo() { + return dsInfo; + } + + /** + * 创建表. + *

+ * + * @param tableName 表名 + * @param tableDesc 表说明 + * @param fields 表字段(字段名 - 类型) + * @param fieldsDesc 字段说明 + * @param indexFields 索引字段 + * @param uniqueFields 唯一值字段 + * @param pkField 主键字段 + * @throws SQLException SQL错误 + * @deprecated - 此功能存在一定限制,建议使用 {@link #ddl(String)} 建表 + */ + @Deprecated + public void createTableIfNotExist(String tableName, String tableDesc, + Map fields, + Map fieldsDesc, + List indexFields, + List uniqueFields, + String pkField) throws SQLException { + tableName = tableName.toLowerCase(); + DBExecutor.ddl( + dsInfo.getDialect().createTableIfNotExist(tableName, tableDesc, + fields, fieldsDesc, indexFields, uniqueFields, pkField), + getConnection(), isCloseConnection() + ); + } + + /** + * DDL操作. + * + * @param ddl DDL语句 + * @throws SQLException SQL错误 + */ + public void ddl(String ddl) throws SQLException { + DBExecutor.ddl(ddl, getConnection(), isCloseConnection()); + } + + /** + * 获取单条记录. + * + * @param tableName 表名 + * @param pkField 主键字段 + * @param pkValue 主键值 + * @param clazz 对象类 + * @param 对象 + * @return java对象 + * @throws SQLException SQL错误 + */ + public E getByPk(String tableName, String pkField, Object pkValue, Class clazz) throws SQLException { + return get("SELECT * FROM " + tableName + " WHERE " + pkField + " = ?", clazz, pkValue); + } + + /** + * 获取单个对象. + * + * @param sql SQL + * @param params 参数 + * @param clazz 对象类 + * @param 对象 + * @return java对象 + * @throws SQLException SQL错误 + */ + public E get(String sql, Class clazz, Object... params) throws SQLException { + return DBExecutor.get(sql, params, clazz, getConnection(), isCloseConnection()); + } + + /** + * 获取多个对象. + * + * @param sql SQL + * @param params 参数 + * @param clazz 对象类 + * @param 对象 + * @return java对象 + * @throws SQLException SQL错误 + */ + public List find(String sql, Class clazz, Object... params) throws SQLException { + return DBExecutor.find(sql, params, clazz, getConnection(), isCloseConnection()); + } + + + /** + * 获取多个对象(带分页). + * + * @param sql SQL + * @param params 参数 + * @param pageNumber 页码(从1开始) + * @param pageSize 每页条数 + * @param clazz 对象类 + * @param 对象 + * @return 多个对象(带分页) + * @throws SQLException SQL错误 + */ + public Page page(String sql, long pageNumber, long pageSize, Class clazz, Object... params) throws SQLException { + return DBExecutor.page(sql, params, pageNumber, pageSize, clazz, getConnection(), isCloseConnection(), dsInfo.getDialect()); + } + + /** + * 判断记录是否存在. + * + * @param tableName 表名 + * @param pkField 主键字段 + * @param pkValue 主键值 + * @return 是否存在 + * @throws SQLException SQL错误 + */ + public boolean exits(String tableName, String pkField, Object pkValue) throws SQLException { + return get("SELECT id FROM " + tableName + " WHERE " + pkField + " = ?", new Object[]{pkValue}).size() != 0; + } + + /** + * 判断记录是否存在. + * + * @param sql SQL + * @param params 参数 + * @return 是否存在 + * @throws SQLException SQL错误 + */ + public boolean exits(String sql, Object... params) throws SQLException { + return count(sql, params) != 0; + } + + /** + * 获取单条记录. + * + * @param tableName 表名 + * @param pkField 主键字段 + * @param pkValue 主键值 + * @return 单条记录 + * @throws SQLException SQL错误 + */ + public Map getByPk(String tableName, String pkField, Object pkValue) throws SQLException { + return get("SELECT * FROM " + tableName + " WHERE " + pkField + " = ?", pkValue); + } + + /** + * 获取单条记录. + * + * @param sql SQL + * @param params 参数 + * @return 单条记录 + * @throws SQLException SQL错误 + */ + public Map get(String sql, Object... params) throws SQLException { + return DBExecutor.get(sql, params, getConnection(), isCloseConnection()); + } + + /** + * 获取多条记录. + * + * @param sql SQL + * @param params 参数 + * @return 多条记录(带分页) + * @throws SQLException SQL错误 + */ + public List> find(String sql, Object... params) throws SQLException { + return DBExecutor.find(sql, params, getConnection(), isCloseConnection()); + } + + /** + * 获取多条记录(带分页). + * + * @param sql SQL + * @param params 参数 + * @param pageNumber 页码(从1开始) + * @param pageSize 每页条数 + * @return 多条记录(带分页) + * @throws SQLException SQL错误 + */ + public Page> page(String sql, int pageNumber, int pageSize, Object... params) throws SQLException { + return DBExecutor.page(sql, params, pageNumber, pageSize, getConnection(), isCloseConnection(), dsInfo.getDialect()); + } + + /** + * 获取记录数. + * + * @param sql SQL + * @param params 参数 + * @return 记录数 + * @throws SQLException SQL错误 + */ + public long count(String sql, Object... params) throws SQLException { + return DBExecutor.count(sql, params, getConnection(), isCloseConnection(), dsInfo.getDialect()); + } + + /** + * 添加记录. + * + * @param tableName 表名 + * @param values 值列表 + * @return 影响行数 + * @throws SQLException SQL错误 + */ + public int insert(String tableName, Map values) throws SQLException { + return DBExecutor.insert(tableName, values, getConnection(), isCloseConnection(), dsInfo.getDialect()); + } + + /** + * 修改记录. + * + * @param tableName 表名 + * @param pkField 主键字段 + * @param pkValue 主键值 + * @param values 值列表 + * @return 影响行数 + * @throws SQLException SQL错误 + */ + public int modify(String tableName, String pkField, Object pkValue, Map values) throws SQLException { + return DBExecutor.modify(tableName, pkField, pkValue, values, getConnection(), isCloseConnection(), dsInfo.getDialect()); + } + + /** + * 更新记录. + * + * @param sql SQL + * @param params 参数 + * @return 影响行数 + * @throws SQLException SQL错误 + */ + public int update(String sql, Object... params) throws SQLException { + return DBExecutor.update(sql, params, getConnection(), isCloseConnection(), dsInfo.getDialect()); + } + + /** + * 批量更新记录. + * + * @param sql SQL + * @param params 参数 + * @return 影响行数 + * @throws SQLException SQL错误 + */ + public int[] batch(String sql, Object[][] params) throws SQLException { + return DBExecutor.batch(sql, params, getConnection(), isCloseConnection(), dsInfo.getDialect()); + } + + /** + * 批量更新记录. + * + * @param sqls SQL + * @throws SQLException SQL错误 + */ + public void batch(Map sqls) throws SQLException { + DBExecutor.batch(sqls, getConnection(), isCloseConnection(), dsInfo.getDialect()); + } + + /** + * 删除单条记录. + * + * @param tableName 表名 + * @param pkField 主键字段 + * @param pkValue 主键值 + * @return 影响行数 + * @throws SQLException SQL错误 + */ + public Integer delete(String tableName, String pkField, Object pkValue) throws SQLException { + return update("DELETE FROM " + tableName + " WHERE " + pkField + " = ?", pkValue); + } + + /** + * 删除所有记录. + * + * @param tableName 表名 + * @return 单条记录 + * @throws SQLException SQL错误 + */ + public Integer deleteAll(String tableName) throws SQLException { + return update("DELETE FROM " + tableName); + } + + /** + * 获取Meta信息. + * + * @param tableName 表名 + * @return Meta信息 + * @throws SQLException SQL错误 + */ + public List getMetaData(String tableName) throws SQLException { + return DBExecutor.getMetaData(tableName, getConnection()); + } + + /** + * 获取Meta信息. + * + * @param tableName 表名 + * @param fieldName 指定的字段名 + * @return Meta信息 + * @throws SQLException SQL错误 + */ + public Meta getMetaData(String tableName, String fieldName) throws SQLException { + return DBExecutor.getMetaData(tableName, fieldName, getConnection()); + } + + /** + * 打开事务. + */ + public void open() { + Connection conn = threadLocalConnection.get(); + try { + if (null == conn) { + conn = getConnection(); + threadLocalConnection.set(conn); + } + conn.setAutoCommit(false); + } catch (SQLException e) { + logger.error("[DewDBUtils]Connection open error", e); + } + } + + /** + * 提交事务. + */ + public void commit() { + Connection conn = threadLocalConnection.get(); + if (null != conn) { + try { + conn.commit(); + } catch (SQLException e) { + logger.error("[DewDBUtils]Connection commit error", e); + } + } + close(); + } + + /** + * 显式回滚事务. + *

+ * 发生SQL错误时会自动回滚,但业务错误需要调用此方法手工回滚. + */ + public void rollback() { + Connection conn = threadLocalConnection.get(); + if (null != conn) { + try { + conn.rollback(); + } catch (SQLException e) { + logger.error("[DewDBUtils]Connection rollback error", e); + } + } + close(); + } + + private boolean isCloseConnection() { + return null == threadLocalConnection.get(); + } + + private void close() { + Connection conn = threadLocalConnection.get(); + if (null != conn) { + try { + if (!conn.isClosed()) { + conn.close(); + } + threadLocalConnection.set(null); + } catch (SQLException e) { + logger.error("[DewDBUtils]Connection close error", e); + } + } + } + + @SneakyThrows + private Connection getConnection() { + try { + if (threadLocalConnection.get() != null && !threadLocalConnection.get().isClosed()) { + return threadLocalConnection.get(); + } + Connection conn = dsInfo.getDataSource().getConnection(); + if (!conn.isClosed()) { + return conn; + } + //Re-setting connection when connection was close. + synchronized (DSLoader.class) { + logger.warn("[DewDBUtils]Connection info [{}] was close", conn.toString()); + DSLoader.loadPool(dsInfo.getDsConfig(), dsInfo.getDialect()); + return dsInfo.getDataSource().getConnection(); + } + } catch (SQLException e) { + logger.error("[DewDBUtils]Connection get error.", e); + throw e; + } + } + +} diff --git a/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/DewDBUtils.java b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/DewDBUtils.java new file mode 100644 index 00000000..e40a152a --- /dev/null +++ b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/DewDBUtils.java @@ -0,0 +1,83 @@ +/* + * Copyright 2020. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package group.idealworld.dew.core.dbutils; + +import group.idealworld.dew.core.dbutils.dto.DBUtilsConfig; +import group.idealworld.dew.core.dbutils.dto.DSConfig; +import group.idealworld.dew.core.dbutils.process.DSLoader; + +/** + * 操作入口类. + * + * @author gudaoxuri + */ +public class DewDBUtils { + + /** + * 初始化数据源. + * + * @param configPath 配置文件路径 + */ + public static void init(String configPath) { + DSLoader.load(configPath); + } + + public static void init2(DBUtilsConfig dbUtilsConfig){ + DSLoader.load2(dbUtilsConfig); + } + + /** + * 添加数据源. + * + * @param dsConfig 配置文件 + */ + public static void addDS(DSConfig dsConfig) { + DSLoader.addDS(dsConfig); + } + + /** + * 删除数据源. + * + * @param dsCode 数据源编码 + */ + public static void removeDS(String dsCode) { + DSLoader.removeDS(dsCode); + } + + /** + * 删除数据源. + * + * @param dewDB DB实例 + */ + public static void removeDS(DewDB dewDB) { + if (dewDB != null + && dewDB.getDsInfo() != null + && dewDB.getDsInfo().getDsConfig() != null) { + DSLoader.removeDS(dewDB.getDsInfo().getDsConfig().getCode()); + } + } + + /** + * 选择DB实例. + * + * @param dsCode 数据源编码 + * @return DB实例 + */ + public static DewDB use(String dsCode) { + return DewDB.pick(dsCode); + } +} diff --git a/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dialect/Dialect.java b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dialect/Dialect.java new file mode 100644 index 00000000..d7e0c251 --- /dev/null +++ b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dialect/Dialect.java @@ -0,0 +1,40 @@ +/* + * Copyright 2020. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package group.idealworld.dew.core.dbutils.dialect; + +import java.sql.SQLException; +import java.util.List; +import java.util.Map; + +public interface Dialect { + + String paging(String sql, long pageNumber, long pageSize) throws SQLException; + + String count(String sql) throws SQLException; + + String getTableInfo(String tableName) throws SQLException; + + @Deprecated + String createTableIfNotExist(String tableName, String tableDesc, Map fields, Map fieldsDesc, + List indexFields, List uniqueFields, String pkField) throws SQLException; + + String validationQuery(); + + String getDriver(); + + DialectType getDialectType(); +} diff --git a/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dialect/DialectFactory.java b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dialect/DialectFactory.java new file mode 100644 index 00000000..8e1badee --- /dev/null +++ b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dialect/DialectFactory.java @@ -0,0 +1,35 @@ +/* + * Copyright 2020. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package group.idealworld.dew.core.dbutils.dialect; + + +public class DialectFactory { + + public static Dialect parseDialect(String url) { + if (url.startsWith("jdbc:h2")) { + return new H2Dialect(); + } else if (url.startsWith("jdbc:mysql")) { + return new MySQLDialect(); + } else if (url.startsWith("jdbc:postgresql")) { + return new PostgresDialect(); + } else if (url.startsWith("jdbc:hive2")) { + return new HiveDialect(); + } + return null; + } + +} diff --git a/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dialect/DialectType.java b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dialect/DialectType.java new file mode 100644 index 00000000..dc73c4a9 --- /dev/null +++ b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dialect/DialectType.java @@ -0,0 +1,23 @@ +/* + * Copyright 2020. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package group.idealworld.dew.core.dbutils.dialect; + +public enum DialectType { + + MYSQL,POSTGRE,H2,HIVE + +} diff --git a/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dialect/H2Dialect.java b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dialect/H2Dialect.java new file mode 100644 index 00000000..26c47823 --- /dev/null +++ b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dialect/H2Dialect.java @@ -0,0 +1,116 @@ +/* + * Copyright 2020. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package group.idealworld.dew.core.dbutils.dialect; + +import java.sql.SQLException; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +class H2Dialect implements Dialect { + + @Override + public String paging(String sql, long pageNumber, long pageSize) throws SQLException { + return sql + " LIMIT " + pageSize + " OFFSET " + (pageNumber - 1) * pageSize; + } + + @Override + public String count(String sql) throws SQLException { + return "SELECT COUNT(1) FROM ( " + sql + " ) "; + } + + @Override + public String getTableInfo(String tableName) throws SQLException { + return "SELECT * FROM INFORMATION_SCHEMA.TABLES t WHERE t.table_name = '" + tableName + "'"; + } + + @Override + public String createTableIfNotExist(String tableName, String tableDesc, Map fields, Map fieldsDesc, List indexFields, List uniqueFields, String pkField) throws SQLException { + StringBuilder sb = new StringBuilder("CREATE TABLE IF NOT EXISTS " + tableName + " ( "); + for (Map.Entry field : fields.entrySet()) { + String f = field.getValue().toLowerCase(); + String t; + switch (f) { + case "int": + case "integer": + t = "INT"; + break; + case "long": + t = "BIGINT"; + break; + case "short": + t = "SMALLINT"; + break; + case "string": + t = "VARCHAR(65535)"; + break; + case "text": + t = "TEXT"; + break; + case "bool": + case "boolean": + t = "BOOLEAN"; + break; + case "float": + // https://www.h2database.com/html/faq.html?highlight=float&search=FLOAT#float_is_double + t = "FLOAT(24)"; + break; + case "double": + t = "DOUBLE"; + break; + case "char": + t = "CHAR"; + break; + case "date": + t = "TIMESTAMP"; + break; + case "uuid": + t = "UUID"; + break; + case "bigdecimal": + case "decimal": + t = "DECIMAL"; + break; + default: + throw new SQLException("Not support type:" + f); + } + sb.append(field.getKey()).append(" ").append(t).append(" ,"); + } + if (pkField != null && !Objects.equals(pkField.trim(), "")) { + return sb.append("primary key(").append(pkField.trim()).append(") )").toString(); + } else { + return sb.substring(0, sb.length() - 1) + ")"; + } + //TODO + } + + @Override + public String validationQuery() { + return "SELECT 1"; + } + + @Override + public String getDriver() { + return "org.h2.Driver"; + } + + @Override + public DialectType getDialectType() { + return DialectType.H2; + } + +} diff --git a/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dialect/HiveDialect.java b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dialect/HiveDialect.java new file mode 100644 index 00000000..79c0c1b3 --- /dev/null +++ b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dialect/HiveDialect.java @@ -0,0 +1,104 @@ +/* + * Copyright 2020. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package group.idealworld.dew.core.dbutils.dialect; + +import java.sql.SQLException; +import java.util.List; +import java.util.Map; + +class HiveDialect implements Dialect { + + @Override + public String paging(String sql, long pageNumber, long pageSize) throws SQLException { + return sql + " LIMIT " + (pageNumber - 1) * pageSize + ", " + pageSize; + } + + @Override + public String count(String sql) throws SQLException { + return "SELECT COUNT(1) FROM ( " + sql + " ) _" + System.currentTimeMillis(); + } + + @Override + public String getTableInfo(String tableName) throws SQLException { + //TODO + throw new RuntimeException("NotImplementedException"); + } + + @Override + public String createTableIfNotExist(String tableName, String tableDesc, Map fields, Map fieldsDesc, List indexFields, List uniqueFields, String pkField) throws SQLException { + //TODO + throw new RuntimeException("NotImplementedException"); + /*StringBuilder sb = new StringBuilder("CREATE TABLE IF NOT EXISTS " + tableName + " ( "); + for (Map.Entry field : fields.entrySet()) { + String f = field.getValue().toLowerCase(); + String t; + switch (f) { + case "int": + case "integer": + t = "INT"; + break; + case "long": + t = "BIGINT"; + break; + case "short": + t = "SMALLINT"; + break; + case "string": + t = "STRING"; + break; + case "bool": + case "boolean": + t = "BOOLEAN"; + break; + case "float": + t = "FLOAT"; + break; + case "double": + t = "DOUBLE"; + break; + case "char": + t = "CHAR"; + break; + case "date": + t = "TIMESTAMP"; + break; + case "decimal": + t = "DECIMAL"; + break; + default: + throw new SQLException("Not support type:" + f); + } + sb.append(field.getKey()).append(" ").append(t).append(" ,"); + } + return sb.substring(0, sb.length() - 1) + ")";*/ + } + + @Override + public String validationQuery() { + return "SELECT 1"; + } + + @Override + public String getDriver() { + return "org.apache.hive.jdbc.HiveDriver"; + } + + @Override + public DialectType getDialectType() { + return DialectType.HIVE; + } +} diff --git a/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dialect/MySQLDialect.java b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dialect/MySQLDialect.java new file mode 100644 index 00000000..01cd1b79 --- /dev/null +++ b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dialect/MySQLDialect.java @@ -0,0 +1,116 @@ +/* + * Copyright 2020. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package group.idealworld.dew.core.dbutils.dialect; + +import java.sql.SQLException; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +class MySQLDialect implements Dialect { + + @Override + public String paging(String sql, long pageNumber, long pageSize) throws SQLException { + return sql + " LIMIT " + (pageNumber - 1) * pageSize + ", " + pageSize; + } + + @Override + public String count(String sql) throws SQLException { + return "SELECT COUNT(1) FROM ( " + sql + " ) _" + System.currentTimeMillis(); + } + + @Override + public String getTableInfo(String tableName) throws SQLException { + //TODO + throw new RuntimeException("NotImplementedException"); + } + + @Override + public String createTableIfNotExist(String tableName, String tableDesc, Map fields, Map fieldsDesc, List indexFields, List uniqueFields, String pkField) throws SQLException { + //TODO + StringBuilder sb = new StringBuilder("CREATE TABLE IF NOT EXISTS " + tableName + " ( "); + for (Map.Entry field : fields.entrySet()) { + String f = field.getValue().toLowerCase(); + String t; + switch (f) { + case "int": + case "integer": + t = "INT"; + break; + case "long": + t = "BIGINT"; + break; + case "short": + t = "SMALLINT"; + break; + case "string": + t = "VARCHAR(2000)"; + break; + case "text": + t = "TEXT"; + break; + case "bool": + case "boolean": + t = "BOOLEAN"; + break; + case "float": + t = "FLOAT"; + break; + case "double": + t = "DOUBLE"; + break; + case "char": + t = "CHAR"; + break; + case "date": + t = "TIMESTAMP"; + break; + case "uuid": + t = "UUID"; + break; + case "bigdecimal": + case "decimal": + t = "DECIMAL"; + break; + default: + throw new SQLException("Not support type:" + f); + } + sb.append(field.getKey()).append(" ").append(t).append(" ,"); + } + if (pkField != null && !Objects.equals(pkField.trim(), "")) { + return sb.append("primary key(").append(pkField.trim()).append(") )").toString(); + } else { + return sb.substring(0, sb.length() - 1) + ")"; + } + } + + @Override + public String validationQuery() { + return "SELECT 1"; + } + + @Override + public String getDriver() { + return "com.mysql.jdbc.Driver"; + } + + @Override + public DialectType getDialectType() { + return DialectType.MYSQL; + } + +} diff --git a/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dialect/PostgresDialect.java b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dialect/PostgresDialect.java new file mode 100644 index 00000000..5ff49b24 --- /dev/null +++ b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dialect/PostgresDialect.java @@ -0,0 +1,128 @@ +/* + * Copyright 2020. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package group.idealworld.dew.core.dbutils.dialect; + +import java.sql.SQLException; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +class PostgresDialect implements Dialect { + + @Override + public String paging(String sql, long pageNumber, long pageSize) throws SQLException { + return sql + " LIMIT " + pageSize + " OFFSET " + (pageNumber - 1) * pageSize; + } + + @Override + public String count(String sql) throws SQLException { + return "SELECT COUNT(1) FROM ( " + sql + " ) _" + System.currentTimeMillis(); + } + + @Override + public String getTableInfo(String tableName) throws SQLException { + return "SELECT * FROM pg_tables t WHERE t.tablename = '" + tableName + "'"; + } + + @Override + public String createTableIfNotExist(String tableName, String tableDesc, Map fields, Map fieldsDesc, List indexFields, List uniqueFields, String pkField) throws SQLException { + StringBuilder sb = new StringBuilder("CREATE TABLE IF NOT EXISTS " + tableName + " ( "); + for (Map.Entry field : fields.entrySet()) { + String f = field.getValue().toLowerCase(); + String t; + switch (f) { + case "seq": + t = "serial"; + break; + case "int": + case "integer": + case "short": + t = "integer"; + break; + case "long": + t = "bigint"; + break; + case "string": + t = "character varying(65535)"; + break; + case "text": + t = "text"; + break; + case "bool": + case "boolean": + t = "boolean"; + break; + case "float": + case "double": + t = "double precision"; + break; + case "char": + t = "character"; + break; + case "date": + t = "date"; + break; + case "bigdecimal": + case "decimal": + t = "numeric"; + break; + default: + throw new SQLException("Not support type:" + f); + } + sb.append(field.getKey()).append(" ").append(t).append(" ,"); + } + if (uniqueFields != null && !uniqueFields.isEmpty()) { + for (String uField : uniqueFields) { + sb.append("CONSTRAINT \"u_").append(tableName).append("_").append(uField).append("\" UNIQUE (\"").append(uField).append("\"),"); + } + } + if (pkField != null && !Objects.equals(pkField.trim(), "")) { + sb.append("primary key(").append(pkField.trim()).append(") );"); + } else { + sb = new StringBuilder(sb.substring(0, sb.length() - 1) + ");"); + } + if (indexFields != null && !indexFields.isEmpty()) { + for (String idxFields : indexFields) { + sb.append("CREATE INDEX \"i_").append(tableName).append("_").append(idxFields).append("\" ON \"").append(tableName).append("\" (\"").append(idxFields).append("\");"); + } + } + if (tableDesc != null && !tableDesc.isEmpty()) { + sb.append("COMMENT ON TABLE \"").append(tableName).append("\" IS '").append(tableDesc).append("';"); + } + if (fieldsDesc != null && !fieldsDesc.isEmpty()) { + for (Map.Entry field : fieldsDesc.entrySet()) { + sb.append("COMMENT ON COLUMN \"").append(tableName).append("\".\"").append(field.getKey()).append("\" IS '").append(field.getValue()).append("';"); + } + } + return sb.toString(); + } + + @Override + public String validationQuery() { + return "SELECT 'x'"; + } + + @Override + public String getDriver() { + return "org.postgresql.Driver"; + } + + @Override + public DialectType getDialectType() { + return DialectType.POSTGRE; + } +} diff --git a/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dto/DBUtilsConfig.java b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dto/DBUtilsConfig.java new file mode 100644 index 00000000..76c4cd7f --- /dev/null +++ b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dto/DBUtilsConfig.java @@ -0,0 +1,46 @@ +/* + * Copyright 2020. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package group.idealworld.dew.core.dbutils.dto; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +/** + * 配置类 + * + * @author gudaoxuri + */ +@ConfigurationProperties(prefix = "ds") +@Data +public class DBUtilsConfig { + + private List ds = new ArrayList<>(); + private DynamicDS dynamicDS = new DynamicDS(); + + @Data + public static class DynamicDS { + + private Boolean enabled = false; + private String dsCode; + private String fetchSql = "select code,url,username,password,monitor,pool_initialSize,pool_maxActive from multi_ds"; + + } +} diff --git a/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dto/DSConfig.java b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dto/DSConfig.java new file mode 100644 index 00000000..b542a946 --- /dev/null +++ b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dto/DSConfig.java @@ -0,0 +1,59 @@ +/* + * Copyright 2020. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package group.idealworld.dew.core.dbutils.dto; + +import lombok.Builder; +import lombok.Data; +import lombok.experimental.Tolerate; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * @author gudaoxuri + */ +@Data +@Builder +public class DSConfig { + + private String code; + private String url; + private String username; + private String password; + @Builder.Default + private Boolean monitor = false; + @Builder.Default + private PoolConfig pool = new PoolConfig(); + + @Data + @Builder + public static class PoolConfig { + + @Builder.Default + private Integer initialSize = 5; + @Builder.Default + private Integer maxActive = 20; + + @Tolerate + public PoolConfig() { + } + + } + + @Tolerate + public DSConfig() { + } +} diff --git a/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dto/Meta.java b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dto/Meta.java new file mode 100644 index 00000000..ccb9de07 --- /dev/null +++ b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dto/Meta.java @@ -0,0 +1,33 @@ +/* + * Copyright 2020. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package group.idealworld.dew.core.dbutils.dto; + +import lombok.Data; + +@Data +public class Meta { + + private int type; + private String code; + private String label; + + public Meta(int type, String code, String label) { + this.type = type; + this.code = code; + this.label = label; + } +} diff --git a/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dto/Page.java b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dto/Page.java new file mode 100644 index 00000000..6f890523 --- /dev/null +++ b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/dto/Page.java @@ -0,0 +1,36 @@ +/* + * Copyright 2020. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package group.idealworld.dew.core.dbutils.dto; + +import lombok.Data; + +import java.util.List; + +/** + * 分页辅助类 + */ +@Data +public class Page { + + //start with 1 + private long pageNumber; + private long pageSize; + private long pageTotal; + private long recordTotal; + private List objects; + +} diff --git a/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/process/DBExecutor.java b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/process/DBExecutor.java new file mode 100644 index 00000000..e66eca5b --- /dev/null +++ b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/process/DBExecutor.java @@ -0,0 +1,386 @@ +/* + * Copyright 2020. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package group.idealworld.dew.core.dbutils.process; + +import group.idealworld.dew.core.dbutils.dialect.Dialect; +import group.idealworld.dew.core.dbutils.dialect.DialectType; +import group.idealworld.dew.core.dbutils.dto.Meta; +import group.idealworld.dew.core.dbutils.dto.Page; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.dbutils.QueryRunner; +import org.apache.commons.dbutils.handlers.*; + +import java.io.BufferedReader; +import java.io.Reader; +import java.math.BigDecimal; +import java.sql.*; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Slf4j +public class DBExecutor { + + static final QueryRunner queryRunner = new QueryRunner(); + + public static E get(String sql, Object[] params, Class clazz, Connection conn, boolean isCloseConn) throws SQLException { + try { + if (params == null) { + return (E) queryRunner.query(conn, sql, new BeanHandler(clazz)); + } else { + return (E) queryRunner.query(conn, sql, new BeanHandler(clazz), params); + } + } catch (SQLException e) { + log.error("[DewDBUtils]Get error : " + sql, e); + throw e; + } finally { + if (isCloseConn) { + closeConnection(conn); + } + } + } + + public static List find(String sql, Object[] params, Class clazz, Connection conn, boolean isCloseConn) throws SQLException { + try { + if (null == params) { + return (List) queryRunner.query(conn, sql, new BeanListHandler(clazz)); + } else { + return (List) queryRunner.query(conn, sql, new BeanListHandler(clazz), params); + } + } catch (SQLException e) { + log.error("[DewDBUtils]Find error : " + sql, e); + throw e; + } finally { + if (isCloseConn) { + closeConnection(conn); + } + } + } + + public static Page page(String sql, Object[] params, long pageNumber, long pageSize, Class clazz, Connection conn, boolean isCloseConn, Dialect dialect) throws SQLException { + Page page = new Page<>(); + String pagedSql = dialect.paging(sql, pageNumber, pageSize); + page.setPageNumber(pageNumber); + page.setPageSize(pageSize); + page.setRecordTotal(count(sql, params, conn, false, dialect)); + page.setPageTotal((page.getRecordTotal() + pageSize - 1) / pageSize); + page.setObjects(find(pagedSql, params, clazz, conn, isCloseConn)); + return page; + } + + public static Map get(String sql, Object[] params, Connection conn, boolean isCloseConn) throws SQLException { + try { + Map result; + if (null == params) { + result = queryRunner.query(conn, sql, new MapHandler()); + } else { + result = queryRunner.query(conn, sql, new MapHandler(), params); + } + if (result != null) { + Map lowCaseResult = new LinkedHashMap<>(); + for (Map.Entry entry : result.entrySet()) { + if (entry.getValue() instanceof Clob) { + lowCaseResult.put(entry.getKey().toLowerCase(), convertClob((Clob) entry.getValue())); + } else { + lowCaseResult.put(entry.getKey().toLowerCase(), entry.getValue()); + } + } + result = lowCaseResult; + } + return result; + } catch (SQLException e) { + log.error("[DewDBUtils]Get error : " + sql, e); + throw e; + } finally { + if (isCloseConn) { + closeConnection(conn); + } + } + } + + public static List> find(String sql, Object[] params, Connection conn, boolean isCloseConn) throws SQLException { + try { + List> result; + if (null == params) { + result = queryRunner.query(conn, sql, new MapListHandler()); + } else { + result = queryRunner.query(conn, sql, new MapListHandler(), params); + } + if (result != null && !result.isEmpty()) { + List> lowCaseResult = new ArrayList<>(); + for (Map item : result) { + Map lowCaseItem = new LinkedHashMap<>(); + for (Map.Entry entry : item.entrySet()) { + if (entry.getValue() instanceof Clob) { + lowCaseItem.put(entry.getKey().toLowerCase(), convertClob((Clob) entry.getValue())); + } else { + lowCaseItem.put(entry.getKey().toLowerCase(), entry.getValue()); + } + } + lowCaseResult.add(lowCaseItem); + } + result = lowCaseResult; + } + return result; + } catch (SQLException e) { + log.error("[DewDBUtils]Find error : " + sql, e); + throw e; + } finally { + if (isCloseConn) { + closeConnection(conn); + } + } + } + + public static Page> page(String sql, Object[] params, long pageNumber, long pageSize, Connection conn, boolean isCloseConn, Dialect dialect) throws SQLException { + Page> page = new Page<>(); + String pagedSql = dialect.paging(sql, pageNumber, pageSize); + page.setPageNumber(pageNumber); + page.setPageSize(pageSize); + page.setRecordTotal(count(sql, params, conn, false, dialect)); + page.setPageTotal((page.getRecordTotal() + pageSize - 1) / pageSize); + page.setObjects(find(pagedSql, params, conn, isCloseConn)); + return page; + } + + public static long count(String sql, Connection conn, boolean isCloseConn, Dialect dialect) throws SQLException { + return count(sql, null, conn, isCloseConn, dialect); + } + + public static long count(String sql, Object[] params, Connection conn, boolean isCloseConn, Dialect dialect) throws SQLException { + String countSql = dialect.count(sql); + try { + if (null == params) { + return (Long) queryRunner.query(conn, countSql, scalarHandler); + } else { + return (Long) queryRunner.query(conn, countSql, scalarHandler, params); + } + + } catch (SQLException e) { + log.error("[DewDBUtils]Count error : " + countSql, e); + throw e; + } finally { + if (isCloseConn) { + closeConnection(conn); + } + } + } + + public static int insert(String tableName, Map values, + Connection conn, boolean closeConnection, Dialect dialect) throws SQLException { + String fields = String.join(",", values.keySet()); + String valueArgs = values.keySet().stream().map(f -> "?").collect(Collectors.joining(",")); + String sql = "INSERT INTO " + tableName + " (" + fields + ") VALUES (" + valueArgs + ")"; + return update(sql, values.values().toArray(), conn, closeConnection, dialect); + } + + public static int modify(String tableName, String pkField, Object pkValue, Map values, + Connection conn, boolean closeConnection, Dialect dialect) throws SQLException { + String set = values.keySet().stream().map(k -> k + " = ?").collect(Collectors.joining(", ")); + String sql = "UPDATE " + tableName + " SET " + set + " WHERE " + pkField + " = ? "; + List params = new ArrayList<>(values.values()); + params.add(pkValue); + return update(sql, params.toArray(), conn, closeConnection, dialect); + } + + public static int update(String sql, Object[] params, Connection conn, boolean isCloseConn, Dialect dialect) throws SQLException { + if (dialect.getDialectType() == DialectType.HIVE && params != null) { + throw new SQLException("SparkSQL don't support [params] parameter."); + } + try { + if (null == params) { + return queryRunner.update(conn, sql); + } else { + return queryRunner.update(conn, sql, params); + } + } catch (SQLException e) { + try { + conn.rollback(); + } catch (SQLException e1) { + log.error("[DewDBUtils]Connection error : " + sql, e1); + throw e1; + } + log.error("[DewDBUtils]Update error : " + sql, e); + throw e; + } finally { + if (isCloseConn) { + closeConnection(conn); + } + } + } + + public static void batch(Map sqls, Connection conn, boolean isCloseConn, Dialect dialect) throws SQLException { + if (dialect.getDialectType() == DialectType.HIVE) { + throw new SQLException("SparkSQL don't support [batch] method."); + } + for (Map.Entry entry : sqls.entrySet()) { + try { + if (null == entry.getValue()) { + queryRunner.update(conn, entry.getKey()); + } else { + queryRunner.update(conn, entry.getKey(), entry.getValue()); + } + } catch (SQLException e) { + try { + conn.rollback(); + } catch (SQLException e1) { + log.error("[DewDBUtils]Connection error : " + entry.getKey(), e1); + throw e1; + } + log.error("[DewDBUtils]Batch error : " + entry.getKey(), e); + throw e; + } finally { + if (isCloseConn) { + closeConnection(conn); + } + } + } + } + + public static int[] batch(String sql, Object[][] params, Connection conn, boolean isCloseConn, Dialect dialect) throws SQLException { + if (dialect.getDialectType() == DialectType.HIVE) { + throw new SQLException("SparkSQL don't support [batch] method."); + } + try { + return queryRunner.batch(conn, sql, params); + } catch (SQLException e) { + try { + conn.rollback(); + } catch (SQLException e1) { + log.error("[DewDBUtils]Connection error : " + sql, e1); + throw e1; + } + log.error("[DewDBUtils]Batch error : " + sql, e); + throw e; + } finally { + if (isCloseConn) { + closeConnection(conn); + } + } + } + + public static List getMetaData(String tableName, Connection conn) throws SQLException { + return findMetaData(tableName, null, conn); + } + + public static Meta getMetaData(String tableName, String fieldName, Connection conn) throws SQLException { + List metas = findMetaData(tableName, fieldName, conn); + if (metas.size() == 1) { + return metas.get(0); + } + return null; + } + + private static List findMetaData(String tableName, String fieldName, Connection conn) throws SQLException { + PreparedStatement st = null; + ResultSet rs = null; + try { + st = conn.prepareStatement("SELECT * FROM " + tableName + " WHERE 1=2"); + rs = st.executeQuery(); + ResultSetMetaData meta = rs.getMetaData(); + List metas = new ArrayList<>(); + for (int i = 1; i <= meta.getColumnCount(); i++) { + String columnName = meta.getColumnName(i).substring(meta.getColumnName(i).lastIndexOf(".") + 1); + String columnLabel = meta.getColumnLabel(i).substring(meta.getColumnLabel(i).lastIndexOf(".") + 1); + if (null != fieldName && !columnLabel.equalsIgnoreCase(fieldName)) { + continue; + } + metas.add(new Meta(meta.getColumnType(i), columnName.toLowerCase(), columnLabel.toLowerCase())); + } + return metas; + } catch (SQLException e) { + log.error("[DewDBUtils]getResultSet error : " + tableName, e); + throw e; + } finally { + if (null != rs) { + rs.close(); + } + if (null != st) { + st.close(); + } + closeConnection(conn); + } + } + + public static void ddl(String sql, Connection conn, boolean isCloseConn) throws SQLException { + try { + log.trace("[DewDBUtils]Execute DDL : " + sql); + queryRunner.update(conn, sql); + } catch (SQLException e) { + try { + conn.rollback(); + } catch (SQLException e1) { + log.error("[DewDBUtils]Connection error : " + sql, e1); + throw e1; + } + log.error("[DewDBUtils]ddl error : " + sql, e); + throw e; + } finally { + if (isCloseConn) { + closeConnection(conn); + } + } + } + + private static void closeConnection(Connection conn) throws SQLException { + if (null != conn && !conn.isClosed()) { + try { + log.trace("[DewDBUtils]Close connection:" + conn.toString()); + conn.close(); + } catch (SQLException e) { + log.error("[DewDBUtils]Close transactionConnection error : ", e); + throw e; + } + } + } + + private static ScalarHandler scalarHandler = new ScalarHandler() { + @Override + public Object handle(ResultSet rs) throws SQLException { + Object obj = super.handle(rs); + if (obj instanceof BigDecimal) { + return ((BigDecimal) obj).longValue(); + } else if (obj instanceof Long) { + return obj; + } else { + return ((Number) obj).longValue(); + } + } + }; + + @SneakyThrows + private static String convertClob(Clob clob) { + StringBuilder value = new StringBuilder(); + String line; + if (clob != null) { + Reader reader = clob.getCharacterStream(); + BufferedReader br = new BufferedReader(reader); + while ((line = br.readLine()) != null) { + value.append(line).append("\r\n"); + } + } + if (value.length() >= 2) { + return value.substring(0, value.length() - 2); + } else { + return ""; + } + } + +} diff --git a/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/process/DSLoader.java b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/process/DSLoader.java new file mode 100644 index 00000000..40050414 --- /dev/null +++ b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/process/DSLoader.java @@ -0,0 +1,169 @@ +/* + * Copyright 2020. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package group.idealworld.dew.core.dbutils.process; + +import com.alibaba.druid.pool.DruidDataSource; +import group.idealworld.dew.core.dbutils.DewDBUtils; +import group.idealworld.dew.core.dbutils.dialect.Dialect; +import group.idealworld.dew.core.dbutils.dialect.DialectFactory; +import group.idealworld.dew.core.dbutils.dto.DBUtilsConfig; +import group.idealworld.dew.core.dbutils.dto.DSConfig; +import group.idealworld.dew.core.dbutils.utils.YamlHelper; +import lombok.Builder; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +import javax.sql.DataSource; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.sql.SQLException; +import java.util.*; +import java.util.stream.Collectors; + +@Slf4j +public class DSLoader { + + private static final Map MULTI_DS = new HashMap<>(); + + public static DSInfo getDSInfo(String dsCode) { + if (!MULTI_DS.containsKey(dsCode)) { + throw new RuntimeException("[DewDBUtils]Can't find dsCode [" + dsCode + "]"); + } + return MULTI_DS.get(dsCode); + } + + public static void load(String configPath) { + MULTI_DS.clear(); + log.info("[DewDBUtils]Load DS from {}", configPath); + File configFile = new File(configPath); + if (!configFile.exists() || configFile.isDirectory()) { + throw new RuntimeException("Config file [" + configPath + "] NOT exist"); + } + DBUtilsConfig dbUtilsConfig; + try { + String content = new String(Files.readAllBytes(configFile.toPath()), StandardCharsets.UTF_8); + dbUtilsConfig = YamlHelper.toObject(DBUtilsConfig.class, content); + } catch (IOException e) { + throw new RuntimeException("Config file [" + configPath + "] parse error", e); + } + loadDS(dbUtilsConfig.getDs()); + if (dbUtilsConfig.getDynamicDS().getEnabled()) { + loadDynamicDS(dbUtilsConfig.getDynamicDS().getDsCode(), dbUtilsConfig.getDynamicDS().getFetchSql()); + } + } + + public static void load2(DBUtilsConfig dbUtilsConfig) { + loadDS(dbUtilsConfig.getDs()); + if (dbUtilsConfig.getDynamicDS().getEnabled()) { + loadDynamicDS(dbUtilsConfig.getDynamicDS().getDsCode(), dbUtilsConfig.getDynamicDS().getFetchSql()); + } + } + + public static void addDS(DSConfig dsConfig) { + log.info("[DewDBUtils]Add DS {}", dsConfig.getCode()); + loadDS(new ArrayList() { + { + add(dsConfig); + } + }); + } + + public static void removeDS(String dsCode) { + log.info("[DewDBUtils]Remove DS {}", dsCode); + if (MULTI_DS.containsKey(dsCode)) { + MULTI_DS.get(dsCode).setDataSource(null); + MULTI_DS.remove(dsCode); + } + } + + private static void loadDS(List dsConfigs) { + dsConfigs.forEach(dsConfig -> { + Dialect dialect = DialectFactory.parseDialect(dsConfig.getUrl()); + assert dialect != null; + MULTI_DS.put(dsConfig.getCode(), DSInfo.builder() + .dataSource(loadPool(dsConfig, dialect)) + .dialect(dialect) + .dsConfig(dsConfig) + .build()); + log.debug("Loaded pool: [{}] {}", dsConfig.getCode(), dsConfig.getUrl()); + }); + } + + public static DataSource loadPool(DSConfig dsConfig, Dialect dialect) { + DruidDataSource dataSource = new DruidDataSource(); + dataSource.setUrl(dsConfig.getUrl()); + dataSource.setDriverClassName(dialect.getDriver()); + dataSource.setUsername(dsConfig.getUsername()); + dataSource.setPassword(dsConfig.getPassword()); + dataSource.setValidationQuery(dialect.validationQuery()); + if (dsConfig.getPool().getInitialSize() != null) { + dataSource.setInitialSize(dsConfig.getPool().getInitialSize()); + } + if (dsConfig.getPool().getMaxActive() != null) { + dataSource.setMaxActive(dsConfig.getPool().getMaxActive()); + } + if (dsConfig.getMonitor()) { + try { + dataSource.setFilters("wall,mergeStat"); + } catch (SQLException e) { + log.warn("[DewDBUtils]Monitor set error", e); + } + } + return dataSource; + } + + private static void loadDynamicDS(String dsCode, String fetchSql) { + List> result = null; + try { + result = DewDBUtils.use(dsCode).find(fetchSql); + } catch (Exception e) { + log.error("[DewDBUtils]Multi DS load error : " + e); + } + if (null != result) { + List dsConfigs = result.stream() + .filter(Objects::nonNull) + .map(res -> { + DSConfig dsConfig = new DSConfig(); + dsConfig.setPool(new DSConfig.PoolConfig()); + dsConfig.setCode(res.get("code").toString()); + dsConfig.setUrl(res.get("url").toString()); + dsConfig.setUsername(res.get("username").toString()); + dsConfig.setPassword(res.get("password").toString()); + dsConfig.setUrl(res.get("url").toString()); + dsConfig.setMonitor(Integer.parseInt(res.get("monitor").toString()) == 1); + dsConfig.getPool().setInitialSize(Integer.parseInt(res.get("pool_initialsize").toString())); + dsConfig.getPool().setMaxActive(Integer.parseInt(res.get("pool_maxactive").toString())); + return dsConfig; + }) + .collect(Collectors.toList()); + loadDS(dsConfigs); + } + } + + @Data + @Builder + public static class DSInfo { + + private DataSource dataSource; + private Dialect dialect; + private DSConfig dsConfig; + + } + +} diff --git a/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/utils/YamlHelper.java b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/utils/YamlHelper.java new file mode 100644 index 00000000..4f64432d --- /dev/null +++ b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/utils/YamlHelper.java @@ -0,0 +1,92 @@ +/* + * Copyright 2020. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package group.idealworld.dew.core.dbutils.utils; + +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.representer.Representer; + +/** + * Yaml helper. + * + * @author gudaoxuri + */ +public class YamlHelper { + + private static Yaml yaml; + + static { + DumperOptions options = new DumperOptions(); + options.setCanonical(false); + options.setDefaultScalarStyle(DumperOptions.ScalarStyle.PLAIN); + options.setIndent(2); + Representer representer = new Representer(); + representer.getPropertyUtils().setSkipMissingProperties(true); + yaml = new Yaml(representer, options); + } + + /** + * To object. + * + * @param the type parameter + * @param content the content + * @return the object + */ + public static T toObject(String content) { + return yaml.load(content); + } + + /** + * To object. + * + * @param the type parameter + * @param clazz the clazz + * @param content the content + * @return the object + */ + public static T toObject(Class clazz, String content) { + return yaml.loadAs(content, clazz); + } + + /** + * To object. + * + * @param the type parameter + * @param clazz the clazz + * @param contents the contents + * @return the object + */ + public static T toObject(Class clazz, String... contents) { + String mergedContent = String.join("\r\n", contents); + return yaml.loadAs(mergedContent, clazz); + } + + /** + * To string. + * + * @param content the content + * @return yaml string + */ + public static String toString(Object content) { + String str = yaml.dump(content); + if (str.startsWith("!!")) { + return str.substring(str.indexOf('\n') + 1); + } + return str; + } + +} diff --git a/framework/modules/dbutils-starter/src/main/resources/META-INF/spring.factories b/framework/modules/dbutils-starter/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..85b2fb0d --- /dev/null +++ b/framework/modules/dbutils-starter/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + group.idealworld.dew.core.dbutils.DbutilsAutoConfiguration diff --git a/framework/modules/dbutils-starter/src/main/resources/application.yml b/framework/modules/dbutils-starter/src/main/resources/application.yml new file mode 100644 index 00000000..e22d0ba8 --- /dev/null +++ b/framework/modules/dbutils-starter/src/main/resources/application.yml @@ -0,0 +1,19 @@ +ds: + ds[0]: + code: default + url: jdbc:h2:mem:db + username: sa + password: + monitor: true + pool: + initialSize: 0 + maxActive: 8 + ds[1]: + code: mysql + url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8 + username: root + password: 123456 + monitor: true + pool: + initialSize: 0 + maxActive: 8 diff --git a/framework/modules/dbutils-starter/src/main/resources/config-dynamic.yml b/framework/modules/dbutils-starter/src/main/resources/config-dynamic.yml new file mode 100644 index 00000000..7d81d582 --- /dev/null +++ b/framework/modules/dbutils-starter/src/main/resources/config-dynamic.yml @@ -0,0 +1,22 @@ +ds: + - code: defalut + url: jdbc:h2:mem:db + username: sa + password: + monitor: true + pool: + initialSize: 0 + maxActive: 8 + - code: mysql + url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8 + username: root + password: 123456 + monitor: true + pool: + initialSize: 0 + maxActive: 8 +dynamicDS: + enabled: true + dsCode: default + fetchSql: select code,url,username,password,monitor,pool_initialSize,pool_maxActive from multi_ds + diff --git a/framework/modules/dbutils-starter/src/main/resources/config.yml b/framework/modules/dbutils-starter/src/main/resources/config.yml new file mode 100644 index 00000000..d7fa9bf6 --- /dev/null +++ b/framework/modules/dbutils-starter/src/main/resources/config.yml @@ -0,0 +1,17 @@ +ds: + - code: default + url: jdbc:h2:mem:db + username: sa + password: + monitor: true + pool: + initialSize: 0 + maxActive: 8 + - code: mysql + url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8 + username: root + password: 123456 + monitor: true + pool: + initialSize: 0 + maxActive: 8 diff --git a/framework/modules/dbutils-starter/src/test/java/group/idealworld/dew/core/dbutils/DbutilsTest.java b/framework/modules/dbutils-starter/src/test/java/group/idealworld/dew/core/dbutils/DbutilsTest.java new file mode 100644 index 00000000..688bc069 --- /dev/null +++ b/framework/modules/dbutils-starter/src/test/java/group/idealworld/dew/core/dbutils/DbutilsTest.java @@ -0,0 +1,263 @@ +/* + * Copyright 2021. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package group.idealworld.dew.core.dbutils; + +import group.idealworld.dew.core.dbutils.dto.Meta; +import group.idealworld.dew.core.dbutils.dto.Page; +import lombok.Data; +import org.junit.Assert; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.SpringBootTest; +import java.io.IOException; +import java.math.BigDecimal; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.*; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicInteger; + + +@SpringBootApplication +@SpringBootTest +public class DbutilsTest { + + private static final Logger log = LoggerFactory.getLogger(DbutilsTest.class); + + @Autowired + private DewDB dewDB; + + @Test + public void testCreateAndUpdate() throws SQLException, SQLException { + Map fields = new HashMap<>(); + fields.put("id", "long"); + fields.put("name", "String"); + fields.put("age", "Int"); + fields.put("height1", "Float"); + fields.put("height2", "Double"); + fields.put("createTime", "Date"); + fields.put("asset", "BigDecimal"); + fields.put("addr", "String"); + fields.put("enable", "Boolean"); + fields.put("txt", "text"); + dewDB.createTableIfNotExist("test", "测试表", fields, new HashMap() {{ + put("name", "姓名"); + put("age", "年龄"); + }}, new ArrayList() {{ + add("name"); + }}, new ArrayList() {{ + add("name"); + }}, "id"); + Map values = new HashMap<>(); + values.put("id", 100); + values.put("name", "gudaoxuri"); + values.put("age", 29); + values.put("height1", 1.1); + values.put("height2", 1.1d); + values.put("asset", new BigDecimal("2.343")); + values.put("enable", true); + values.put("addr", "浙江杭州"); + // values.put("createTime", new java.sql.Date()); + values.put("txt", "浙江杭州"); + dewDB.insert("test", values); + values.put("name", "孤岛旭日"); + dewDB.modify("test", "id", 100, values); + Map res = dewDB.getByPk("test", "id", 100); + Assert.assertEquals("孤岛旭日", res.get("name")); + Assert.assertEquals(29, res.get("age")); + Assert.assertEquals(1.1f, res.get("height1")); + Assert.assertEquals(1.1d, res.get("height2")); + Assert.assertEquals("浙江杭州", res.get("addr")); + Assert.assertEquals("浙江杭州", res.get("txt")); + dewDB.delete("test", "id", 100); + Assert.assertNull(dewDB.getByPk("test", "id", 100)); + } + + @Test + public void testMeta() throws Exception { + testCreateTable(dewDB); + List metas = dewDB.getMetaData("tuser"); + Assert.assertEquals("id", metas.get(0).getLabel()); + Meta meta = dewDB.getMetaData("tuser", "name"); + Assert.assertEquals("name", meta.getLabel()); + testDropTable(dewDB); + } + + @Test + public void testFlow() throws SQLException, IOException { + testCreateTable(dewDB); + dewDB.update("insert into tuser (id,name,password,age,asset,enable) values ( ? , ? , ? , ? , ? , ? )", + 1, "张三", "123", 22, 2333.22, true); + dewDB.batch("insert into tuser (id,name,password,age,asset,enable) values ( ? , ? , ? , ? , ? , ? )", new Object[][]{ + {2, "李四", "123", 22, 2333.22, true}, + {3, "王五1", "123", 22, 2333.22, false}, + {4, "王五2", "123", 22, 2333.22, false}, + {5, "王五3", "123", 20, 2333.22, false} + }); + // get + Assert.assertEquals(1, dewDB.get("select * from tuser where id = ?", 1).get("id")); + // count + Assert.assertEquals(5, dewDB.count("select * from tuser")); + // find + Assert.assertEquals(4, dewDB.find("select * from tuser where age = ?", 22).size()); + // page + Page> pageResult = dewDB.page("select * from tuser", 1, 2); + Assert.assertEquals(5, pageResult.getRecordTotal()); + Assert.assertEquals(3, pageResult.getPageTotal()); + // get + User user = dewDB.get("select * from tuser where id = ? ", User.class, 1); + Assert.assertEquals(1, user.getId()); + // find + List users = dewDB.find("select * from tuser where age = ?", User.class, 22); + Assert.assertEquals(4, users.size()); + + testDropTable(dewDB); + } + + @Test + public void testPool() throws Exception { + testCreateTable(dewDB); + dewDB.update("insert into tuser (id,name,password,age,asset,enable) values ( ? , ? , ? , ? , ? , ? )", + 1, "张三", "123", 22, 2333.22, true); + dewDB.batch("insert into tuser (id,name,password,age,asset,enable) values ( ? , ? , ? , ? , ? , ? )", new Object[][]{ + {2, "李四", "123", 22, 2333.22, true}, + {3, "王五1", "123", 22, 2333.22, false}, + {4, "王五2", "123", 22, 2333.22, false}, + {5, "王五3", "123", 20, 2333.22, false} + }); + final CountDownLatch watch = new CountDownLatch(10000); + final AtomicInteger count = new AtomicInteger(0); + for (int i = 0; i < 100; i++) { + new Thread(() -> { + for (int i1 = 0; i1 < 100; i1++) { + try { + log.debug(">>>>>>>>>>>>>>" + count.incrementAndGet()); + watch.countDown(); + // find + Assert.assertEquals(4, dewDB.find("select * from tuser where age = ?", 22).size()); + // page + Page> pageResult = dewDB.page("select * from tuser", 1, 2); + Assert.assertEquals(5, pageResult.getRecordTotal()); + Assert.assertEquals(3, pageResult.getPageTotal()); + } catch (SQLException e) { + e.printStackTrace(); + } + } + }).start(); + } + watch.await(); + testDropTable(dewDB); + } + + + @Test + public void testTransaction() throws SQLException { + testCreateTable(dewDB); + //rollback test + dewDB.open(); + dewDB.update("insert into tuser (id,name,password,age,asset,enable) values ( ? , ? , ? , ? , ? , ? )", + 1, "张三", "123", 22, 2333.22, true); + dewDB.rollback(); + Assert.assertEquals(0, dewDB.count("select * from tuser")); + + //error test + dewDB.open(); + dewDB.update("insert into tuser (id,name,password,age,asset,enable) values ( ? , ? , ? , ? , ? , ? )", + 1, "张三", "123", 22, 2333.22, true); + //has error + try { + dewDB.update("insert into tuser (id,name,password,age,asset,enable) values ( ? , ? , ? , ? , ? , ? )", + 1, "张三", "123", 22, 2333.22); + dewDB.commit(); + } catch (SQLException e) { + log.warn("[DewDBUtils]Has Error!"); + } + Assert.assertEquals(0, dewDB.count("select * from tuser")); + + //commit test + dewDB.open(); + dewDB.update("insert into tuser (id,name,password,age,asset,enable) values ( ? , ? , ? , ? , ? , ? )", + 1, "张三", "123", 22, 2333.22, true); + dewDB.commit(); + Assert.assertEquals(1, dewDB.count("select * from tuser")); + + testDropTable(dewDB); + } + + @Test + public void testDataType() throws Exception { + DewDB db = DewDBUtils.use("default"); + db.ddl("create table datatype(" + + "id int not null," + + "name varchar(255)," + + "dt date," + + "dt2 datetime," + + "ts timestamp," + + "age int," + + "primary key(id)" + + ")"); + Date now = new Date(); + db.insert("datatype", new HashMap() { + { + put("id", 1); + put("name", "测试"); + put("age", 1); + put("dt", now); + put("dt2", now); + put("ts", new Timestamp(now.getTime())); + } + }); + List result = db.find("select * from datatype", DataTypeTest.class); + Assert.assertEquals(1, result.size()); + Assert.assertEquals(1L, result.get(0).getId().longValue()); + Assert.assertEquals("测试", result.get(0).getName()); + Assert.assertEquals(1, result.get(0).getAge().longValue()); + Assert.assertEquals(now.getTime(), result.get(0).getTs().getTime()); + Assert.assertEquals(now.getDay(), result.get(0).getDt().getDay()); + Assert.assertEquals(now.getTime(), result.get(0).getDt2().getTime()); + } + + @Data + public static class DataTypeTest { + private Long id; + private String name; + private Date dt; + private Date dt2; + private Timestamp ts; + private Long age; + } + + private void testCreateTable(DewDB db) throws SQLException { + db.ddl("create table tuser(" + + "id int not null," + + "name varchar(255)," + + "password varchar(255)," + + "age int," + + "asset decimal," + + "enable boolean," + + "primary key(id)" + + ")"); + } + + private void testDropTable(DewDB db) throws SQLException { + db.ddl("drop table tuser"); + } + +} diff --git a/framework/modules/dbutils-starter/src/test/java/group/idealworld/dew/core/dbutils/User.java b/framework/modules/dbutils-starter/src/test/java/group/idealworld/dew/core/dbutils/User.java new file mode 100644 index 00000000..780252f4 --- /dev/null +++ b/framework/modules/dbutils-starter/src/test/java/group/idealworld/dew/core/dbutils/User.java @@ -0,0 +1,38 @@ +/* + * Copyright 2020. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package group.idealworld.dew.core.dbutils; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +public class User { + + private long id; + private String name; + private String password; + private int age; + private float height1; + private double height2; + private Date createTime; + private BigDecimal asset; + private String txt; + private boolean enable; + +} diff --git a/framework/modules/hi-starter/pom.xml b/framework/modules/hi-starter/pom.xml new file mode 100644 index 00000000..eb60bc12 --- /dev/null +++ b/framework/modules/hi-starter/pom.xml @@ -0,0 +1,52 @@ + + + + build-framework + group.idealworld.dew + 3.0.0-Beta3 + ../../pom.xml + + 4.0.0 + + hi-starter + + + org.testng + testng + RELEASE + compile + + + org.springframework + spring-context + 5.0.8.RELEASE + compile + + + org.springframework.boot + spring-boot + 2.6.0 + compile + + + jakarta.annotation + jakarta.annotation-api + 1.3.5 + compile + + + group.idealworld.dew + cluster-common + 3.0.0-Beta3 + compile + + + + + 11 + 11 + + + \ No newline at end of file diff --git a/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/Hi.java b/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/Hi.java new file mode 100644 index 00000000..300c015f --- /dev/null +++ b/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/Hi.java @@ -0,0 +1,30 @@ +package group.idealworld.dew.core.hi; + + +import group.idealworld.dew.core.hi.api.cluster.HiClusterAPI; +import group.idealworld.dew.core.hi.process.HiBuilder; + +/** + * Hi标签入口. + * + * @author gudaoxuri + */ +public class Hi { + + /** + * 初始化Hi标签. + * + * @param basicPackage Hi标签扫描的基础包 + */ + public static void init(String basicPackage) { + HiBuilder.build(basicPackage); + } + + /** + * 集群操作集合. + */ + @group.idealworld.dew.core.hi.process.Hi + public static HiClusterAPI cluster; + + +} diff --git a/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/api/cluster/HiClusterAPI.java b/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/api/cluster/HiClusterAPI.java new file mode 100644 index 00000000..cfa6641a --- /dev/null +++ b/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/api/cluster/HiClusterAPI.java @@ -0,0 +1,28 @@ +package group.idealworld.dew.core.hi.api.cluster; + + +import group.idealworld.dew.core.cluster.ClusterMQ; +import group.idealworld.dew.core.hi.process.Hi; + +import java.util.UUID; + +/** + * 集群操作集合. + * + * @author gudaoxuri + */ +public class HiClusterAPI { + + /** + * The constant instanceId. + */ + protected static String instanceId = UUID.randomUUID().toString().replace("-", ""); + + /** + * MQ服务. + */ + @Hi + public ClusterMQ mq; + + +} diff --git a/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/exception/RTAPINotFoundException.java b/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/exception/RTAPINotFoundException.java new file mode 100644 index 00000000..019a42f1 --- /dev/null +++ b/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/exception/RTAPINotFoundException.java @@ -0,0 +1,55 @@ +package group.idealworld.dew.core.hi.exception; + +/** + * The type Rt service not found exception. + * + * @author gudaoxuri + */ +public class RTAPINotFoundException extends RuntimeException { + + /** + * Instantiates a new Rt exception. + */ + public RTAPINotFoundException() { + } + + /** + * Instantiates a new Rt exception. + * + * @param message the message + */ + public RTAPINotFoundException(String message) { + super(message); + } + + /** + * Instantiates a new Rt exception. + * + * @param message the message + * @param cause the cause + */ + public RTAPINotFoundException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Instantiates a new Rt exception. + * + * @param cause the cause + */ + public RTAPINotFoundException(Throwable cause) { + super(cause); + } + + /** + * Instantiates a new Rt exception. + * + * @param message the message + * @param cause the cause + * @param enableSuppression the enable suppression + * @param writableStackTrace the writable stack trace + */ + public RTAPINotFoundException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/exception/RTException.java b/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/exception/RTException.java new file mode 100644 index 00000000..d7c47618 --- /dev/null +++ b/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/exception/RTException.java @@ -0,0 +1,55 @@ +package group.idealworld.dew.core.hi.exception; + +/** + * The type Rt exception. + * + * @author gudaoxuri + */ +public class RTException extends RuntimeException { + + /** + * Instantiates a new Rt exception. + */ + public RTException() { + } + + /** + * Instantiates a new Rt exception. + * + * @param message the message + */ + public RTException(String message) { + super(message); + } + + /** + * Instantiates a new Rt exception. + * + * @param message the message + * @param cause the cause + */ + public RTException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Instantiates a new Rt exception. + * + * @param cause the cause + */ + public RTException(Throwable cause) { + super(cause); + } + + /** + * Instantiates a new Rt exception. + * + * @param message the message + * @param cause the cause + * @param enableSuppression the enable suppression + * @param writableStackTrace the writable stack trace + */ + public RTException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/exception/RTGeneralSecurityException.java b/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/exception/RTGeneralSecurityException.java new file mode 100644 index 00000000..991005dc --- /dev/null +++ b/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/exception/RTGeneralSecurityException.java @@ -0,0 +1,55 @@ +package group.idealworld.dew.core.hi.exception; + +/** + * The type Rt general security exception. + * + * @author gudaoxuri + */ +public class RTGeneralSecurityException extends RTException { + + /** + * Instantiates a new Rt general security exception. + */ + public RTGeneralSecurityException() { + } + + /** + * Instantiates a new Rt general security exception. + * + * @param message the message + */ + public RTGeneralSecurityException(String message) { + super(message); + } + + /** + * Instantiates a new Rt general security exception. + * + * @param message the message + * @param cause the cause + */ + public RTGeneralSecurityException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Instantiates a new Rt general security exception. + * + * @param cause the cause + */ + public RTGeneralSecurityException(Throwable cause) { + super(cause); + } + + /** + * Instantiates a new Rt general security exception. + * + * @param message the message + * @param cause the cause + * @param enableSuppression the enable suppression + * @param writableStackTrace the writable stack trace + */ + public RTGeneralSecurityException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/exception/RTIOException.java b/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/exception/RTIOException.java new file mode 100644 index 00000000..efcfac44 --- /dev/null +++ b/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/exception/RTIOException.java @@ -0,0 +1,55 @@ +package group.idealworld.dew.core.hi.exception; + +/** + * The type Runtime IO exception. + * + * @author gudaoxuri + */ +public class RTIOException extends RTException { + + /** + * Instantiates a new Rtio exception. + */ + public RTIOException() { + } + + /** + * Instantiates a new Rtio exception. + * + * @param message the message + */ + public RTIOException(String message) { + super(message); + } + + /** + * Instantiates a new Rtio exception. + * + * @param message the message + * @param cause the cause + */ + public RTIOException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Instantiates a new Rtio exception. + * + * @param cause the cause + */ + public RTIOException(Throwable cause) { + super(cause); + } + + /** + * Instantiates a new Rtio exception. + * + * @param message the message + * @param cause the cause + * @param enableSuppression the enable suppression + * @param writableStackTrace the writable stack trace + */ + public RTIOException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/exception/RTReflectiveOperationException.java b/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/exception/RTReflectiveOperationException.java new file mode 100644 index 00000000..b0a8aa1e --- /dev/null +++ b/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/exception/RTReflectiveOperationException.java @@ -0,0 +1,56 @@ +package group.idealworld.dew.core.hi.exception; + +/** + * The type Rt reflective operation exception. + * + * @author gudaoxuri + */ +public class RTReflectiveOperationException extends RTException { + + + /** + * Instantiates a new Rt reflective operation exception. + */ + public RTReflectiveOperationException() { + } + + /** + * Instantiates a new Rt reflective operation exception. + * + * @param message the message + */ + public RTReflectiveOperationException(String message) { + super(message); + } + + /** + * Instantiates a new Rt reflective operation exception. + * + * @param message the message + * @param cause the cause + */ + public RTReflectiveOperationException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Instantiates a new Rt reflective operation exception. + * + * @param cause the cause + */ + public RTReflectiveOperationException(Throwable cause) { + super(cause); + } + + /** + * Instantiates a new Rt reflective operation exception. + * + * @param message the message + * @param cause the cause + * @param enableSuppression the enable suppression + * @param writableStackTrace the writable stack trace + */ + public RTReflectiveOperationException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/exception/RTScriptException.java b/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/exception/RTScriptException.java new file mode 100644 index 00000000..01157a16 --- /dev/null +++ b/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/exception/RTScriptException.java @@ -0,0 +1,55 @@ +package group.idealworld.dew.core.hi.exception; + +/** + * The type Rt script exception. + * + * @author gudaoxuri + */ +public class RTScriptException extends RTException { + + /** + * Instantiates a new Rt script exception. + */ + public RTScriptException() { + } + + /** + * Instantiates a new Rt script exception. + * + * @param message the message + */ + public RTScriptException(String message) { + super(message); + } + + /** + * Instantiates a new Rt script exception. + * + * @param message the message + * @param cause the cause + */ + public RTScriptException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Instantiates a new Rt script exception. + * + * @param cause the cause + */ + public RTScriptException(Throwable cause) { + super(cause); + } + + /** + * Instantiates a new Rt script exception. + * + * @param message the message + * @param cause the cause + * @param enableSuppression the enable suppression + * @param writableStackTrace the writable stack trace + */ + public RTScriptException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/exception/RTUnsupportedEncodingException.java b/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/exception/RTUnsupportedEncodingException.java new file mode 100644 index 00000000..d99ecf11 --- /dev/null +++ b/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/exception/RTUnsupportedEncodingException.java @@ -0,0 +1,55 @@ +package group.idealworld.dew.core.hi.exception; + +/** + * The type Rt unsupported encoding exception. + * + * @author gudaoxuri + */ +public class RTUnsupportedEncodingException extends RTException { + + /** + * Instantiates a new Rt unsupported encoding exception. + */ + public RTUnsupportedEncodingException() { + } + + /** + * Instantiates a new Rt unsupported encoding exception. + * + * @param message the message + */ + public RTUnsupportedEncodingException(String message) { + super(message); + } + + /** + * Instantiates a new Rt unsupported encoding exception. + * + * @param message the message + * @param cause the cause + */ + public RTUnsupportedEncodingException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Instantiates a new Rt unsupported encoding exception. + * + * @param cause the cause + */ + public RTUnsupportedEncodingException(Throwable cause) { + super(cause); + } + + /** + * Instantiates a new Rt unsupported encoding exception. + * + * @param message the message + * @param cause the cause + * @param enableSuppression the enable suppression + * @param writableStackTrace the writable stack trace + */ + public RTUnsupportedEncodingException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/ext/HiAutoConfiguration.java b/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/ext/HiAutoConfiguration.java new file mode 100644 index 00000000..5932b2b7 --- /dev/null +++ b/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/ext/HiAutoConfiguration.java @@ -0,0 +1,32 @@ +package group.idealworld.dew.core.hi.ext; + +import group.idealworld.dew.core.hi.Hi; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; + + +/** + * The type Hi auto configuration. + * + * @author gudaoxuri + */ +@Configuration +@ComponentScan(basePackageClasses = {Hi.class}) +public class HiAutoConfiguration { + + @Autowired + private HiConfig hiConfig; + @Autowired + private ApplicationContext injectApplicationContext; + + @PostConstruct + private void init() { + SpringBeanDetector.applicationContext = injectApplicationContext; + Hi.init(hiConfig.getBasicPackage()); + } + +} diff --git a/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/ext/HiConfig.java b/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/ext/HiConfig.java new file mode 100644 index 00000000..1760f461 --- /dev/null +++ b/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/ext/HiConfig.java @@ -0,0 +1,34 @@ +package group.idealworld.dew.core.hi.ext; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * The type Hi config. + * + * @author gudaoxuri + */ +@Component +@ConfigurationProperties(prefix = "hive.hi") +public class HiConfig { + + private String basicPackage = "cn.com"; + + /** + * Gets basic package. + * + * @return the basic package + */ + public String getBasicPackage() { + return basicPackage; + } + + /** + * Sets basic package. + * + * @param basicPackage the basic package + */ + public void setBasicPackage(String basicPackage) { + this.basicPackage = basicPackage; + } +} diff --git a/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/ext/SpringBeanDetector.java b/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/ext/SpringBeanDetector.java new file mode 100644 index 00000000..f1bb427c --- /dev/null +++ b/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/ext/SpringBeanDetector.java @@ -0,0 +1,35 @@ +package group.idealworld.dew.core.hi.ext; + + + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Configuration; + +import java.util.Optional; + +/** + * Spring Bean探测器. + * + * @author gudaoxuri + */ +@Configuration +public class SpringBeanDetector { + + protected static ApplicationContext applicationContext; + + /** + * 获取一个Bean实例. + * + * @param clazz Bean对应的类 + * @return Bean实例 + */ + public static Optional getBean(Class clazz) { + try { + return Optional.of(applicationContext.getBean(clazz)); + } catch (BeansException beansException) { + return Optional.empty(); + } + } + +} diff --git a/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/helper/ScanHelper.java b/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/helper/ScanHelper.java new file mode 100644 index 00000000..42501e62 --- /dev/null +++ b/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/helper/ScanHelper.java @@ -0,0 +1,113 @@ +/* + * Copyright 2020. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package group.idealworld.dew.core.hi.helper; + + +import group.idealworld.dew.core.hi.exception.RTException; +import java.io.File; +import java.io.IOException; +import java.net.JarURLConnection; +import java.net.URL; +import java.net.URLDecoder; +import java.util.Enumeration; +import java.util.function.Consumer; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +/** + * Java扫描操作. + * + * @author gudaoxuri + */ +public class ScanHelper { + + /** + * 扫描获取指定包下的JVM类并执行指定操作. + * + * @param basePackage 要扫描的根包名 + */ + public static void scan(String basePackage, Consumer> executeFun) { + String packageDir = basePackage.replace('.', '/'); + try { + Enumeration urls = Thread.currentThread().getContextClassLoader().getResources(packageDir); + while (urls.hasMoreElements()) { + URL url = urls.nextElement(); + switch (url.getProtocol()) { + case "file": + findAndAddClassesByFile( + basePackage, new File(URLDecoder.decode(url.getFile(), "UTF-8")), executeFun); + break; + case "jar": + findAndAddClassesByJar( + ((JarURLConnection) url.openConnection()).getJarFile(), packageDir, executeFun); + break; + default: + break; + } + } + } catch (IOException e) { + throw new RTException(e); + } + } + + private static void findAndAddClassesByFile(String currentPackage, File currentFile, Consumer> executeFun) { + if (currentFile.exists() && currentFile.isDirectory()) { + File[] files = currentFile.listFiles(file -> file.isDirectory() || file.getName().endsWith(".class")); + for (File file : files) { + if (file.isDirectory()) { + findAndAddClassesByFile(currentPackage + "." + file.getName(), file, executeFun); + } else { + String className = file.getName().substring(0, file.getName().length() - 6); + try { + Class clazz = Thread.currentThread().getContextClassLoader().loadClass(currentPackage + '.' + className); + executeFun.accept(clazz); + } catch (Throwable e) { + // Ignore NoClassDefFoundError when class extends/implements some not import class. + } + } + } + } + } + + private static void findAndAddClassesByJar(JarFile jar, String currentPath, Consumer> executeFun) { + Enumeration entries = jar.entries(); + JarEntry jarEntry; + String jarName; + while (entries.hasMoreElements()) { + jarEntry = entries.nextElement(); + jarName = jarEntry.getName(); + if (jarName.charAt(0) == '/') { + jarName = jarName.substring(1); + } + if (jarName.startsWith(currentPath)) { + int idx = jarName.lastIndexOf('/'); + if (jarName.endsWith(".class") + && !jarEntry.isDirectory()) { + String className = jarName.substring(jarName.lastIndexOf('/') + 1, + jarName.length() - 6); + try { + Class clazz = Class.forName(jarName.substring(0, idx).replace('/', '.') + '.' + className); + executeFun.accept(clazz); + } catch (Throwable e) { + // Ignore NoClassDefFoundError when class extends/implements some not import class. + } + } + } + } + } + +} diff --git a/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/process/Hi.java b/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/process/Hi.java new file mode 100644 index 00000000..92bdcf49 --- /dev/null +++ b/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/process/Hi.java @@ -0,0 +1,17 @@ +package group.idealworld.dew.core.hi.process; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Hi注解,用于IoC. + * + * @author gudoxuri + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.METHOD}) +public @interface Hi { + +} diff --git a/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/process/HiBuilder.java b/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/process/HiBuilder.java new file mode 100644 index 00000000..764d034b --- /dev/null +++ b/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/process/HiBuilder.java @@ -0,0 +1,311 @@ +package group.idealworld.dew.core.hi.process; + + +import group.idealworld.dew.core.hi.exception.RTAPINotFoundException; +import group.idealworld.dew.core.hi.exception.RTReflectiveOperationException; +import group.idealworld.dew.core.hi.ext.SpringBeanDetector; +import group.idealworld.dew.core.hi.helper.ScanHelper; + +import java.lang.reflect.*; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * Hi Builder. + * + * Lightweight and custom IoC operation + * to find {@code Hi.class} annotation and automatically assemble the corresponding implementation classes. + * + * @author gudaoxuri + */ +public class HiBuilder { + + // Mapping of API classes to implementation classes + // key : API classes (interface or abstract class) + // value : corresponding implementation class + private static final Map, Class> METHOD_CLAZZ_IMPL = new HashMap<>(); + // Spring environment dependency identification + private static boolean enableSpringSupport = hasDependency("org.springframework.context.ApplicationContext"); + + /** + * Build. + * + * @param basicPackage the basic package + */ + public static void build(String basicPackage) { + Class hiClass = Hi.class; + Class hiRefClass = HiRef.class; + // Hi Info + // key : API classes (interface or abstract class) + Map, Set> hiInfo = new HashMap<>(); + // Scan Hi annotations + ScanHelper.scan(basicPackage, clazz -> { + Arrays.stream(clazz.getDeclaredFields()) + .filter(field -> field.isAnnotationPresent(hiClass)) + .forEach(field -> { + if (!hiInfo.containsKey(field.getType())) { + hiInfo.put(field.getType(), new HashSet<>()); + } + hiInfo.get(field.getType()).add(new HiScanInfo(clazz, field.getType(), field)); + }); + Arrays.stream(clazz.getDeclaredMethods()) + .filter(method -> method.isAnnotationPresent(hiClass)) + .forEach(method -> { + if (!hiInfo.containsKey(method.getReturnType())) { + hiInfo.put(method.getReturnType(), new HashSet<>()); + } + hiInfo.get(method.getReturnType()).add(new HiScanInfo(clazz, method.getReturnType(), method)); + }); + }); + // Find the implementation class of each object + ScanHelper.scan(basicPackage, clazz -> { + if (clazz.isInterface() || clazz.isEnum() + || Modifier.isAbstract(clazz.getModifiers()) + || clazz.isAnnotation() || clazz.isArray()) { + // Ignore interfaces, enumerations, abstract classes, etc. + return; + } + hiInfo.forEach((fieldOrMethodTypeClazz, scanInfoSet) -> { + if (fieldOrMethodTypeClazz.isAssignableFrom(clazz)) { + // Find a matching implementation + scanInfoSet.forEach(scanInfo -> scanInfo.implClazz = clazz); + } else if (clazz.isAnnotationPresent(hiRefClass) + && clazz.getAnnotation(hiRefClass).value().isAssignableFrom(fieldOrMethodTypeClazz)) { + // Find the implementation referenced via @HiRef + scanInfoSet.forEach(scanInfo -> { + scanInfo.implClazz = clazz; + scanInfo.isRefMode = true; + }); + } + }); + }); + // Building from Hi + Set hiScanInfoSet = hiInfo.values().stream() + .flatMap(Collection::stream) + // Exclude not implemented (package isn‘t imported) functions + .filter(hiScanInfo -> hiScanInfo.implClazz != null) + .collect(Collectors.toSet()); + doBuilding(hiScanInfoSet, group.idealworld.dew.core.hi.Hi.class, null); + } + + private static void doBuilding(Set hiScanInfoSet, Class clazz, Object clazzInst) { + hiScanInfoSet.stream() + .filter(hiScanInfo -> hiScanInfo.belongClazz == clazz) + .forEach(hiScanInfo -> { + // Field building + hiScanInfo.fieldOpt.ifPresent(field -> { + try { + Object newInst = null; + // Using spring bean first + Optional instOpt = getBean(hiScanInfo.fieldOrMethodTypeClazz); + if (instOpt.isPresent()) { + newInst = instOpt.get(); + } + if (newInst == null) { + if (hiScanInfo.implClazz.getName().contains("$") && + !Modifier.isStatic(hiScanInfo.implClazz.getModifiers())) { + // Internal non-static class + Constructor constructor = hiScanInfo.implClazz.getDeclaredConstructor(clazzInst.getClass()); + constructor.setAccessible(true); + newInst = constructor.newInstance(clazzInst); + } else { + Constructor constructor = hiScanInfo.implClazz.getDeclaredConstructor(); + constructor.setAccessible(true); + newInst = constructor.newInstance(); + } + } + if (!hiScanInfo.isRefMode) { + // Normal implementation mode + if (Modifier.isStatic(field.getModifiers())) { + field.set(null, newInst); + } else { + field.set(clazzInst, newInst); + } + } else { + // @HiRef reference mode + if (Modifier.isStatic(field.getModifiers())) { + field.set(null, proxyInst(field.getType(), newInst)); + } else { + field.set(clazzInst, proxyInst(field.getType(), newInst)); + } + } + doBuilding(hiScanInfoSet, hiScanInfo.fieldOrMethodTypeClazz, newInst); + } catch (IllegalAccessException | InstantiationException + | NoSuchMethodException | InvocationTargetException e) { + throw new RTReflectiveOperationException(e); + } + }); + // Method building + hiScanInfo.methodOpt.ifPresent(method -> + METHOD_CLAZZ_IMPL.put(hiScanInfo.fieldOrMethodTypeClazz, hiScanInfo.implClazz)); + }); + } + + /** + * Instantiates a new proxy objects. + * + * @param interfaceClazz Interface type + * @param implObj Implementing Class Object + * @return Proxy Object + */ + private static Object proxyInst(Class interfaceClazz, Object implObj) { + Class implClazz = implObj.getClass(); + Arrays.asList(interfaceClazz.getMethods()) + .forEach(interfaceMethod -> { + try { + implClazz.getMethod(interfaceMethod.getName(), interfaceMethod.getParameterTypes()); + } catch (NoSuchMethodException e) { + throw new RTReflectiveOperationException( + "Can't find the public [" + interfaceMethod.getName() + "] method needed " + + "for the [" + interfaceClazz.getName() + "] interface " + + "in the [" + implClazz.getName() + "] implementation class", e); + } + } + ); + Object newProxyInstance = Proxy.newProxyInstance( + interfaceClazz.getClassLoader(), + new Class[]{interfaceClazz}, + new DynamicProxy(implObj)); + return newProxyInstance; + } + + /** + * Dynamic proxy. + * + * Automatically match implementations with the same interface method name + parameter type. + */ + private static class DynamicProxy implements InvocationHandler { + + private static final Map METHOD_MAPPING = new ConcurrentHashMap<>(); + + private Object implObj; + private Class implClazz; + + public DynamicProxy(final Object implObj) { + this.implObj = implObj; + implClazz = implObj.getClass(); + } + + @Override + public Object invoke(Object proxy, Method interfaceMethod, Object[] args) throws Throwable { + // Cache + if (!METHOD_MAPPING.containsKey(interfaceMethod)) { + METHOD_MAPPING.put( + interfaceMethod, + implClazz.getMethod(interfaceMethod.getName(), interfaceMethod.getParameterTypes()) + ); + } + return METHOD_MAPPING.get(interfaceMethod).invoke(implObj, args); + } + + } + + /** + * Gets bean. + * + * @param clazz the class + * @return the bean + */ + public static Optional getBean(Class clazz) { + if (!enableSpringSupport) { + return Optional.empty(); + } + return SpringBeanDetector.getBean(clazz); + } + + /** + * New inst. + * + * @param the type parameter + * @param clazz the class + * @param parameters the parameters + * @return the e + */ + public static E newInst(Class clazz, Object... parameters) { + if (!METHOD_CLAZZ_IMPL.containsKey(clazz)) { + throw new RTAPINotFoundException("This method isn't implemented (please check dependencies)"); + } + try { + List> parameterClasses = Arrays.stream(parameters) + .map(Object::getClass) + .collect(Collectors.toList()); + Constructor constructor = METHOD_CLAZZ_IMPL.get(clazz) + .getDeclaredConstructor(parameterClasses.toArray(new Class[parameterClasses.size()])); + constructor.setAccessible(true); + return (E) constructor.newInstance(parameters); + } catch (IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException e) { + throw new RTReflectiveOperationException(e); + } + } + + private static boolean hasDependency(String clazz) { + try { + Class.forName(clazz); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } + + private static class HiScanInfo { + + /** + * The Belong class. + */ + public Class belongClazz; + /** + * The Field or method type class. + */ + public Class fieldOrMethodTypeClazz; + /** + * The Field opt. + */ + public Optional fieldOpt; + /** + * The Method opt. + */ + public Optional methodOpt; + /** + * The Impl clazz. + */ + public Class implClazz; + + /** + * The Is ref mode. + */ + public boolean isRefMode; + + /** + * Instantiates a new Hi scan info. + * + * @param belongClazz the belong class + * @param fieldOrMethodTypeClazz the field or method type class + * @param field the field + */ + public HiScanInfo(Class belongClazz, Class fieldOrMethodTypeClazz, + Field field) { + this.belongClazz = belongClazz; + this.fieldOrMethodTypeClazz = fieldOrMethodTypeClazz; + this.fieldOpt = Optional.of(field); + this.methodOpt = Optional.empty(); + } + + /** + * Instantiates a new Hi scan info. + * + * @param belongClazz the belong class + * @param fieldOrMethodTypeClazz the field or method type class + * @param method the method + */ + public HiScanInfo(Class belongClazz, Class fieldOrMethodTypeClazz, + Method method) { + this.belongClazz = belongClazz; + this.fieldOrMethodTypeClazz = fieldOrMethodTypeClazz; + this.fieldOpt = Optional.empty(); + this.methodOpt = Optional.of(method); + } + + } + +} diff --git a/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/process/HiRef.java b/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/process/HiRef.java new file mode 100644 index 00000000..fd596f79 --- /dev/null +++ b/framework/modules/hi-starter/src/main/java/group/idealworld/dew/core/hi/process/HiRef.java @@ -0,0 +1,24 @@ +package group.idealworld.dew.core.hi.process; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Hi 引用注解,用于弱实现,多用于静态方法处理. + * + * @author gudoxuri + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface HiRef { + + /** + * 引用接口. + * + * @return the string + */ + Class value(); + +} diff --git a/framework/modules/test-starter/src/main/resources/broker.conf b/framework/modules/test-starter/src/main/resources/broker.conf new file mode 100644 index 00000000..29fca1b2 --- /dev/null +++ b/framework/modules/test-starter/src/main/resources/broker.conf @@ -0,0 +1,8 @@ +brokerClusterName = DefaultCluster +brokerName = broker-a +brokerId = 0 +deleteWhen = 04 +fileReservedTime = 48 +brokerRole = ASYNC_MASTER +flushDiskType = ASYNC_FLUSH +brokerIP1 = 127.0.0.1 \ No newline at end of file From ef331298c05bbf444cdd92dd71933ee3d6e80fc4 Mon Sep 17 00:00:00 2001 From: nipeixuan <34534268+nipeixuan@users.noreply.github.com> Date: Wed, 12 Jan 2022 15:37:56 +0800 Subject: [PATCH 02/11] =?UTF-8?q?=E5=8A=A8=E6=80=81=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=BA=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- framework/modules/dbutils-starter/pom.xml | 17 ----------------- framework/modules/parent-starter/pom.xml | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/framework/modules/dbutils-starter/pom.xml b/framework/modules/dbutils-starter/pom.xml index 77383e72..0be50260 100644 --- a/framework/modules/dbutils-starter/pom.xml +++ b/framework/modules/dbutils-starter/pom.xml @@ -32,23 +32,6 @@ jar - 11 - 11 - UTF-8 - ${project.build.sourceEncoding} - ${project.build.sourceEncoding} - ${java.version} - ${java.version} - ${java.version} - 1.7 - 1.1.23 - 8.0.21 - 42.2.14 - 1.4.200 - 1.26 - 1.18.12 - 4.13 - 1.7.30 diff --git a/framework/modules/parent-starter/pom.xml b/framework/modules/parent-starter/pom.xml index 93b80a17..39f9863a 100644 --- a/framework/modules/parent-starter/pom.xml +++ b/framework/modules/parent-starter/pom.xml @@ -75,6 +75,24 @@ 2.3.0 1.3.3 1.18 + + 11 + 11 + UTF-8 + ${project.build.sourceEncoding} + ${project.build.sourceEncoding} + ${java.version} + ${java.version} + ${java.version} + 1.7 + 1.1.23 + 8.0.21 + 42.2.14 + 1.4.200 + 1.26 + 1.18.12 + 4.13 + 1.7.30 From 3bc3ac80b18266f1d4ee64c95f6468ecdd647475 Mon Sep 17 00:00:00 2001 From: nipeixuan <34534268+nipeixuan@users.noreply.github.com> Date: Wed, 12 Jan 2022 15:52:14 +0800 Subject: [PATCH 03/11] =?UTF-8?q?=E5=8A=A8=E6=80=81=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=BA=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dbutils/DbutilsAutoConfiguration.java | 2 +- .../dew/core/dbutils/DewDBUtils.java | 11 +++++----- .../dew/core/dbutils/process/DSLoader.java | 21 +------------------ 3 files changed, 7 insertions(+), 27 deletions(-) diff --git a/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/DbutilsAutoConfiguration.java b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/DbutilsAutoConfiguration.java index 13284fde..2703fc41 100644 --- a/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/DbutilsAutoConfiguration.java +++ b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/DbutilsAutoConfiguration.java @@ -15,7 +15,7 @@ public class DbutilsAutoConfiguration { @Bean public DewDB dewDB(DBUtilsConfig DBUtilsConfig) { - DewDBUtils.init2(DBUtilsConfig); + DewDBUtils.init(DBUtilsConfig); DewDB dewDB = DewDBUtils.use("default"); return dewDB; } diff --git a/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/DewDBUtils.java b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/DewDBUtils.java index e40a152a..362e2b9d 100644 --- a/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/DewDBUtils.java +++ b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/DewDBUtils.java @@ -27,17 +27,16 @@ */ public class DewDBUtils { + + /** * 初始化数据源. * - * @param configPath 配置文件路径 + * @param dbUtilsConfig 加载配置信息 */ - public static void init(String configPath) { - DSLoader.load(configPath); - } - public static void init2(DBUtilsConfig dbUtilsConfig){ - DSLoader.load2(dbUtilsConfig); + public static void init(DBUtilsConfig dbUtilsConfig){ + DSLoader.load(dbUtilsConfig); } /** diff --git a/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/process/DSLoader.java b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/process/DSLoader.java index 40050414..45bde1d4 100644 --- a/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/process/DSLoader.java +++ b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/process/DSLoader.java @@ -48,27 +48,8 @@ public static DSInfo getDSInfo(String dsCode) { return MULTI_DS.get(dsCode); } - public static void load(String configPath) { - MULTI_DS.clear(); - log.info("[DewDBUtils]Load DS from {}", configPath); - File configFile = new File(configPath); - if (!configFile.exists() || configFile.isDirectory()) { - throw new RuntimeException("Config file [" + configPath + "] NOT exist"); - } - DBUtilsConfig dbUtilsConfig; - try { - String content = new String(Files.readAllBytes(configFile.toPath()), StandardCharsets.UTF_8); - dbUtilsConfig = YamlHelper.toObject(DBUtilsConfig.class, content); - } catch (IOException e) { - throw new RuntimeException("Config file [" + configPath + "] parse error", e); - } - loadDS(dbUtilsConfig.getDs()); - if (dbUtilsConfig.getDynamicDS().getEnabled()) { - loadDynamicDS(dbUtilsConfig.getDynamicDS().getDsCode(), dbUtilsConfig.getDynamicDS().getFetchSql()); - } - } - public static void load2(DBUtilsConfig dbUtilsConfig) { + public static void load(DBUtilsConfig dbUtilsConfig) { loadDS(dbUtilsConfig.getDs()); if (dbUtilsConfig.getDynamicDS().getEnabled()) { loadDynamicDS(dbUtilsConfig.getDynamicDS().getDsCode(), dbUtilsConfig.getDynamicDS().getFetchSql()); From ccfca4d4bd4eb4651bf6f8408ecdf986e2e00329 Mon Sep 17 00:00:00 2001 From: nipeixuan <34534268+nipeixuan@users.noreply.github.com> Date: Thu, 13 Jan 2022 09:53:51 +0800 Subject: [PATCH 04/11] =?UTF-8?q?=E5=8A=A8=E6=80=81=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=BA=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- framework/pom.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/framework/pom.xml b/framework/pom.xml index e1bbd052..40277229 100644 --- a/framework/pom.xml +++ b/framework/pom.xml @@ -46,6 +46,8 @@ modules/test-starter assists/sdkgen-maven-plugin modules/cluster-rocket + modules/hi-starter + modules/dbutils-starter From 826e69643853ba2410920ecb54e935d866cc5384 Mon Sep 17 00:00:00 2001 From: nipeixuan <34534268+nipeixuan@users.noreply.github.com> Date: Mon, 24 Jan 2022 09:19:00 +0800 Subject: [PATCH 05/11] add quartz module --- .../main/resources/META-INF/spring.factories | 3 + framework/modules/quartz-starter/pom.xml | 62 ++++++ .../core/quartz/DruidConnectionProvider.java | 177 ++++++++++++++++++ .../dew/core/quartz/QuartzConfig.java | 57 ++++++ .../dew/core/quartz/QuartzConfigDTO.java | 80 ++++++++ .../dew/core/quartz/QuartzOperation.java | 79 ++++++++ .../dew/core/quartz/QuartzTemplate.java | 139 ++++++++++++++ .../core/quartz/SimpleSchedulerListener.java | 99 ++++++++++ .../dew/core/quartz/TestController.java | 126 +++++++++++++ .../main/resources/META-INF/spring.factories | 2 + .../src/main/resources/application.properties | 8 + .../src/main/resources/quartz.properties | 26 +++ .../dew/core/quartz/TfCommandJob.java | 24 +++ .../dew/core/quartz/quartzTest.java | 57 ++++++ 14 files changed, 939 insertions(+) create mode 100644 framework/modules/hi-starter/src/main/resources/META-INF/spring.factories create mode 100644 framework/modules/quartz-starter/pom.xml create mode 100644 framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/DruidConnectionProvider.java create mode 100644 framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/QuartzConfig.java create mode 100644 framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/QuartzConfigDTO.java create mode 100644 framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/QuartzOperation.java create mode 100644 framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/QuartzTemplate.java create mode 100644 framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/SimpleSchedulerListener.java create mode 100644 framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/TestController.java create mode 100644 framework/modules/quartz-starter/src/main/resources/META-INF/spring.factories create mode 100644 framework/modules/quartz-starter/src/main/resources/application.properties create mode 100644 framework/modules/quartz-starter/src/main/resources/quartz.properties create mode 100644 framework/modules/quartz-starter/src/test/java/group/idealworld/dew/core/quartz/TfCommandJob.java create mode 100644 framework/modules/quartz-starter/src/test/java/group/idealworld/dew/core/quartz/quartzTest.java diff --git a/framework/modules/hi-starter/src/main/resources/META-INF/spring.factories b/framework/modules/hi-starter/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..4f76b288 --- /dev/null +++ b/framework/modules/hi-starter/src/main/resources/META-INF/spring.factories @@ -0,0 +1,3 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +group.idealworld.dew.core.hi.ext.HiAutoConfiguration,\ +group.idealworld.dew.core.hi.ext.SpringBeanDetector diff --git a/framework/modules/quartz-starter/pom.xml b/framework/modules/quartz-starter/pom.xml new file mode 100644 index 00000000..4939243c --- /dev/null +++ b/framework/modules/quartz-starter/pom.xml @@ -0,0 +1,62 @@ + + + + + 4.0.0 + + group.idealworld.dew + parent-starter + 3.0.0-Beta3 + ../parent-starter + + + quartz-starter + 1.1.4 Dew quartz- + Dew 集群 quartz调度 + jar + + + + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-data-jpa + + + mysql + mysql-connector-java + + + com.alibaba + druid-spring-boot-starter + 1.1.17 + + + org.springframework.boot + spring-boot-starter-quartz + + + + diff --git a/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/DruidConnectionProvider.java b/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/DruidConnectionProvider.java new file mode 100644 index 00000000..a0f87386 --- /dev/null +++ b/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/DruidConnectionProvider.java @@ -0,0 +1,177 @@ +package group.idealworld.dew.core.quartz; + +import com.alibaba.druid.pool.DruidDataSource; +import org.quartz.SchedulerException; +import org.quartz.utils.ConnectionProvider; + +import java.sql.Connection; +import java.sql.SQLException; + +public class DruidConnectionProvider implements ConnectionProvider { + + /** + * 常量配置,与quartz.properties文件的key保持一致(去掉前缀),同时提供set方法,Quartz框架自动注入值。 + * @return + * @throws SQLException + */ + + //JDBC驱动 + public String driver; + //JDBC连接串 + public String URL; + //数据库用户名 + public String user; + //数据库用户密码 + public String password; + //数据库最大连接数 + public int maxConnection; + //数据库SQL查询每次连接返回执行到连接池,以确保它仍然是有效的。 + public String validationQuery; + + private boolean validateOnCheckout; + + private int idleConnectionValidationSeconds; + + public String maxCachedStatementsPerConnection; + + private String discardIdleConnectionsSeconds; + + public static final int DEFAULT_DB_MAX_CONNECTIONS = 10; + + public static final int DEFAULT_DB_MAX_CACHED_STATEMENTS_PER_CONNECTION = 120; + + //Druid连接池 + private DruidDataSource datasource; + + @Override + public Connection getConnection() throws SQLException { + return datasource.getConnection(); + } + + @Override + public void shutdown() throws SQLException { + datasource.close(); + } + + @Override + public void initialize() throws SQLException { + if (this.URL == null) { + throw new SQLException("DBPool could not be created: DB URL cannot be null"); + } + + if (this.driver == null) { + throw new SQLException("DBPool driver could not be created: DB driver class name cannot be null!"); + } + + if (this.maxConnection < 0) { + throw new SQLException("DBPool maxConnectins could not be created: Max connections must be greater than zero!"); + } + + datasource = new DruidDataSource(); + try{ + datasource.setDriverClassName(this.driver); + } catch (Exception e) { + try { + throw new SchedulerException("Problem setting driver class name on datasource: " + e.getMessage(), e); + } catch (SchedulerException e1) { + } + } + + datasource.setUrl(this.URL); + datasource.setUsername(this.user); + datasource.setPassword(this.password); + datasource.setMaxActive(this.maxConnection); + datasource.setMinIdle(1); + datasource.setMaxWait(0); + datasource.setMaxPoolPreparedStatementPerConnectionSize(DEFAULT_DB_MAX_CONNECTIONS); + + if (this.validationQuery != null) { + datasource.setValidationQuery(this.validationQuery); + if(!this.validateOnCheckout) + datasource.setTestOnReturn(true); + else + datasource.setTestOnBorrow(true); + datasource.setValidationQueryTimeout(this.idleConnectionValidationSeconds); + } + } + + public String getDriver() { + return driver; + } + + public void setDriver(String driver) { + this.driver = driver; + } + + public String getURL() { + return URL; + } + + public void setURL(String URL) { + this.URL = URL; + } + + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public int getMaxConnection() { + return maxConnection; + } + + public void setMaxConnection(int maxConnection) { + this.maxConnection = maxConnection; + } + + public String getValidationQuery() { + return validationQuery; + } + + public void setValidationQuery(String validationQuery) { + this.validationQuery = validationQuery; + } + + public boolean isValidateOnCheckout() { + return validateOnCheckout; + } + + public void setValidateOnCheckout(boolean validateOnCheckout) { + this.validateOnCheckout = validateOnCheckout; + } + + public int getIdleConnectionValidationSeconds() { + return idleConnectionValidationSeconds; + } + + public void setIdleConnectionValidationSeconds(int idleConnectionValidationSeconds) { + this.idleConnectionValidationSeconds = idleConnectionValidationSeconds; + } + + public DruidDataSource getDatasource() { + return datasource; + } + + public void setDatasource(DruidDataSource datasource) { + this.datasource = datasource; + } + + public String getDiscardIdleConnectionsSeconds() { + return discardIdleConnectionsSeconds; + } + + public void setDiscardIdleConnectionsSeconds(String discardIdleConnectionsSeconds) { + this.discardIdleConnectionsSeconds = discardIdleConnectionsSeconds; + } +} \ No newline at end of file diff --git a/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/QuartzConfig.java b/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/QuartzConfig.java new file mode 100644 index 00000000..a701cb03 --- /dev/null +++ b/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/QuartzConfig.java @@ -0,0 +1,57 @@ +package group.idealworld.dew.core.quartz; + +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.springframework.beans.factory.config.PropertiesFactoryBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; +import org.springframework.scheduling.quartz.SchedulerFactoryBean; + +import java.io.IOException; + +@Configuration +public class QuartzConfig { + + @Bean + public SchedulerFactoryBean schedulerFactoryBean() throws IOException { + //获取配置属性 + PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean(); + propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties")); + //在quartz.properties中的属性被读取并注入后再初始化对象 + propertiesFactoryBean.afterPropertiesSet(); + SchedulerFactoryBean factory = new SchedulerFactoryBean(); + factory.setQuartzProperties(propertiesFactoryBean.getObject()); +// factory.setJobFactory(jobFactory);//支持在JOB实例中注入其他的业务对象 + factory.setApplicationContextSchedulerContextKey("applicationContextKey"); + factory.setWaitForJobsToCompleteOnShutdown(true); + factory.setOverwriteExistingJobs(false); + factory.setStartupDelay(10); + + return factory; + } + + @Bean + public QuartzTemplate quartzTemplate(Scheduler scheduler){ + return new QuartzTemplate(scheduler); + } + + @Bean + public SimpleSchedulerListener simpleSchedulerListener(){ + return new SimpleSchedulerListener(); + } + + /** + * 通过SchedulerFactoryBean获取Scheduler的实例 + * @return + * @throws IOException + * @throws SchedulerException + */ + @Bean(name = "scheduler") + public Scheduler scheduler(SimpleSchedulerListener simpleSchedulerListener) throws IOException, SchedulerException { + Scheduler scheduler = schedulerFactoryBean().getScheduler(); + scheduler.getListenerManager().addSchedulerListener(simpleSchedulerListener); + scheduler.start(); + return scheduler; + } +} \ No newline at end of file diff --git a/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/QuartzConfigDTO.java b/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/QuartzConfigDTO.java new file mode 100644 index 00000000..1df82f01 --- /dev/null +++ b/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/QuartzConfigDTO.java @@ -0,0 +1,80 @@ +package group.idealworld.dew.core.quartz; + +import java.io.Serializable; +import java.util.Map; + +public class QuartzConfigDTO implements Serializable { + + + private static final long serialVersionUID = 1L; + /** + * 任务名称 + */ + private String jobName; + + /** + * 任务所属组 + */ + private String groupName; + + /** + * 任务执行类 + */ + private String jobClass; + + /** + * 任务调度时间表达式 + */ + private String cronExpression; + + /** + * 附加参数 + */ + private Map param; + + + public String getJobName() { + return jobName; + } + + public QuartzConfigDTO setJobName(String jobName) { + this.jobName = jobName; + return this; + } + + public String getGroupName() { + return groupName; + } + + public QuartzConfigDTO setGroupName(String groupName) { + this.groupName = groupName; + return this; + } + + public String getJobClass() { + return jobClass; + } + + public QuartzConfigDTO setJobClass(String jobClass) { + this.jobClass = jobClass; + return this; + } + + public String getCronExpression() { + return cronExpression; + } + + public QuartzConfigDTO setCronExpression(String cronExpression) { + this.cronExpression = cronExpression; + return this; + } + + public Map getParam() { + return param; + } + + public QuartzConfigDTO setParam(Map param) { + this.param = param; + return this; + } +} diff --git a/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/QuartzOperation.java b/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/QuartzOperation.java new file mode 100644 index 00000000..b8fb82b7 --- /dev/null +++ b/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/QuartzOperation.java @@ -0,0 +1,79 @@ +package group.idealworld.dew.core.quartz; + +import java.util.Map; + +public interface QuartzOperation { + /** + * 添加任务可以传参数 + * + * @param clazzName + * @param jobName + * @param groupName + * @param cronExp + * @param param + */ + void addJob(String clazzName, String jobName, String groupName, String cronExp, Map param); + + /** + * 暂停任务 + * + * @param jobName + * @param groupName + */ + void pauseJob(String jobName, String groupName); + + /** + * 恢复任务 + * + * @param jobName + * @param groupName + */ + void resumeJob(String jobName, String groupName); + + /** + * 立即运行一次定时任务 + * + * @param jobName + * @param groupName + */ + void runOnce(String jobName, String groupName); + + /** + * 更新任务 + * + * @param jobName + * @param groupName + * @param cronExp + * @param param + */ + void updateJob(String jobName, String groupName, String cronExp, Map param); + + /** + * 删除任务 + * + * @param jobName + * @param groupName + */ + void deleteJob(String jobName, String groupName); + + /** + * 启动所有任务 + */ + void startAllJobs(); + + /** + * 暂停所有任务 + */ + void pauseAllJobs(); + + /** + * 恢复所有任务 + */ + void resumeAllJobs(); + + /** + * 关闭所有任务 + */ + void shutdownAllJobs(); + +} diff --git a/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/QuartzTemplate.java b/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/QuartzTemplate.java new file mode 100644 index 00000000..aff2feb9 --- /dev/null +++ b/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/QuartzTemplate.java @@ -0,0 +1,139 @@ +package group.idealworld.dew.core.quartz; + +import org.quartz.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; + +public class QuartzTemplate implements QuartzOperation { + + private static final Logger log = LoggerFactory.getLogger(QuartzTemplate.class); + + private Scheduler scheduler; + + public QuartzTemplate(Scheduler scheduler){ + this.scheduler = scheduler; + } + + @Override + public void addJob(String clazzName, String jobName, String groupName, String cronExp, Map param) { + try { +// scheduler.start(); + //构建job信息 + Class jobClass = (Class) Class.forName(clazzName); + JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, groupName).build(); + //表达式调度构建器(即任务执行的时间) + CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExp); + //按新的cronExpression表达式构建一个新的trigger + CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, groupName).withSchedule(scheduleBuilder).build(); + //获得JobDataMap,写入数据 + if (param != null) { + trigger.getJobDataMap().putAll(param); + } + scheduler.scheduleJob(jobDetail, trigger); + } catch (Exception e) { + log.error("创建任务失败", e); + } + } + + @Override + public void pauseJob(String jobName, String groupName) { + try { + scheduler.pauseJob(JobKey.jobKey(jobName, groupName)); + } catch (SchedulerException e) { + log.error("暂停任务失败", e); + } + } + + @Override + public void resumeJob(String jobName, String groupName) { + try { + scheduler.resumeJob(JobKey.jobKey(jobName, groupName)); + } catch (SchedulerException e) { + log.error("恢复任务失败", e); + } + } + + @Override + public void runOnce(String jobName, String groupName) { + try { + scheduler.triggerJob(JobKey.jobKey(jobName, groupName)); + } catch (SchedulerException e) { + log.error("立即运行一次定时任务失败", e); + } + } + + @Override + public void updateJob(String jobName, String groupName, String cronExp, Map param) { + try { + TriggerKey triggerKey = TriggerKey.triggerKey(jobName, groupName); + CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); + if (cronExp != null) { + // 表达式调度构建器 + CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExp); + // 按新的cronExpression表达式重新构建trigger + trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build(); + } + //修改map + if (param != null) { + trigger.getJobDataMap().putAll(param); + } + // 按新的trigger重新设置job执行 + scheduler.rescheduleJob(triggerKey, trigger); + } catch (Exception e) { + log.error("更新任务失败", e); + } + } + + @Override + public void deleteJob(String jobName, String groupName) { + try { + //暂停、移除、删除 + scheduler.pauseTrigger(TriggerKey.triggerKey(jobName, groupName)); + scheduler.unscheduleJob(TriggerKey.triggerKey(jobName, groupName)); + scheduler.deleteJob(JobKey.jobKey(jobName, groupName)); + } catch (Exception e) { + log.error("删除任务失败", e); + } + } + + @Override + public void startAllJobs() { + try { + scheduler.start(); + } catch (Exception e) { + log.error("开启所有的任务失败", e); + } + } + + @Override + public void pauseAllJobs() { + try { + scheduler.pauseAll(); + } catch (Exception e) { + log.error("暂停所有任务失败", e); + } + } + + @Override + public void resumeAllJobs() { + try { + scheduler.resumeAll(); + } catch (Exception e) { + log.error("恢复所有任务失败", e); + } + } + + @Override + public void shutdownAllJobs() { + try { + + if (!scheduler.isShutdown()) { + scheduler.shutdown(true); + } + } catch (Exception e) { + log.error("关闭所有的任务失败", e); + } + } +} diff --git a/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/SimpleSchedulerListener.java b/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/SimpleSchedulerListener.java new file mode 100644 index 00000000..fbd622d8 --- /dev/null +++ b/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/SimpleSchedulerListener.java @@ -0,0 +1,99 @@ +package group.idealworld.dew.core.quartz; + +import org.quartz.*; +import org.quartz.listeners.SchedulerListenerSupport; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SimpleSchedulerListener extends SchedulerListenerSupport { + + private static final Logger log = LoggerFactory.getLogger(SimpleSchedulerListener.class); + + @Override + public void jobScheduled(Trigger trigger) { + log.info("create job" + trigger.getJobKey()); + } + + @Override + public void jobUnscheduled(TriggerKey triggerKey) { + } + + @Override + public void triggerFinalized(Trigger trigger) { + } + + @Override + public void triggerPaused(TriggerKey triggerKey) { + } + + @Override + public void triggersPaused(String triggerGroup) { + } + + @Override + public void triggerResumed(TriggerKey triggerKey) { + } + + @Override + public void triggersResumed(String triggerGroup) { + } + + @Override + public void jobAdded(JobDetail jobDetail) { + log.info("job add: " + jobDetail.getJobClass()); + } + + @Override + public void jobDeleted(JobKey jobKey) { + log.info("delete job: " + jobKey); + } + + @Override + public void jobPaused(JobKey jobKey) { + log.info("job paused: " + jobKey); + } + + @Override + public void jobsPaused(String jobGroup) { + log.info("jobGroup paused: " + jobGroup); + } + + @Override + public void jobResumed(JobKey jobKey) { + log.info("job resumed: " + jobKey); + } + + @Override + public void jobsResumed(String jobGroup) { + log.info("jobGroup resumed: " + jobGroup); + } + + @Override + public void schedulerError(String msg, SchedulerException cause) { + cause.printStackTrace(); + } + + @Override + public void schedulerInStandbyMode() { + } + + @Override + public void schedulerStarted() { + } + + @Override + public void schedulerStarting() { + } + + @Override + public void schedulerShutdown() { + } + + @Override + public void schedulerShuttingdown() { + } + + @Override + public void schedulingDataCleared() { + } +} diff --git a/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/TestController.java b/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/TestController.java new file mode 100644 index 00000000..0a15d1b0 --- /dev/null +++ b/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/TestController.java @@ -0,0 +1,126 @@ +//package group.idealworld.dew.core.quartz; +// +//import org.slf4j.Logger; +//import org.slf4j.LoggerFactory; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.http.HttpStatus; +//import org.springframework.web.bind.annotation.RequestBody; +//import org.springframework.web.bind.annotation.RequestMapping; +//import org.springframework.web.bind.annotation.RestController; +// +//@RestController +//@RequestMapping("/test") +//public class TestController { +// +// private static final Logger log = LoggerFactory.getLogger(TestController.class); +// +// @Autowired +// private QuartzOperation quartzJobService; +// +// /** +// * 添加新任务 +// * @param configDTO +// * @return +// */ +// @RequestMapping("/addJob") +// public Object addJob(@RequestBody QuartzConfigDTO configDTO) { +// quartzJobService.addJob(configDTO.getJobClass(), configDTO.getJobName(), configDTO.getGroupName(), configDTO.getCronExpression(), configDTO.getParam()); +// return HttpStatus.OK; +// } +// +// /** +// * 暂停任务 +// * @param configDTO +// * @return +// */ +// @RequestMapping("/pauseJob") +// public Object pauseJob(@RequestBody QuartzConfigDTO configDTO) { +// quartzJobService.pauseJob(configDTO.getJobName(), configDTO.getGroupName()); +// return HttpStatus.OK; +// } +// +// /** +// * 恢复任务 +// * @param configDTO +// * @return +// */ +// @RequestMapping("/resumeJob") +// public Object resumeJob(@RequestBody QuartzConfigDTO configDTO) { +// quartzJobService.resumeJob(configDTO.getJobName(), configDTO.getGroupName()); +// return HttpStatus.OK; +// } +// +// /** +// * 立即运行一次定时任务 +// * @param configDTO +// * @return +// */ +// @RequestMapping("/runOnce") +// public Object runOnce(@RequestBody QuartzConfigDTO configDTO) { +// quartzJobService.runOnce(configDTO.getJobName(), configDTO.getGroupName()); +// return HttpStatus.OK; +// } +// +// /** +// * 更新任务 +// * @param configDTO +// * @return +// */ +// @RequestMapping("/updateJob") +// public Object updateJob(@RequestBody QuartzConfigDTO configDTO) { +// quartzJobService.updateJob(configDTO.getJobName(), configDTO.getGroupName(), configDTO.getCronExpression(), configDTO.getParam()); +// return HttpStatus.OK; +// } +// +// /** +// * 删除任务 +// * @param configDTO +// * @return +// */ +// @RequestMapping("/deleteJob") +// public Object deleteJob(@RequestBody QuartzConfigDTO configDTO) { +// quartzJobService.deleteJob(configDTO.getJobName(), configDTO.getGroupName()); +// return HttpStatus.OK; +// } +// +// /** +// * 启动所有任务 +// * @return +// */ +// @RequestMapping("/startAllJobs") +// public Object startAllJobs() { +// quartzJobService.startAllJobs(); +// return HttpStatus.OK; +// } +// +// /** +// * 暂停所有任务 +// * @return +// */ +// @RequestMapping("/pauseAllJobs") +// public Object pauseAllJobs() { +// quartzJobService.pauseAllJobs(); +// return HttpStatus.OK; +// } +// +// /** +// * 恢复所有任务 +// * @return +// */ +// @RequestMapping("/resumeAllJobs") +// public Object resumeAllJobs() { +// quartzJobService.resumeAllJobs(); +// return HttpStatus.OK; +// } +// +// /** +// * 关闭所有任务 +// * @return +// */ +// @RequestMapping("/shutdownAllJobs") +// public Object shutdownAllJobs() { +// quartzJobService.shutdownAllJobs(); +// return HttpStatus.OK; +// } +// +//} diff --git a/framework/modules/quartz-starter/src/main/resources/META-INF/spring.factories b/framework/modules/quartz-starter/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..7f3c95b4 --- /dev/null +++ b/framework/modules/quartz-starter/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + group.idealworld.dew.core.quartz.QuartzConfig diff --git a/framework/modules/quartz-starter/src/main/resources/application.properties b/framework/modules/quartz-starter/src/main/resources/application.properties new file mode 100644 index 00000000..cbc23113 --- /dev/null +++ b/framework/modules/quartz-starter/src/main/resources/application.properties @@ -0,0 +1,8 @@ +spring.application.name=springboot-quartz-001 +server.port=8080 + +spring.datasource.url=jdbc:mysql://127.0.0.1:3306/quartz?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true +spring.datasource.username=root +spring.datasource.password=123456 +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver + diff --git a/framework/modules/quartz-starter/src/main/resources/quartz.properties b/framework/modules/quartz-starter/src/main/resources/quartz.properties new file mode 100644 index 00000000..49b7a810 --- /dev/null +++ b/framework/modules/quartz-starter/src/main/resources/quartz.properties @@ -0,0 +1,26 @@ +org.quartz.scheduler.instanceName=SsmScheduler +org.quartz.scheduler.instanceId=AUTO +org.quartz.scheduler.wrapJobExecutionInUserTransaction=false + +org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool +org.quartz.threadPool.threadCount=10 +org.quartz.threadPool.threadPriority=5 +org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true +#org.quartz.threadPool.makeThreadsDaemons=true + +#JobDataMaps????String?? +org.quartz.jobStore.useProperties=true +org.quartz.jobStore.tablePrefix=QRTZ_ +org.quartz.jobStore.misfireThreshold=60000 +org.quartz.jobStore.isClustered=true +org.quartz.jobStore.clusterCheckinInterval=2000 +org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX +org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate +org.quartz.jobStore.dataSource=qzDS +org.quartz.dataSource.qzDS.connectionProvider.class=group.idealworld.dew.core.quartz.DruidConnectionProvider +org.quartz.dataSource.qzDS.driver=com.mysql.cj.jdbc.Driver +org.quartz.dataSource.qzDS.URL=jdbc:mysql://127.0.0.1:3306/quartz?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true +org.quartz.dataSource.qzDS.user=root +org.quartz.dataSource.qzDS.password=123456 +org.quartz.dataSource.qzDS.maxConnection=5 +org.quartz.dataSource.qzDS.validationQuery=select 0 from dual \ No newline at end of file diff --git a/framework/modules/quartz-starter/src/test/java/group/idealworld/dew/core/quartz/TfCommandJob.java b/framework/modules/quartz-starter/src/test/java/group/idealworld/dew/core/quartz/TfCommandJob.java new file mode 100644 index 00000000..9f22bfcd --- /dev/null +++ b/framework/modules/quartz-starter/src/test/java/group/idealworld/dew/core/quartz/TfCommandJob.java @@ -0,0 +1,24 @@ +package group.idealworld.dew.core.quartz; + +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.SchedulerException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.text.SimpleDateFormat; +import java.util.Date; + +public class TfCommandJob implements Job { + + private static final Logger log = LoggerFactory.getLogger(TfCommandJob.class); + + @Override + public void execute(JobExecutionContext context) { + try { + log.info(context.getScheduler().getSchedulerInstanceId() + "--" + new SimpleDateFormat("YYYY-MM-dd HH:mm:ss").format(new Date())); + } catch (SchedulerException e) { + log.error("任务执行失败",e); + } + } +} \ No newline at end of file diff --git a/framework/modules/quartz-starter/src/test/java/group/idealworld/dew/core/quartz/quartzTest.java b/framework/modules/quartz-starter/src/test/java/group/idealworld/dew/core/quartz/quartzTest.java new file mode 100644 index 00000000..0f5c8b6e --- /dev/null +++ b/framework/modules/quartz-starter/src/test/java/group/idealworld/dew/core/quartz/quartzTest.java @@ -0,0 +1,57 @@ +package group.idealworld.dew.core.quartz; + + +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.quartz.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootApplication +@SpringBootTest +public class quartzTest { + + private static final Logger log = LoggerFactory.getLogger(quartzTest.class); + + @Autowired + QuartzTemplate template; + + @Test + public void test() throws InterruptedException { + testQuartz(); + testRunOnce(); + startAllJob(); + deleteJob(); + } + + + public void testQuartz() throws InterruptedException { + template.addJob("group.idealworld.dew.core.quartz.TfCommandJob", "myJob", "default", "0/1 * * * * ?", null); + Thread.sleep(3000); + template.pauseJob("myJob","default"); + Thread.sleep(3000); + template.resumeJob("myJob","default"); + Thread.sleep(3000); + } + + + public void testRunOnce() throws InterruptedException { + template.runOnce("myJob","default"); + } + + + public void startAllJob() throws InterruptedException { + template.startAllJobs(); + Thread.sleep(5000); + } + + + public void deleteJob(){ + log.info("deleteJob"); + template.deleteJob("myJob","default"); + } + +} From eae0b2c3f827bd80d0a8c7ec26651618dd2bd831 Mon Sep 17 00:00:00 2001 From: nipeixuan <34534268+nipeixuan@users.noreply.github.com> Date: Tue, 25 Jan 2022 09:18:40 +0800 Subject: [PATCH 06/11] add quartz module --- framework/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/pom.xml b/framework/pom.xml index 40277229..19754bd5 100644 --- a/framework/pom.xml +++ b/framework/pom.xml @@ -48,6 +48,7 @@ modules/cluster-rocket modules/hi-starter modules/dbutils-starter + modules/quartz-starter From 62ccf85cb728730a1e6f1f18c199ac54a1b307b6 Mon Sep 17 00:00:00 2001 From: nipeixuan <34534268+nipeixuan@users.noreply.github.com> Date: Sun, 20 Feb 2022 20:35:44 +0800 Subject: [PATCH 07/11] ERROR CODE --- .../dew/errorInfo/AbstractException.java | 28 +++++++ .../dew/errorInfo/ClientErrorCodeMark.java | 56 ++++++++++++++ .../dew/errorInfo/ClientException.java | 59 ++++++++++++++ .../idealworld/dew/errorInfo/ErrorInfo.java | 16 ++++ .../dew/errorInfo/ModuleErrorCodeMark.java | 61 +++++++++++++++ .../dew/errorInfo/ModuleException.java | 56 ++++++++++++++ .../dew/errorInfo/SystemErrorCodeMark.java | 77 +++++++++++++++++++ .../dew/errorInfo/SystemException.java | 65 ++++++++++++++++ .../dew/core/quartz/ClusterTest.java | 55 +++++++++++++ 9 files changed, 473 insertions(+) create mode 100644 framework/modules/boot-starter/src/main/java/group/idealworld/dew/errorInfo/AbstractException.java create mode 100644 framework/modules/boot-starter/src/main/java/group/idealworld/dew/errorInfo/ClientErrorCodeMark.java create mode 100644 framework/modules/boot-starter/src/main/java/group/idealworld/dew/errorInfo/ClientException.java create mode 100644 framework/modules/boot-starter/src/main/java/group/idealworld/dew/errorInfo/ErrorInfo.java create mode 100644 framework/modules/boot-starter/src/main/java/group/idealworld/dew/errorInfo/ModuleErrorCodeMark.java create mode 100644 framework/modules/boot-starter/src/main/java/group/idealworld/dew/errorInfo/ModuleException.java create mode 100644 framework/modules/boot-starter/src/main/java/group/idealworld/dew/errorInfo/SystemErrorCodeMark.java create mode 100644 framework/modules/boot-starter/src/main/java/group/idealworld/dew/errorInfo/SystemException.java create mode 100644 framework/modules/quartz-starter/src/test/java/group/idealworld/dew/core/quartz/ClusterTest.java diff --git a/framework/modules/boot-starter/src/main/java/group/idealworld/dew/errorInfo/AbstractException.java b/framework/modules/boot-starter/src/main/java/group/idealworld/dew/errorInfo/AbstractException.java new file mode 100644 index 00000000..e2a78371 --- /dev/null +++ b/framework/modules/boot-starter/src/main/java/group/idealworld/dew/errorInfo/AbstractException.java @@ -0,0 +1,28 @@ +package group.idealworld.dew.errorInfo; + +/** + * 自定义全局异常. + * @author nipeixuan + */ +public abstract class AbstractException extends RuntimeException implements ErrorInfo { + + public AbstractException() { + super(); + } + + public AbstractException(String message) { + super(message); + } + + public AbstractException(String message, Throwable cause) { + super(message, cause); + } + + public AbstractException(Throwable cause) { + super(cause); + } + + protected AbstractException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} \ No newline at end of file diff --git a/framework/modules/boot-starter/src/main/java/group/idealworld/dew/errorInfo/ClientErrorCodeMark.java b/framework/modules/boot-starter/src/main/java/group/idealworld/dew/errorInfo/ClientErrorCodeMark.java new file mode 100644 index 00000000..27df984f --- /dev/null +++ b/framework/modules/boot-starter/src/main/java/group/idealworld/dew/errorInfo/ClientErrorCodeMark.java @@ -0,0 +1,56 @@ +package group.idealworld.dew.errorInfo; + +import lombok.Getter; + +/** + * 客户端异常标记接口。 + * 用于标记{@link ClientException}对象的错误码参数类型,防止错误码串用. + * @author nipeixuan + */ +public interface ClientErrorCodeMark extends ErrorInfo { + + @Getter + enum Code implements ClientErrorCodeMark { + + PARAMETER_ERROR("errorCode 1", "parameter error"), + + FREQUENTLY("errorCode 2", "the operation is too frequent. Please try again later"), + ; + + Code(String errorCode) { + this.errorCode = errorCode; + } + Code(String errorCode, String errorDesc) { + this.errorCode = errorCode; + this.errorDesc = errorDesc; + } + + /** + * 错误码. + */ + private String errorCode; + /** + * 错误码对应的外部描述信息,该信息是通过错误码自动获取,并且直接返回给调用方. + */ + private String errorDesc; + } + + /** + * 快速构建一个自定义错误描述错误信息对象. + */ + default ClientErrorCodeMark as(String desc) { + ErrorInfo errorInfo = this; + return new ClientErrorCodeMark() { + @Override + public String getErrorCode() { + return errorInfo.getErrorCode(); + } + + @Override + public String getErrorDesc() { + return desc; + } + }; + } + +} \ No newline at end of file diff --git a/framework/modules/boot-starter/src/main/java/group/idealworld/dew/errorInfo/ClientException.java b/framework/modules/boot-starter/src/main/java/group/idealworld/dew/errorInfo/ClientException.java new file mode 100644 index 00000000..39681cc4 --- /dev/null +++ b/framework/modules/boot-starter/src/main/java/group/idealworld/dew/errorInfo/ClientException.java @@ -0,0 +1,59 @@ +package group.idealworld.dew.errorInfo; + +import lombok.Setter; +import org.apache.commons.lang3.StringUtils; + +/** + * @author nipeixuan + */ +public class ClientException extends AbstractException { + + private ErrorInfo errorCode; + /** + * 本地错误描述信息,其优先级高于{@link ErrorInfo}中的errorDesc,即如果该值不为空,则以该值为准 + */ + @Setter + private String errorDesc; + + public ClientException(ClientErrorCodeMark errorCode) { + super(); + this.errorCode = errorCode; + } + + public ClientException(ClientErrorCodeMark errorCode, String message) { + super(message); + this.errorCode = errorCode; + } + + public ClientException(ClientErrorCodeMark errorCode, Throwable cause) { + super(cause); + this.errorCode = errorCode; + } + + public ClientException(ClientErrorCodeMark errorCode, String message, Throwable cause) { + super(message, cause); + this.errorCode = errorCode; + } + + @Override + public String getErrorCode() { + return errorCode.getErrorCode(); + } + + @Override + public String getErrorDesc() { + if (!StringUtils.isEmpty(errorDesc)) { + return errorDesc; + } + return errorCode.getErrorDesc(); + } + + @Override + public String getMessage() { + String message = super.getMessage(); + if (StringUtils.isEmpty(message)) { + message = this.getErrorDesc(); + } + return message; + } +} diff --git a/framework/modules/boot-starter/src/main/java/group/idealworld/dew/errorInfo/ErrorInfo.java b/framework/modules/boot-starter/src/main/java/group/idealworld/dew/errorInfo/ErrorInfo.java new file mode 100644 index 00000000..be06d9d5 --- /dev/null +++ b/framework/modules/boot-starter/src/main/java/group/idealworld/dew/errorInfo/ErrorInfo.java @@ -0,0 +1,16 @@ +package group.idealworld.dew.errorInfo; + +/** + * 公共接口,能获取错误描述和错误码. + * + * @author nipeixuan + */ +public interface ErrorInfo { + + + String getErrorCode(); + + + String getErrorDesc(); + +} \ No newline at end of file diff --git a/framework/modules/boot-starter/src/main/java/group/idealworld/dew/errorInfo/ModuleErrorCodeMark.java b/framework/modules/boot-starter/src/main/java/group/idealworld/dew/errorInfo/ModuleErrorCodeMark.java new file mode 100644 index 00000000..36d33d17 --- /dev/null +++ b/framework/modules/boot-starter/src/main/java/group/idealworld/dew/errorInfo/ModuleErrorCodeMark.java @@ -0,0 +1,61 @@ +package group.idealworld.dew.errorInfo; + +import lombok.Getter; + +public interface ModuleErrorCodeMark extends ErrorInfo{ + + @Getter + enum Code implements ModuleErrorCodeMark { + + HAZELCAST_ERROR("errorCode8", "hazelcast error"), + + MQTT_ERROR("errorCode9", "mqtt error"), + + RABBIT_ERROR("errorCode10", "rabbit error"), + + RADIS_ERROR("errorCode11", "redis error"), + + ROCKET_ERROR("errorCode12", "rocket error"), + + DBUTILS_ERROR("errorCode13", "dbutils error"), + + HBASE_ERROR("errorCode13", "hbase error"), + + ; + + Code(String errorCode) { + this.errorCode = errorCode; + } + Code(String errorCode, String errorDesc) { + this.errorCode = errorCode; + this.errorDesc = errorDesc; + } + + /** + * 错误码. + */ + private String errorCode; + /** + * 错误码对应的外部描述信息,该信息是通过错误码自动获取,并且直接返回给调用方. + */ + private String errorDesc; + } + + /** + * 快速构建一个自定义错误描述错误信息对象. + */ + default ModuleErrorCodeMark as(String desc) { + ErrorInfo errorInfo = this; + return new ModuleErrorCodeMark() { + @Override + public String getErrorCode() { + return errorInfo.getErrorCode(); + } + @Override + public String getErrorDesc() { + return desc; + } + }; + } + +} diff --git a/framework/modules/boot-starter/src/main/java/group/idealworld/dew/errorInfo/ModuleException.java b/framework/modules/boot-starter/src/main/java/group/idealworld/dew/errorInfo/ModuleException.java new file mode 100644 index 00000000..063e3bc2 --- /dev/null +++ b/framework/modules/boot-starter/src/main/java/group/idealworld/dew/errorInfo/ModuleException.java @@ -0,0 +1,56 @@ +package group.idealworld.dew.errorInfo; + +import lombok.Setter; +import org.apache.commons.lang3.StringUtils; + +public class ModuleException extends AbstractException{ + + private ErrorInfo errorCode; + /** + * 本地错误描述信息,其优先级高于{@link ErrorInfo}中的errorDesc,即如果该值不为空,则以该值为准 + */ + @Setter + private String errorDesc; + + public ModuleException(ClientErrorCodeMark errorCode) { + super(); + this.errorCode = errorCode; + } + + public ModuleException(ClientErrorCodeMark errorCode, String message) { + super(message); + this.errorCode = errorCode; + } + + public ModuleException(ClientErrorCodeMark errorCode, Throwable cause) { + super(cause); + this.errorCode = errorCode; + } + + public ModuleException(ClientErrorCodeMark errorCode, String message, Throwable cause) { + super(message, cause); + this.errorCode = errorCode; + } + + @Override + public String getErrorCode() { + return errorCode.getErrorCode(); + } + + @Override + public String getErrorDesc() { + if (!StringUtils.isEmpty(errorDesc)) { + return errorDesc; + } + return errorCode.getErrorDesc(); + } + + @Override + public String getMessage() { + String message = super.getMessage(); + if (StringUtils.isEmpty(message)) { + message = this.getErrorDesc(); + } + return message; + } +} diff --git a/framework/modules/boot-starter/src/main/java/group/idealworld/dew/errorInfo/SystemErrorCodeMark.java b/framework/modules/boot-starter/src/main/java/group/idealworld/dew/errorInfo/SystemErrorCodeMark.java new file mode 100644 index 00000000..1d56fb4c --- /dev/null +++ b/framework/modules/boot-starter/src/main/java/group/idealworld/dew/errorInfo/SystemErrorCodeMark.java @@ -0,0 +1,77 @@ +package group.idealworld.dew.errorInfo; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 标记接口. + * 用于标记{@link SystemException}对象的错误码参数类型,防止错误码串用. + * @author nipeixuan + */ +public interface SystemErrorCodeMark extends ErrorInfo { + + /** + * 我方系统异常错误码枚举 + */ + @Getter + @AllArgsConstructor + enum Code implements SystemErrorCodeMark { + + /** + * 系统错误(本系统). + */ + SYSTEM_ERROR_LOCAL("error Code3", "system error"), + /** + * 系统内部错误(其他服务引起的错误,如通道异常) + */ + SYSTEM_ERROR_OTHER("error Code4", "system busy"), + + // ----------------------------------------------------------------------------------------------------------------- + + /** + * 系统内部错误 + */ + SYS_0001("error code5"), + /** + * API不存在 + */ + SYSTEM_NOT_API("error code6" , "API not exists"), + /** + * API不存在 + */ + HTTP_NOT_SUPPORTED("error code7" , "the request method is not supported"), + ; + + Code(String errorCode) { + this.errorCode = errorCode; + } + + /** + * 错误码. + */ + private String errorCode; + /** + * 错误码对应的外部描述信息,该信息是通过错误码自动获取,并且直接返回给调用方. + */ + private String errorDesc; + } + + /** + * 快速构建一个自定义错误描述错误信息对象. + */ + default SystemErrorCodeMark as(String desc) { + ErrorInfo errorInfo = this; + return new SystemErrorCodeMark() { + @Override + public String getErrorCode() { + return errorInfo.getErrorCode(); + } + + @Override + public String getErrorDesc() { + return desc; + } + }; + } + +} diff --git a/framework/modules/boot-starter/src/main/java/group/idealworld/dew/errorInfo/SystemException.java b/framework/modules/boot-starter/src/main/java/group/idealworld/dew/errorInfo/SystemException.java new file mode 100644 index 00000000..cb162778 --- /dev/null +++ b/framework/modules/boot-starter/src/main/java/group/idealworld/dew/errorInfo/SystemException.java @@ -0,0 +1,65 @@ +package group.idealworld.dew.errorInfo; + +import lombok.Setter; +import org.apache.commons.lang3.StringUtils; + +/** + * + * @author nipeixuan + */ +public class SystemException extends AbstractException { + + private ErrorInfo errorCode; + /** + * 本地错误描述信息,其优先级高于{@link ErrorInfo}中的errorDesc,即如果该值不为空,则以该值为准 + */ + @Setter + private String errorDesc; + + + public SystemException(SystemErrorCodeMark errorCode) { + super(); + this.errorCode = errorCode; + } + + public SystemException(SystemErrorCodeMark errorCode, String message) { + super(message); + this.errorCode = errorCode; + } + + /** + * @param errorCode 枚举的错误码类型,其中包含了错误码和对应的错误码描述信息. + */ + public SystemException(SystemErrorCodeMark errorCode, Throwable cause) { + super(cause); + this.errorCode = errorCode; + } + + + public SystemException(SystemErrorCodeMark errorCode, String message, Throwable cause) { + super(message, cause); + this.errorCode = errorCode; + } + + @Override + public String getErrorCode() { + return errorCode.getErrorCode(); + } + + @Override + public String getErrorDesc() { + if (!StringUtils.isEmpty(errorDesc)) { + return errorDesc; + } + return errorCode.getErrorDesc(); + } + + @Override + public String getMessage() { + String message = super.getMessage(); + if (StringUtils.isEmpty(message)) { + message = this.getErrorDesc(); + } + return message; + } +} diff --git a/framework/modules/quartz-starter/src/test/java/group/idealworld/dew/core/quartz/ClusterTest.java b/framework/modules/quartz-starter/src/test/java/group/idealworld/dew/core/quartz/ClusterTest.java new file mode 100644 index 00000000..273a9f2e --- /dev/null +++ b/framework/modules/quartz-starter/src/test/java/group/idealworld/dew/core/quartz/ClusterTest.java @@ -0,0 +1,55 @@ +/* + * Copyright 2021. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package group.idealworld.dew.core.quartz; + +import group.idealworld.dew.test.MySqlExtension; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.quartz.SchedulerException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.testcontainers.junit.jupiter.Testcontainers; + +/** + * Cluster test. + * + * @author gudaoxuri + */ +@ExtendWith({SpringExtension.class, MySqlExtension.class}) +@ContextConfiguration(initializers = MySqlExtension.Initializer.class) +@SpringBootApplication +@SpringBootTest +@Testcontainers +public class ClusterTest { + + @Autowired + QuartzTemplate template; + + /** + * Test mq. + * + * @throws InterruptedException the interrupted exception + */ + @Test + public void testMQ() throws InterruptedException, SchedulerException { + new QuartzTest().test(); + } + +} From 2d1c1d89e0b47ec4779413ee7881d88de800858a Mon Sep 17 00:00:00 2001 From: nipeixuan <34534268+nipeixuan@users.noreply.github.com> Date: Mon, 21 Feb 2022 17:25:48 +0800 Subject: [PATCH 08/11] master --- .../dew/core/quartz/ClusterTest.java | 55 ------------------- 1 file changed, 55 deletions(-) delete mode 100644 framework/modules/quartz-starter/src/test/java/group/idealworld/dew/core/quartz/ClusterTest.java diff --git a/framework/modules/quartz-starter/src/test/java/group/idealworld/dew/core/quartz/ClusterTest.java b/framework/modules/quartz-starter/src/test/java/group/idealworld/dew/core/quartz/ClusterTest.java deleted file mode 100644 index 273a9f2e..00000000 --- a/framework/modules/quartz-starter/src/test/java/group/idealworld/dew/core/quartz/ClusterTest.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2021. the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package group.idealworld.dew.core.quartz; - -import group.idealworld.dew.test.MySqlExtension; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.quartz.SchedulerException; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.testcontainers.junit.jupiter.Testcontainers; - -/** - * Cluster test. - * - * @author gudaoxuri - */ -@ExtendWith({SpringExtension.class, MySqlExtension.class}) -@ContextConfiguration(initializers = MySqlExtension.Initializer.class) -@SpringBootApplication -@SpringBootTest -@Testcontainers -public class ClusterTest { - - @Autowired - QuartzTemplate template; - - /** - * Test mq. - * - * @throws InterruptedException the interrupted exception - */ - @Test - public void testMQ() throws InterruptedException, SchedulerException { - new QuartzTest().test(); - } - -} From 35a18418c65d9c863e5bbe5615ed5dc2a79894e0 Mon Sep 17 00:00:00 2001 From: nipeixuan <34534268+nipeixuan@users.noreply.github.com> Date: Tue, 22 Feb 2022 19:06:01 +0800 Subject: [PATCH 09/11] add quartz module --- framework/modules/quartz-starter/pom.xml | 9 +- .../core/quartz/DruidConnectionProvider.java | 177 ------------------ .../dew/core/quartz/QuartzConfig.java | 31 +-- .../dew/core/quartz/QuartzOperation.java | 34 ++-- .../dew/core/quartz/QuartzTemplate.java | 41 ++-- .../dew/core/quartz/TestController.java | 126 ------------- .../src/main/resources/application.properties | 8 - .../src/main/resources/quartz.properties | 26 --- .../dew/core/quartz/TfCommandJob.java | 3 +- .../dew/core/quartz/quartzTest.java | 57 +++--- .../idealworld/dew/test/MySqlExtension.java | 14 +- 11 files changed, 83 insertions(+), 443 deletions(-) delete mode 100644 framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/DruidConnectionProvider.java delete mode 100644 framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/TestController.java delete mode 100644 framework/modules/quartz-starter/src/main/resources/application.properties delete mode 100644 framework/modules/quartz-starter/src/main/resources/quartz.properties diff --git a/framework/modules/quartz-starter/pom.xml b/framework/modules/quartz-starter/pom.xml index 4939243c..fa75804f 100644 --- a/framework/modules/quartz-starter/pom.xml +++ b/framework/modules/quartz-starter/pom.xml @@ -48,15 +48,14 @@ mysql mysql-connector-java - - com.alibaba - druid-spring-boot-starter - 1.1.17 - org.springframework.boot spring-boot-starter-quartz + + group.idealworld.dew + test-starter + diff --git a/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/DruidConnectionProvider.java b/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/DruidConnectionProvider.java deleted file mode 100644 index a0f87386..00000000 --- a/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/DruidConnectionProvider.java +++ /dev/null @@ -1,177 +0,0 @@ -package group.idealworld.dew.core.quartz; - -import com.alibaba.druid.pool.DruidDataSource; -import org.quartz.SchedulerException; -import org.quartz.utils.ConnectionProvider; - -import java.sql.Connection; -import java.sql.SQLException; - -public class DruidConnectionProvider implements ConnectionProvider { - - /** - * 常量配置,与quartz.properties文件的key保持一致(去掉前缀),同时提供set方法,Quartz框架自动注入值。 - * @return - * @throws SQLException - */ - - //JDBC驱动 - public String driver; - //JDBC连接串 - public String URL; - //数据库用户名 - public String user; - //数据库用户密码 - public String password; - //数据库最大连接数 - public int maxConnection; - //数据库SQL查询每次连接返回执行到连接池,以确保它仍然是有效的。 - public String validationQuery; - - private boolean validateOnCheckout; - - private int idleConnectionValidationSeconds; - - public String maxCachedStatementsPerConnection; - - private String discardIdleConnectionsSeconds; - - public static final int DEFAULT_DB_MAX_CONNECTIONS = 10; - - public static final int DEFAULT_DB_MAX_CACHED_STATEMENTS_PER_CONNECTION = 120; - - //Druid连接池 - private DruidDataSource datasource; - - @Override - public Connection getConnection() throws SQLException { - return datasource.getConnection(); - } - - @Override - public void shutdown() throws SQLException { - datasource.close(); - } - - @Override - public void initialize() throws SQLException { - if (this.URL == null) { - throw new SQLException("DBPool could not be created: DB URL cannot be null"); - } - - if (this.driver == null) { - throw new SQLException("DBPool driver could not be created: DB driver class name cannot be null!"); - } - - if (this.maxConnection < 0) { - throw new SQLException("DBPool maxConnectins could not be created: Max connections must be greater than zero!"); - } - - datasource = new DruidDataSource(); - try{ - datasource.setDriverClassName(this.driver); - } catch (Exception e) { - try { - throw new SchedulerException("Problem setting driver class name on datasource: " + e.getMessage(), e); - } catch (SchedulerException e1) { - } - } - - datasource.setUrl(this.URL); - datasource.setUsername(this.user); - datasource.setPassword(this.password); - datasource.setMaxActive(this.maxConnection); - datasource.setMinIdle(1); - datasource.setMaxWait(0); - datasource.setMaxPoolPreparedStatementPerConnectionSize(DEFAULT_DB_MAX_CONNECTIONS); - - if (this.validationQuery != null) { - datasource.setValidationQuery(this.validationQuery); - if(!this.validateOnCheckout) - datasource.setTestOnReturn(true); - else - datasource.setTestOnBorrow(true); - datasource.setValidationQueryTimeout(this.idleConnectionValidationSeconds); - } - } - - public String getDriver() { - return driver; - } - - public void setDriver(String driver) { - this.driver = driver; - } - - public String getURL() { - return URL; - } - - public void setURL(String URL) { - this.URL = URL; - } - - public String getUser() { - return user; - } - - public void setUser(String user) { - this.user = user; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public int getMaxConnection() { - return maxConnection; - } - - public void setMaxConnection(int maxConnection) { - this.maxConnection = maxConnection; - } - - public String getValidationQuery() { - return validationQuery; - } - - public void setValidationQuery(String validationQuery) { - this.validationQuery = validationQuery; - } - - public boolean isValidateOnCheckout() { - return validateOnCheckout; - } - - public void setValidateOnCheckout(boolean validateOnCheckout) { - this.validateOnCheckout = validateOnCheckout; - } - - public int getIdleConnectionValidationSeconds() { - return idleConnectionValidationSeconds; - } - - public void setIdleConnectionValidationSeconds(int idleConnectionValidationSeconds) { - this.idleConnectionValidationSeconds = idleConnectionValidationSeconds; - } - - public DruidDataSource getDatasource() { - return datasource; - } - - public void setDatasource(DruidDataSource datasource) { - this.datasource = datasource; - } - - public String getDiscardIdleConnectionsSeconds() { - return discardIdleConnectionsSeconds; - } - - public void setDiscardIdleConnectionsSeconds(String discardIdleConnectionsSeconds) { - this.discardIdleConnectionsSeconds = discardIdleConnectionsSeconds; - } -} \ No newline at end of file diff --git a/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/QuartzConfig.java b/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/QuartzConfig.java index a701cb03..73e0fe39 100644 --- a/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/QuartzConfig.java +++ b/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/QuartzConfig.java @@ -13,23 +13,6 @@ @Configuration public class QuartzConfig { - @Bean - public SchedulerFactoryBean schedulerFactoryBean() throws IOException { - //获取配置属性 - PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean(); - propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties")); - //在quartz.properties中的属性被读取并注入后再初始化对象 - propertiesFactoryBean.afterPropertiesSet(); - SchedulerFactoryBean factory = new SchedulerFactoryBean(); - factory.setQuartzProperties(propertiesFactoryBean.getObject()); -// factory.setJobFactory(jobFactory);//支持在JOB实例中注入其他的业务对象 - factory.setApplicationContextSchedulerContextKey("applicationContextKey"); - factory.setWaitForJobsToCompleteOnShutdown(true); - factory.setOverwriteExistingJobs(false); - factory.setStartupDelay(10); - - return factory; - } @Bean public QuartzTemplate quartzTemplate(Scheduler scheduler){ @@ -41,17 +24,5 @@ public SimpleSchedulerListener simpleSchedulerListener(){ return new SimpleSchedulerListener(); } - /** - * 通过SchedulerFactoryBean获取Scheduler的实例 - * @return - * @throws IOException - * @throws SchedulerException - */ - @Bean(name = "scheduler") - public Scheduler scheduler(SimpleSchedulerListener simpleSchedulerListener) throws IOException, SchedulerException { - Scheduler scheduler = schedulerFactoryBean().getScheduler(); - scheduler.getListenerManager().addSchedulerListener(simpleSchedulerListener); - scheduler.start(); - return scheduler; - } + } \ No newline at end of file diff --git a/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/QuartzOperation.java b/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/QuartzOperation.java index b8fb82b7..cc5ca617 100644 --- a/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/QuartzOperation.java +++ b/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/QuartzOperation.java @@ -1,5 +1,7 @@ package group.idealworld.dew.core.quartz; +import org.quartz.SchedulerException; + import java.util.Map; public interface QuartzOperation { @@ -7,54 +9,48 @@ public interface QuartzOperation { * 添加任务可以传参数 * * @param clazzName - * @param jobName - * @param groupName + * @param jobKey * @param cronExp * @param param */ - void addJob(String clazzName, String jobName, String groupName, String cronExp, Map param); + void addJob(String clazzName, String jobKey, String cronExp, Map param); /** * 暂停任务 * - * @param jobName - * @param groupName + * @param jobKey */ - void pauseJob(String jobName, String groupName); + void pauseJob(String jobKey); /** * 恢复任务 * - * @param jobName - * @param groupName + * @param jobKey */ - void resumeJob(String jobName, String groupName); + void resumeJob(String jobKey); /** * 立即运行一次定时任务 * - * @param jobName - * @param groupName + * @param jobKey */ - void runOnce(String jobName, String groupName); + void runOnce(String jobKey); /** * 更新任务 * - * @param jobName - * @param groupName + * @param jobKey * @param cronExp * @param param */ - void updateJob(String jobName, String groupName, String cronExp, Map param); + void updateJob(String jobKey, String cronExp, Map param); /** * 删除任务 * - * @param jobName - * @param groupName + * @param jobKey */ - void deleteJob(String jobName, String groupName); + void deleteJob(String jobKey); /** * 启动所有任务 @@ -76,4 +72,6 @@ public interface QuartzOperation { */ void shutdownAllJobs(); + boolean checkJob(String jobKey) throws SchedulerException; + } diff --git a/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/QuartzTemplate.java b/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/QuartzTemplate.java index aff2feb9..87f89a9a 100644 --- a/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/QuartzTemplate.java +++ b/framework/modules/quartz-starter/src/main/java/group/idealworld/dew/core/quartz/QuartzTemplate.java @@ -17,16 +17,16 @@ public QuartzTemplate(Scheduler scheduler){ } @Override - public void addJob(String clazzName, String jobName, String groupName, String cronExp, Map param) { + public void addJob(String clazzName, String jobkey, String cronExp, Map param) { try { // scheduler.start(); //构建job信息 Class jobClass = (Class) Class.forName(clazzName); - JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, groupName).build(); + JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobkey).build(); //表达式调度构建器(即任务执行的时间) CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExp); //按新的cronExpression表达式构建一个新的trigger - CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, groupName).withSchedule(scheduleBuilder).build(); + CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobkey).withSchedule(scheduleBuilder).build(); //获得JobDataMap,写入数据 if (param != null) { trigger.getJobDataMap().putAll(param); @@ -38,36 +38,36 @@ public void addJob(String clazzName, String jobName, String groupName, String cr } @Override - public void pauseJob(String jobName, String groupName) { + public void pauseJob(String jobKey) { try { - scheduler.pauseJob(JobKey.jobKey(jobName, groupName)); + scheduler.pauseJob(JobKey.jobKey(jobKey)); } catch (SchedulerException e) { log.error("暂停任务失败", e); } } @Override - public void resumeJob(String jobName, String groupName) { + public void resumeJob(String jobKey) { try { - scheduler.resumeJob(JobKey.jobKey(jobName, groupName)); + scheduler.resumeJob(JobKey.jobKey(jobKey)); } catch (SchedulerException e) { log.error("恢复任务失败", e); } } @Override - public void runOnce(String jobName, String groupName) { + public void runOnce(String jobKey) { try { - scheduler.triggerJob(JobKey.jobKey(jobName, groupName)); + scheduler.triggerJob(JobKey.jobKey(jobKey)); } catch (SchedulerException e) { log.error("立即运行一次定时任务失败", e); } } @Override - public void updateJob(String jobName, String groupName, String cronExp, Map param) { + public void updateJob(String jobKey, String cronExp, Map param) { try { - TriggerKey triggerKey = TriggerKey.triggerKey(jobName, groupName); + TriggerKey triggerKey = TriggerKey.triggerKey(jobKey); CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); if (cronExp != null) { // 表达式调度构建器 @@ -87,12 +87,12 @@ public void updateJob(String jobName, String groupName, String cronExp, Map { public void initialize(ConfigurableApplicationContext configurableApplicationContext) { TestPropertyValues.of( - "spring.datasource.url=" + mysqlContainer.getJdbcUrl(), + "spring.datasource.url=jdbc:mysql://127.0.0.1:" + mysqlContainer.getFirstMappedPort() + "/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true", "spring.datasource.username=" + mysqlContainer.getUsername(), - "spring.datasource.password=" + mysqlContainer.getPassword() + "spring.datasource.password=" + mysqlContainer.getPassword(), + "spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver" ).applyTo(configurableApplicationContext.getEnvironment()); } } From 2dcfd2027a3bc673fa5a7e450e07ff3c0a6dadb5 Mon Sep 17 00:00:00 2001 From: nipeixuan <34534268+nipeixuan@users.noreply.github.com> Date: Wed, 23 Feb 2022 14:58:39 +0800 Subject: [PATCH 10/11] update quartz module --- .../dew/core/quartz/ClusterTest.java | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 framework/modules/quartz-starter/src/test/java/group/idealworld/dew/core/quartz/ClusterTest.java diff --git a/framework/modules/quartz-starter/src/test/java/group/idealworld/dew/core/quartz/ClusterTest.java b/framework/modules/quartz-starter/src/test/java/group/idealworld/dew/core/quartz/ClusterTest.java new file mode 100644 index 00000000..ff95bc78 --- /dev/null +++ b/framework/modules/quartz-starter/src/test/java/group/idealworld/dew/core/quartz/ClusterTest.java @@ -0,0 +1,55 @@ +/* + * Copyright 2021. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package group.idealworld.dew.core.quartz; + +import group.idealworld.dew.test.MySqlExtension; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.quartz.SchedulerException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.testcontainers.junit.jupiter.Testcontainers; + +/** + * Cluster test. + * + * @author nipeixuan + */ +@ExtendWith({SpringExtension.class, MySqlExtension.class}) +@ContextConfiguration(initializers = MySqlExtension.Initializer.class) +@SpringBootApplication +@SpringBootTest +@Testcontainers +public class ClusterTest { + + @Autowired + private QuartzTemplate quartzTemplate; + + /** + * Test mq. + * + * @throws InterruptedException the interrupted exception + */ + @Test + public void testMQ() throws InterruptedException, SchedulerException { + new QuartzTest().testQuartz(quartzTemplate); + } + +} From 7f94bc4ce2459fa13cac886c466e59001e5e97e8 Mon Sep 17 00:00:00 2001 From: nipeixuan <34534268+nipeixuan@users.noreply.github.com> Date: Wed, 23 Feb 2022 14:59:35 +0800 Subject: [PATCH 11/11] update quartz module --- .../dew/core/quartz/{quartzTest.java => QuartzTest.java} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename framework/modules/quartz-starter/src/test/java/group/idealworld/dew/core/quartz/{quartzTest.java => QuartzTest.java} (100%) diff --git a/framework/modules/quartz-starter/src/test/java/group/idealworld/dew/core/quartz/quartzTest.java b/framework/modules/quartz-starter/src/test/java/group/idealworld/dew/core/quartz/QuartzTest.java similarity index 100% rename from framework/modules/quartz-starter/src/test/java/group/idealworld/dew/core/quartz/quartzTest.java rename to framework/modules/quartz-starter/src/test/java/group/idealworld/dew/core/quartz/QuartzTest.java