diff --git a/report-core/pom.xml b/report-core/pom.xml index fc1d8bd7..ec175901 100644 --- a/report-core/pom.xml +++ b/report-core/pom.xml @@ -48,6 +48,12 @@ 2.2.6.RELEASE + + org.springframework.boot + spring-boot-configuration-processor + true + + com.anji-plus spring-boot-gaea diff --git a/report-core/src/main/java/com/anjiplus/template/gaea/business/config/DruidProperties.java b/report-core/src/main/java/com/anjiplus/template/gaea/business/config/DruidProperties.java new file mode 100644 index 00000000..bed72ad5 --- /dev/null +++ b/report-core/src/main/java/com/anjiplus/template/gaea/business/config/DruidProperties.java @@ -0,0 +1,99 @@ +package com.anjiplus.template.gaea.business.config; + +import com.alibaba.druid.pool.DruidDataSource; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * Created by raodeming on 2021/8/6. + */ +@Component +@ConfigurationProperties(prefix = "spring.druid") +@Data +public class DruidProperties { + /** + * 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时 + */ + private int initialSize; + + /** + * 最小连接池数量 + */ + private int minIdle; + + /** + * 最大连接池数量 + */ + private int maxActive; + + /** + * 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置 + */ + private int maxWait; + + /** + * 关闭空闲连接的检测时间间隔.Destroy线程会检测连接的间隔时间,如果连接空闲时间大于等于minEvictableIdleTimeMillis则关闭物理连接。 + */ + private int timeBetweenEvictionRunsMillis; + + /** + * 连接的最小生存时间.连接保持空闲而不被驱逐的最小时间 + */ + private int minEvictableIdleTimeMillis; + + /** + * 申请连接时检测空闲时间,根据空闲时间再检测连接是否有效.建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRun + */ + private boolean testWhileIdle; + + /** + * 开启PSCache + */ + private boolean poolPreparedStatements; + + /** + * 设置PSCache值 + */ + private int maxPoolPreparedStatementPerConnectionSize; + + /** + * 连接出错后再尝试连接三次 + */ + private int connectionErrorRetryAttempts; + + /** + * 数据库服务宕机自动重连机制 + */ + private boolean breakAfterAcquireFailure; + + /** + * 连接出错后重试时间间隔 + */ + private int timeBetweenConnectErrorMillis; + + public DruidDataSource dataSource(String url, String username, String password, String driverClassName) { + DruidDataSource datasource = new DruidDataSource(); + datasource.setUrl(url); + datasource.setUsername(username); + datasource.setPassword(password); + datasource.setDriverClassName(driverClassName); + //configuration + datasource.setInitialSize(initialSize); + datasource.setMinIdle(minIdle); + datasource.setMaxActive(maxActive); + datasource.setMaxWait(maxWait); + + datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); + datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); + datasource.setTestWhileIdle(testWhileIdle); + + datasource.setPoolPreparedStatements(poolPreparedStatements); + datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize); + + datasource.setConnectionErrorRetryAttempts(connectionErrorRetryAttempts); + datasource.setBreakAfterAcquireFailure(breakAfterAcquireFailure); + datasource.setTimeBetweenConnectErrorMillis(timeBetweenConnectErrorMillis); + return datasource; + } +} diff --git a/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dataSource/service/JdbcService.java b/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dataSource/service/JdbcService.java new file mode 100644 index 00000000..dc1041b3 --- /dev/null +++ b/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dataSource/service/JdbcService.java @@ -0,0 +1,39 @@ +package com.anjiplus.template.gaea.business.modules.dataSource.service; + +import com.anjiplus.template.gaea.business.modules.dataSource.controller.dto.DataSourceDto; + +import java.sql.Connection; +import java.sql.SQLException; + +/** + * Created by raodeming on 2021/8/6. + */ +public interface JdbcService { + + /** + * 删除数据库连接池 + * + * @param id + */ + void removeJdbcConnectionPool(Long id); + + + /** + * 获取连接 + * + * @param dataSource + * @return + * @throws SQLException + */ + Connection getPooledConnection(DataSourceDto dataSource) throws SQLException; + + /** + * 测试数据库连接 获取一个连接 + * + * @param dataSource + * @return + * @throws ClassNotFoundException driverName不正确 + * @throws SQLException + */ + Connection getUnPooledConnection(DataSourceDto dataSource) throws SQLException; +} diff --git a/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dataSource/service/impl/DataSourceServiceImpl.java b/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dataSource/service/impl/DataSourceServiceImpl.java index 48c7d80f..08f5b1b8 100644 --- a/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dataSource/service/impl/DataSourceServiceImpl.java +++ b/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dataSource/service/impl/DataSourceServiceImpl.java @@ -17,9 +17,9 @@ import com.anjiplus.template.gaea.business.modules.dataSource.controller.dto.Dat import com.anjiplus.template.gaea.business.modules.dataSource.controller.param.ConnectionParam; import com.anjiplus.template.gaea.business.modules.dataSource.dao.DataSourceMapper; import com.anjiplus.template.gaea.business.modules.dataSource.dao.entity.DataSource; -import com.anjiplus.template.gaea.business.util.JdbcConstants; import com.anjiplus.template.gaea.business.modules.dataSource.service.DataSourceService; -import com.anjiplus.template.gaea.business.util.JdbcUtil; +import com.anjiplus.template.gaea.business.modules.dataSource.service.JdbcService; +import com.anjiplus.template.gaea.business.util.JdbcConstants; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.extern.slf4j.Slf4j; @@ -59,6 +59,9 @@ public class DataSourceServiceImpl implements DataSourceService { @Autowired private DataSetParamService dataSetParamService; + @Autowired + private JdbcService jdbcService; + @Override public GaeaBaseMapper getMapper() { return dataSourceMapper; @@ -222,7 +225,7 @@ public class DataSourceServiceImpl implements DataSourceService { analysisRelationalDbConfig(dto); Connection pooledConnection = null; try { - pooledConnection = JdbcUtil.getPooledConnection(dto); + pooledConnection = jdbcService.getPooledConnection(dto); PreparedStatement statement = pooledConnection.prepareStatement(dto.getDynSentence()); ResultSet rs = statement.executeQuery(); @@ -307,13 +310,18 @@ public class DataSourceServiceImpl implements DataSourceService { public void testRelationalDb(DataSourceDto dto) { analysisRelationalDbConfig(dto); try { - Connection unPooledConnection = JdbcUtil.getUnPooledConnection(dto); + Connection unPooledConnection = jdbcService.getUnPooledConnection(dto); String catalog = unPooledConnection.getCatalog(); log.info("数据库测试连接成功:{}", catalog); unPooledConnection.close(); } catch (SQLException e) { log.error("error",e); - throw BusinessExceptionBuilder.build(ResponseCode.DATA_SOURCE_CONNECTION_FAILED, e.getMessage()); + if (e.getCause() instanceof ClassNotFoundException) { + throw BusinessExceptionBuilder.build(ResponseCode.CLASS_NOT_FOUND, e.getCause().getMessage()); + } else { + throw BusinessExceptionBuilder.build(ResponseCode.DATA_SOURCE_CONNECTION_FAILED, e.getMessage()); + } + } } @@ -418,6 +426,6 @@ public class DataSourceServiceImpl implements DataSourceService { */ @Override public void processAfterOperation(DataSource entity, BaseOperationEnum operationEnum) throws BusinessException { - JdbcUtil.removeJdbcConnectionPool(entity.getId()); + jdbcService.removeJdbcConnectionPool(entity.getId()); } } diff --git a/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dataSource/service/impl/JdbcServiceImpl.java b/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dataSource/service/impl/JdbcServiceImpl.java new file mode 100644 index 00000000..a7a49430 --- /dev/null +++ b/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dataSource/service/impl/JdbcServiceImpl.java @@ -0,0 +1,96 @@ +package com.anjiplus.template.gaea.business.modules.dataSource.service.impl; + +import com.alibaba.druid.pool.DruidDataSource; +import com.anjiplus.template.gaea.business.config.DruidProperties; +import com.anjiplus.template.gaea.business.modules.dataSource.controller.dto.DataSourceDto; +import com.anjiplus.template.gaea.business.modules.dataSource.service.JdbcService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Created by raodeming on 2021/8/6. + */ +@Service +@Slf4j +public class JdbcServiceImpl implements JdbcService { + + @Autowired + private DruidProperties druidProperties; + + /** + * 所有数据源的连接池存在map里 + */ + static Map map = new ConcurrentHashMap<>(); + + public DruidDataSource getJdbcConnectionPool(DataSourceDto dataSource) { + if (map.containsKey(dataSource.getId())) { + return map.get(dataSource.getId()); + } else { + try { + if (!map.containsKey(dataSource.getId())) { + DruidDataSource pool = druidProperties.dataSource(dataSource.getJdbcUrl(), + dataSource.getUsername(), dataSource.getPassword(), dataSource.getDriverName()); + map.put(dataSource.getId(), pool); + log.info("创建连接池成功:{}", dataSource.getJdbcUrl()); + } + return map.get(dataSource.getId()); + } finally { + } + } + } + + + /** + * 删除数据库连接池 + * + * @param id + */ + @Override + public void removeJdbcConnectionPool(Long id) { + try { + DruidDataSource pool = map.get(id); + if (pool != null) { + log.info("remove pool success, datasourceId:{}", id); + map.remove(id); + } + } catch (Exception e) { + log.error("error", e); + } finally { + } + } + + /** + * 获取连接 + * + * @param dataSource + * @return + * @throws SQLException + */ + @Override + public Connection getPooledConnection(DataSourceDto dataSource) throws SQLException{ + DruidDataSource pool = getJdbcConnectionPool(dataSource); + return pool.getConnection(); + } + + /** + * 测试数据库连接 获取一个连接 + * + * @param dataSource + * @return + * @throws ClassNotFoundException driverName不正确 + * @throws SQLException + */ + @Override + public Connection getUnPooledConnection(DataSourceDto dataSource) throws SQLException { + DruidDataSource druidDataSource = druidProperties.dataSource(dataSource.getJdbcUrl(), + dataSource.getUsername(), dataSource.getPassword(), dataSource.getDriverName()); + return druidDataSource.getConnection(); + } + +} diff --git a/report-core/src/main/java/com/anjiplus/template/gaea/business/util/DriverClassUtil.java b/report-core/src/main/java/com/anjiplus/template/gaea/business/util/DriverClassUtil.java deleted file mode 100644 index 02b6dc82..00000000 --- a/report-core/src/main/java/com/anjiplus/template/gaea/business/util/DriverClassUtil.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.anjiplus.template.gaea.business.util; - -import com.anji.plus.gaea.exception.BusinessExceptionBuilder; -import com.anjiplus.template.gaea.business.code.ResponseCode; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; - -import java.util.HashMap; -import java.util.Map; - -/** - * Created by raodeming on 2021/4/19. - */ -@Slf4j -public final class DriverClassUtil { - - /** - * 存放驱动类信息 - */ - private static final Map DRIVER_CLASS_MAP; - - static { - DRIVER_CLASS_MAP = new HashMap<>(32); - DRIVER_CLASS_MAP.put("jdbc:db2", "COM.ibm.db2.jdbc.app.DB2Driver"); - DRIVER_CLASS_MAP.put("jdbc:firebirdsql", "org.firebirdsql.jdbc.FBDriver"); - DRIVER_CLASS_MAP.put("jdbc:edbc", "ca.edbc.jdbc.EdbcDriver"); - DRIVER_CLASS_MAP.put("jdbc:pointbase", "com.pointbase.jdbc.jdbcUniversalDriver"); - DRIVER_CLASS_MAP.put("jdbc:fake", "com.alibaba.druid.mock.MockDriver"); - DRIVER_CLASS_MAP.put("jdbc:informix-sqli", "com.informix.jdbc.IfxDriver"); - DRIVER_CLASS_MAP.put("jdbc:sqlite", "org.sqlite.JDBC"); - DRIVER_CLASS_MAP.put("jdbc:microsoft", "com.microsoft.jdbc.sqlserver.SQLServerDriver"); - DRIVER_CLASS_MAP.put("jdbc:hsqldb", "org.hsqldb.jdbcDriver"); - DRIVER_CLASS_MAP.put("jdbc:postgresql", "org.postgresql.Driver"); - DRIVER_CLASS_MAP.put("jdbc:ingres", "com.ingres.jdbc.IngresDriver"); - DRIVER_CLASS_MAP.put("jdbc:cloudscape", "COM.cloudscape.core.JDBCDriver"); - DRIVER_CLASS_MAP.put("jdbc:JSQLConnect", "com.jnetdirect.jsql.JSQLDriver"); - DRIVER_CLASS_MAP.put("jdbc:derby", "org.apache.derby.jdbc.EmbeddedDriver"); - DRIVER_CLASS_MAP.put("jdbc:timesten", "com.timesten.jdbc.TimesTenDriver"); - DRIVER_CLASS_MAP.put("jdbc:interbase", "interbase.interclient.Driver"); - DRIVER_CLASS_MAP.put("jdbc:h2", "org.h2.Driver"); - DRIVER_CLASS_MAP.put("jdbc:as400", "com.ibm.as400.access.AS400JDBCDriver"); - DRIVER_CLASS_MAP.put("jdbc:sybase:Tds", "com.sybase.jdbc2.jdbc.SybDriver"); - DRIVER_CLASS_MAP.put("jdbc:mock", "com.alibaba.druid.mock.MockDriver"); - DRIVER_CLASS_MAP.put("jdbc:oracle", "oracle.jdbc.driver.OracleDriver"); - DRIVER_CLASS_MAP.put("jdbc:mysql", "com.mysql.jdbc.Driver"); - DRIVER_CLASS_MAP.put("jdbc:odps", "com.aliyun.odps.jdbc.OdpsDriver"); - DRIVER_CLASS_MAP.put("jdbc:mckoi", "com.mckoi.JDBCDriver"); - DRIVER_CLASS_MAP.put("jdbc:jtds", "net.sourceforge.jtds.jdbc.Driver"); - DRIVER_CLASS_MAP.put("jdbc:sapdb", "com.sap.dbtech.jdbc.DriverSapDB"); - DRIVER_CLASS_MAP.put("jdbc:JTurbo", "com.newatlanta.jturbo.driver.Driver"); - DRIVER_CLASS_MAP.put("jdbc:mimer:multi1", "com.mimer.jdbc.Driver"); - } - - /** - * 加载驱动类信息 - * @param driverClass 驱动类 - * @param url 连接信息 - */ - public static void loadDriverClass(String driverClass, final String url) { - if(StringUtils.isEmpty(driverClass)) { - driverClass = getDriverClassByUrl(url); - } - - try { - Class.forName(driverClass); - } catch (ClassNotFoundException e) { - log.error("error", e); - throw BusinessExceptionBuilder.build(ResponseCode.CLASS_NOT_FOUND); - } - } - - - /** - * 根据 URL 获取对应的驱动类 - * - * 1. 禁止 url 为空 - * 2. 如果未找到,则直接报错。 - * @param url url - * @return 驱动信息 - */ - private static String getDriverClassByUrl(final String url) { - for(Map.Entry entry : DRIVER_CLASS_MAP.entrySet()) { - String urlPrefix = entry.getKey(); - if(url.startsWith(urlPrefix)) { - return entry.getValue(); - } - } - - throw BusinessExceptionBuilder.build(ResponseCode.CLASS_NOT_FOUND, "Can't auto find match driver class for url: " + url); - } - -} diff --git a/report-core/src/main/java/com/anjiplus/template/gaea/business/util/JdbcUtil.java b/report-core/src/main/java/com/anjiplus/template/gaea/business/util/JdbcUtil.java deleted file mode 100644 index 2b36ff56..00000000 --- a/report-core/src/main/java/com/anjiplus/template/gaea/business/util/JdbcUtil.java +++ /dev/null @@ -1,103 +0,0 @@ -package com.anjiplus.template.gaea.business.util; - -import com.alibaba.druid.pool.DruidDataSource; -import com.anjiplus.template.gaea.business.modules.dataSource.controller.dto.DataSourceDto; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; - -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.SQLException; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Created by raodeming on 2021/3/18. - */ -@Slf4j -public class JdbcUtil { - - /** - * 所有数据源的连接池存在map里 - */ - static Map map = new ConcurrentHashMap<>(); - - public static DruidDataSource getJdbcConnectionPool(DataSourceDto dataSource) { - if (map.containsKey(dataSource.getId())) { - return map.get(dataSource.getId()); - } else { - try { - if (!map.containsKey(dataSource.getId())) { - DruidDataSource pool = new DruidDataSource(); - pool.setUrl(dataSource.getJdbcUrl()); - pool.setUsername(dataSource.getUsername()); - pool.setPassword(dataSource.getPassword()); - pool.setDriverClassName(dataSource.getDriverName()); - - //下面都是可选的配置 - pool.setInitialSize(10); //初始连接数,默认0 - pool.setMaxActive(30); //最大连接数,默认8 - pool.setMinIdle(10); //最小闲置数 - pool.setMaxWait(2000); //获取连接的最大等待时间,单位毫秒 - pool.setPoolPreparedStatements(true); //缓存PreparedStatement,默认false - pool.setMaxOpenPreparedStatements(20); //缓存PreparedStatement的最大数量,默认-1(不缓存)。大于0时会自动开启缓存PreparedStatement,所以可以省略上一句代码 - pool.setConnectionErrorRetryAttempts(0); - pool.setBreakAfterAcquireFailure(true); - map.put(dataSource.getId(), pool); - log.info("创建连接池成功:{}", dataSource.getJdbcUrl()); - } - return map.get(dataSource.getId()); - } finally { - } - } - } - - /** - * 删除数据库连接池 - * - * @param id - */ - public static void removeJdbcConnectionPool(Long id) { - try { - DruidDataSource pool = map.get(id); - if (pool != null) { - log.info("remove pool success, datasourceId:{}", id); - map.remove(id); - } - } catch (Exception e) { - log.error("error", e); - } finally { - } - } - - /** - * 获取连接 - * - * @param dataSource - * @return - * @throws SQLException - */ - public static Connection getPooledConnection(DataSourceDto dataSource) throws SQLException { - DruidDataSource pool = getJdbcConnectionPool(dataSource); - return pool.getConnection(); - } - - /** - * 测试数据库连接 获取一个连接 - * - * @param dataSource - * @return - * @throws ClassNotFoundException driverName不正确 - * @throws SQLException - */ - public static Connection getUnPooledConnection(DataSourceDto dataSource) throws SQLException { - DriverClassUtil.loadDriverClass(dataSource.getDriverName(), dataSource.getJdbcUrl()); - if (StringUtils.isBlank(dataSource.getUsername()) && StringUtils.isBlank(dataSource.getPassword())) { - return DriverManager.getConnection(dataSource.getJdbcUrl()); - } - return DriverManager.getConnection(dataSource.getJdbcUrl(), - dataSource.getUsername(), dataSource.getPassword()); - } - - -} diff --git a/report-core/src/main/resources/bootstrap.yml b/report-core/src/main/resources/bootstrap.yml index fa5efe7b..8bdadede 100644 --- a/report-core/src/main/resources/bootstrap.yml +++ b/report-core/src/main/resources/bootstrap.yml @@ -22,6 +22,20 @@ spring: url: jdbc:mysql://10.108.26.197:3306/aj_report?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false username: root password: appuser@anji + #数据源连接池配置 + druid: + initial-size: 10 # 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时 + min-idle: 10 # 最小连接池数量 + maxActive: 200 # 最大连接池数量 + maxWait: 60000 # 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置 + timeBetweenEvictionRunsMillis: 60000 # 关闭空闲连接的检测时间间隔.Destroy线程会检测连接的间隔时间,如果连接空闲时间大于等于minEvictableIdleTimeMillis则关闭物理连接。 + minEvictableIdleTimeMillis: 300000 # 连接的最小生存时间.连接保持空闲而不被驱逐的最小时间 + testWhileIdle: true # 申请连接时检测空闲时间,根据空闲时间再检测连接是否有效.建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRun + poolPreparedStatements: true # 开启PSCache + maxPoolPreparedStatementPerConnectionSize: 20 #设置PSCache值 + connectionErrorRetryAttempts: 3 # 连接出错后再尝试连接三次 + breakAfterAcquireFailure: true # 数据库服务宕机自动重连机制 + timeBetweenConnectErrorMillis: 300000 # 连接出错后重试时间间隔 flyway: baseline-on-migrate: true #数据库连接配置