diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/rest/DatabaseController.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/rest/DatabaseController.java index eb22d0c7..0148ec9e 100644 --- a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/rest/DatabaseController.java +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/rest/DatabaseController.java @@ -61,4 +61,13 @@ public class DatabaseController { databaseService.delete(id); return new ResponseEntity(HttpStatus.OK); } + + @Log("测试数据库链接") + @ApiOperation(value = "测试数据库链接") + @PostMapping("/testConnect") + @PreAuthorize("@el.check('database:testConnect')") + public ResponseEntity testConnect(@Validated @RequestBody Database resources){ + return new ResponseEntity<>(databaseService.testConnection(resources),HttpStatus.CREATED); + } + } diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/DatabaseService.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/DatabaseService.java index 9e2e7b18..aaa77a69 100644 --- a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/DatabaseService.java +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/DatabaseService.java @@ -51,4 +51,11 @@ public interface DatabaseService { * @param id / */ void delete(String id); + + /** + * 测试连接数据库 + * @param resources + * @return + */ + boolean testConnection(Database resources); } diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/impl/DatabaseServiceImpl.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/impl/DatabaseServiceImpl.java index aa9e3cf7..ab1819d9 100644 --- a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/impl/DatabaseServiceImpl.java +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/impl/DatabaseServiceImpl.java @@ -1,12 +1,14 @@ package me.zhengjie.modules.mnt.service.impl; import cn.hutool.core.util.IdUtil; +import lombok.extern.slf4j.Slf4j; import me.zhengjie.modules.mnt.domain.Database; import me.zhengjie.modules.mnt.repository.DatabaseRepository; import me.zhengjie.modules.mnt.service.DatabaseService; import me.zhengjie.modules.mnt.service.dto.DatabaseDto; import me.zhengjie.modules.mnt.service.dto.DatabaseQueryCriteria; import me.zhengjie.modules.mnt.service.mapper.DatabaseMapper; +import me.zhengjie.modules.mnt.util.SqlUtils; import me.zhengjie.utils.PageUtil; import me.zhengjie.utils.QueryHelp; import me.zhengjie.utils.ValidationUtil; @@ -21,6 +23,7 @@ import org.springframework.transaction.annotation.Transactional; * @date 2019-08-24 */ @Service +@Slf4j @Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class) public class DatabaseServiceImpl implements DatabaseService { @@ -72,4 +75,15 @@ public class DatabaseServiceImpl implements DatabaseService { public void delete(String id) { databaseRepository.deleteById(id); } + + @Override + public boolean testConnection(Database resources) { + try { + return SqlUtils.testConnection(resources.getJdbcUrl(), resources.getUserName(), resources.getPwd()); + } catch (Exception e) { + log.error(e.getMessage()); + return false; + } + + } } diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/util/DataTypeEnum.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/util/DataTypeEnum.java new file mode 100644 index 00000000..e6d4ef46 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/util/DataTypeEnum.java @@ -0,0 +1,123 @@ +/* + * << + * Davinci + * == + * Copyright (C) 2016 - 2019 EDP + * == + * 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 me.zhengjie.modules.mnt.util; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public enum DataTypeEnum { + + MYSQL("mysql", "mysql", "com.mysql.jdbc.Driver", "`", "`", "'", "'"), + + ORACLE("oracle", "oracle", "oracle.jdbc.driver.OracleDriver", "\"", "\"", "\"", "\""), + + SQLSERVER("sqlserver", "sqlserver", "com.microsoft.sqlserver.jdbc.SQLServerDriver", "\"", "\"", "\"", "\""), + + H2("h2", "h2", "org.h2.Driver", "`", "`", "\"", "\""), + + PHOENIX("phoenix", "hbase phoenix", "org.apache.phoenix.jdbc.PhoenixDriver", "", "", "\"", "\""), + + MONGODB("mongo", "mongodb", "mongodb.jdbc.MongoDriver", "`", "`", "\"", "\""), + + ELASTICSEARCH("sql4es", "elasticsearch", "nl.anchormen.sql4es.jdbc.ESDriver", "", "", "'", "'"), + + PRESTO("presto", "presto", "com.facebook.presto.jdbc.PrestoDriver", "", "", "\"", "\""), + + MOONBOX("moonbox", "moonbox", "moonbox.jdbc.MbDriver", "`", "`", "`", "`"), + + CASSANDRA("cassandra", "cassandra", "com.github.adejanovski.cassandra.jdbc.CassandraDriver", "", "", "'", "'"), + + CLICKHOUSE("clickhouse", "clickhouse", "ru.yandex.clickhouse.ClickHouseDriver", "", "", "\"", "\""), + + KYLIN("kylin", "kylin", "org.apache.kylin.jdbc.Driver", "\"", "\"", "\"", "\""), + + VERTICA("vertica", "vertica", "com.vertica.jdbc.Driver", "", "", "'", "'"), + + HANA("sap", "sap hana", "com.sap.db.jdbc.Driver", "", "", "'", "'"), + + IMPALA("impala", "impala", "com.cloudera.impala.jdbc41.Driver", "", "", "'", "'"); + + + private String feature; + private String desc; + private String driver; + private String keywordPrefix; + private String keywordSuffix; + private String aliasPrefix; + private String aliasSuffix; + + private static final String jdbcUrlPrefix = "jdbc:"; + + DataTypeEnum(String feature, String desc, String driver, String keywordPrefix, String keywordSuffix, String aliasPrefix, String aliasSuffix) { + this.feature = feature; + this.desc = desc; + this.driver = driver; + this.keywordPrefix = keywordPrefix; + this.keywordSuffix = keywordSuffix; + this.aliasPrefix = aliasPrefix; + this.aliasSuffix = aliasSuffix; + } + + public static DataTypeEnum urlOf(String jdbcUrl) { + String url = jdbcUrl.toLowerCase().trim(); + for (DataTypeEnum dataTypeEnum : values()) { + if (url.startsWith(jdbcUrlPrefix + dataTypeEnum.feature)) { + try { + Class aClass = Class.forName(dataTypeEnum.getDriver()); + if (null == aClass) { + throw new RuntimeException("Unable to get driver instance for jdbcUrl: " + jdbcUrl); + } + } catch (ClassNotFoundException e) { + throw new RuntimeException("Unable to get driver instance: " + jdbcUrl); + } + return dataTypeEnum; + } + } + return null; + } + + public String getFeature() { + return feature; + } + + public String getDesc() { + return desc; + } + + public String getDriver() { + return driver; + } + + public String getKeywordPrefix() { + return keywordPrefix; + } + + public String getKeywordSuffix() { + return keywordSuffix; + } + + public String getAliasPrefix() { + return aliasPrefix; + } + + public String getAliasSuffix() { + return aliasSuffix; + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/util/SqlUtils.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/util/SqlUtils.java new file mode 100644 index 00000000..6e59f8c8 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/util/SqlUtils.java @@ -0,0 +1,163 @@ +package me.zhengjie.modules.mnt.util; + +import cn.hutool.crypto.SecureUtil; +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.druid.util.StringUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.jdbc.core.JdbcTemplate; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; + +@Slf4j +public class SqlUtils { + + public static final String COLON = ":"; + + private static volatile Map map = new HashMap<>(); + + private static String getKey(String jdbcUrl, String username, String password) { + StringBuilder sb = new StringBuilder(); + if (!StringUtils.isEmpty(username)) { + sb.append(username); + } + if (!StringUtils.isEmpty(password)) { + sb.append(COLON).append(password); + } + sb.append(COLON).append(jdbcUrl.trim()); + + return SecureUtil.md5(sb.toString()); + } + + /** + * 获取数据源 + * + * @param jdbcUrl + * @param userName + * @param password + * @return + */ + private static DataSource getDataSource(String jdbcUrl, String userName, String password) { + String key = getKey(jdbcUrl, userName, password); + if (!map.containsKey(key) || null == map.get(key)) { + DruidDataSource druidDataSource = new DruidDataSource(); + + String className = null; + try { + className = DriverManager.getDriver(jdbcUrl.trim()).getClass().getName(); + } catch (SQLException e) { + throw new RuntimeException("Get class name error: =" + jdbcUrl); + } + if (StringUtils.isEmpty(className)) { + DataTypeEnum dataTypeEnum = DataTypeEnum.urlOf(jdbcUrl); + if (null == dataTypeEnum ) { + throw new RuntimeException("Not supported data type: jdbcUrl=" + jdbcUrl); + } + druidDataSource.setDriverClassName(dataTypeEnum.getDriver()); + } else { + druidDataSource.setDriverClassName(className); + } + + + druidDataSource.setUrl(jdbcUrl); + druidDataSource.setUsername(userName); + druidDataSource.setPassword(password); + // 配置获取连接等待超时的时间 + druidDataSource.setMaxWait(3000); + // 配置初始化大小、最小、最大 + druidDataSource.setInitialSize(1); + druidDataSource.setMinIdle(1); + druidDataSource.setMaxActive(1); + + // 配置间隔多久才进行一次检测需要关闭的空闲连接,单位是毫秒 + druidDataSource.setTimeBetweenEvictionRunsMillis(50000); + // 配置一旦重试多次失败后等待多久再继续重试连接,单位是毫秒 + druidDataSource.setTimeBetweenConnectErrorMillis(18000); + // 配置一个连接在池中最小生存的时间,单位是毫秒 + druidDataSource.setMinEvictableIdleTimeMillis(300000); + // 这个特性能解决 MySQL 服务器8小时关闭连接的问题 + druidDataSource.setMaxEvictableIdleTimeMillis(25200000); + + try { + druidDataSource.init(); + } catch (SQLException e) { + log.error("Exception during pool initialization", e); + throw new RuntimeException(e.getMessage()); + } + map.put(key, druidDataSource); + } + return map.get(key); + } + + private static Connection getConnection(String jdbcUrl, String userName, String password) { + DataSource dataSource = getDataSource(jdbcUrl, userName, password); + Connection connection = null; + try { + connection = dataSource.getConnection(); + } catch (Exception e) { + connection = null; + } + try { + if (null == connection || connection.isClosed() || !connection.isValid(5)) { + log.info("connection is closed or invalid, retry get connection!"); + connection = dataSource.getConnection(); + } + } catch (Exception e) { + log.error("create connection error, jdbcUrl: {}", jdbcUrl); + throw new RuntimeException("create connection error, jdbcUrl: " + jdbcUrl); + } + return connection; + } + + private static void releaseConnection(Connection connection) { + if (null != connection) { + try { + connection.close(); + connection = null; + } catch (Exception e) { + e.printStackTrace(); + log.error("connection close error", e.getMessage()); + } + } + } + + + public static void closeResult(ResultSet rs) { + if (rs != null) { + try { + rs.close(); + rs = null; + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + public static boolean testConnection(String jdbcUrl, String userName, String password) { + Connection connection = null; + try { + connection = getConnection(jdbcUrl, userName, password); + if (null != connection) { + return true; + } + }catch (Exception e){ + log.info("Get connection failed:",e.getMessage()); + }finally { + releaseConnection(connection); + } + return false; + } + + public JdbcTemplate jdbcTemplate(String jdbcUrl, String userName, String password) { + DataSource dataSource = getDataSource(jdbcUrl, userName, password); + JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); + jdbcTemplate.setFetchSize(1000); + return jdbcTemplate; + } + +} diff --git a/sql/eladmin.sql b/sql/eladmin.sql index 4df5cc02..bc7b8ca6 100644 --- a/sql/eladmin.sql +++ b/sql/eladmin.sql @@ -368,6 +368,8 @@ CREATE TABLE `mnt_database` ( PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; +INSERT INTO `mnt_database` VALUES ('604dd98ae8b44b128544c2135628f87d', '本机', 'jdbc:log4jdbc:mysql://localhost:3306/eladmin?serverTimezone=UTC', 'root', '123456'); + -- ---------------------------- -- Table structure for mnt_deploy -- ----------------------------