diff --git a/framework/modules/cluster-rocket/src/main/java/group/idealworld/dew/core/cluster/spi/rocket/RocketAutoConfiguration.java b/framework/modules/cluster-rocket/src/main/java/group/idealworld/dew/core/cluster/spi/rocket/RocketAutoConfiguration.java index 885072b5..7cd23fff 100644 --- a/framework/modules/cluster-rocket/src/main/java/group/idealworld/dew/core/cluster/spi/rocket/RocketAutoConfiguration.java +++ b/framework/modules/cluster-rocket/src/main/java/group/idealworld/dew/core/cluster/spi/rocket/RocketAutoConfiguration.java @@ -41,7 +41,10 @@ public class RocketAutoConfiguration { private static final Logger logger = LoggerFactory.getLogger(RocketAutoConfiguration.class); @Value("${rocketmq.producer.group}") - private String groupName; + private String producerGroupName; + + @Value("${rocketmq.consumer.group}") + private String consumerGroupName; @Value("${rocketmq.name-server}") private String nameServer; @@ -72,7 +75,7 @@ public RocketAdapter rocketAdapter(RocketMQTemplate rocketMQTemplate) { @Bean @ConditionalOnExpression("'${dew.cluster.mq}'=='rocket'") public RocketClusterMQ rocketClusterMQ(RocketAdapter rocketAdapter) { - return new RocketClusterMQ(rocketAdapter, nameServer, groupName); + return new RocketClusterMQ(rocketAdapter, nameServer, producerGroupName, consumerGroupName); } } diff --git a/framework/modules/cluster-rocket/src/main/java/group/idealworld/dew/core/cluster/spi/rocket/RocketClusterMQ.java b/framework/modules/cluster-rocket/src/main/java/group/idealworld/dew/core/cluster/spi/rocket/RocketClusterMQ.java index 1fccb402..b4e70f4b 100644 --- a/framework/modules/cluster-rocket/src/main/java/group/idealworld/dew/core/cluster/spi/rocket/RocketClusterMQ.java +++ b/framework/modules/cluster-rocket/src/main/java/group/idealworld/dew/core/cluster/spi/rocket/RocketClusterMQ.java @@ -54,12 +54,15 @@ public class RocketClusterMQ extends AbsClusterMQ { private final String nameServer; - private final String groupName; + private final String producerGroupName; - public RocketClusterMQ(RocketAdapter rocketAdapter, String nameServer, String groupName) { + private final String consumerGroupName; + + public RocketClusterMQ(RocketAdapter rocketAdapter, String nameServer, String producerGroupName, String consumerGroupName) { this.rocketAdapter = rocketAdapter; this.nameServer = nameServer; - this.groupName = groupName; + this.producerGroupName = producerGroupName; + this.consumerGroupName = consumerGroupName; } /** @@ -141,7 +144,7 @@ protected boolean doPublish(String topic, String message, Optional consumer) { - DefaultMQPushConsumer mqConsumer = new DefaultMQPushConsumer(groupName); + DefaultMQPushConsumer mqConsumer = new DefaultMQPushConsumer(producerGroupName); mqConsumer.setNamesrvAddr(nameServer); mqConsumer.setInstanceName(UUID.randomUUID().toString()); @@ -181,7 +184,7 @@ protected boolean doRequest(String address, String message, Optional consumer) { - DefaultMQPushConsumer mqConsumer = new DefaultMQPushConsumer(groupName); + DefaultMQPushConsumer mqConsumer = new DefaultMQPushConsumer(consumerGroupName); mqConsumer.setNamesrvAddr(nameServer); mqConsumer.setInstanceName(UUID.randomUUID().toString()); try { diff --git a/framework/modules/dbutils-starter/pom.xml b/framework/modules/dbutils-starter/pom.xml new file mode 100644 index 00000000..0be50260 --- /dev/null +++ b/framework/modules/dbutils-starter/pom.xml @@ -0,0 +1,111 @@ + + + + + 4.0.0 + + group.idealworld.dew + parent-starter + 3.0.0-Beta3 + ../parent-starter + + + dbutils-starter + 1.1.4 Dew dbutils- + Dew 集群 动态数据源实现 + jar + + + + + + + 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..2703fc41 --- /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.init(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..362e2b9d --- /dev/null +++ b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/DewDBUtils.java @@ -0,0 +1,82 @@ +/* + * 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 dbUtilsConfig 加载配置信息 + */ + + public static void init(DBUtilsConfig dbUtilsConfig){ + DSLoader.load(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..45bde1d4 --- /dev/null +++ b/framework/modules/dbutils-starter/src/main/java/group/idealworld/dew/core/dbutils/process/DSLoader.java @@ -0,0 +1,150 @@ +/* + * 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(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/quartz-starter/pom.xml b/framework/modules/quartz-starter/pom.xml new file mode 100644 index 00000000..35608d3c --- /dev/null +++ b/framework/modules/quartz-starter/pom.xml @@ -0,0 +1,20 @@ + + + + build-framework + group.idealworld.dew + 3.0.0-Beta3 + ../../pom.xml + + 4.0.0 + + quartz-starter + + + 11 + 11 + + + \ No newline at end of file diff --git a/framework/modules/test-starter/src/main/java/group/idealworld/dew/test/RocketMQExtension.java b/framework/modules/test-starter/src/main/java/group/idealworld/dew/test/RocketMQExtension.java index 89c78d86..402c2224 100644 --- a/framework/modules/test-starter/src/main/java/group/idealworld/dew/test/RocketMQExtension.java +++ b/framework/modules/test-starter/src/main/java/group/idealworld/dew/test/RocketMQExtension.java @@ -84,7 +84,8 @@ public void initialize(ConfigurableApplicationContext configurableApplicationCon "rocketmq.name-server=" + rocketmqContainer.getServiceHost("namesrv_1", 9876) + ":" + rocketmqContainer.getServicePort("namesrv_1", 9876), - "rocketmq.producer.group=rocketmq-group" + "rocketmq.producer.group=rocketmq-producer-group", + "rocketmq.consumer.group=rocketmq-consumer-group" ).applyTo(configurableApplicationContext.getEnvironment()); } }