springcloud微服务搭建多数据源(mysql,oracle,postgres,等等)管理模块,支持通过注解方式切换不同类型的数据库

news/2024/5/19 18:42:41

1.背景

同一套微服务管理系统,业务完全一样,但不同的客户可能要求使用自己熟悉的数据库,比如,mysql,oracle,postgres,还有一些国产数据库。如果能够将数据库模块独立出来,兼容各家的数据库操作,在发布系统之前,只需要通过注解切换到定制的数据库类型,那么就会方便很多。

2.实现方案

完整项目文件结构;

2.1.单体项目运行,未注册到nacos

新建多数据源管理模块,并在application.yml里面配置不同数据库的信息;

# Tomcat
server:port: 9305# Spring
spring:application:# 应用名称name: sharetek-multi-datasourceprofiles:# 环境配置active: @profiles.active@--- # nacos 配置
spring:cloud:nacos:# nacos 服务地址server-addr: @nacos.server@discovery:# 注册组group: @nacos.discovery.group@namespace: ${spring.profiles.active}cluster-name: xaconfig:# 配置组group: @nacos.config.group@namespace: ${spring.profiles.active}config:import:- optional:nacos:application-common.yml- optional:nacos:datasource.yml- optional:nacos:${spring.application.name}.ymldatasource:type: com.zaxxer.hikari.HikariDataSource# 数据源mysql:type: ${spring.datasource.type}driver-class-name: com.mysql.cj.jdbc.Driverjdbc-url: jdbc:mysql://localhost:3306/sharetek-demodb?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=trueusername: xxxpassword: xxxoracle:type: ${spring.datasource.type}driverClassName: oracle.jdbc.OracleDriverurl: jdbc:oracle:thin:@//localhost:1521/XEusername: xxxpassword: xxxhikari:connectionTestQuery: SELECT 1 FROM DUAL
#    postgres:
#      type: ${spring.datasource.type}
#      driverClassName: org.postgresql.Driver
#      url: ${datasource.system-postgres.url}
#      username: ${datasource.system-postgres.username}
#      password: ${datasource.system-postgres.password}

 

启动类,

pom.xml配置内容如下,(有些依赖是其他系统模块的,主要是加入数据库驱动等依赖)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://maven.apache.org/POM/4.0.0"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><groupId>org.sharetek</groupId><artifactId>sharetek-visual</artifactId><version>${revision}</version></parent><modelVersion>4.0.0</modelVersion><artifactId>sharetek-multi-datasource</artifactId><description>sharetek-multi-datasource 多数据源管理</description><dependencies><dependency><groupId>org.sharetek</groupId><artifactId>sharetek-common-web</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.23</version></dependency><!-- 数据库连接池--><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.16</version></dependency><!-- sharetek Common Log --><dependency><groupId>org.sharetek</groupId><artifactId>sharetek-common-log</artifactId></dependency><!-- Mysql Connector --><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId></dependency><!-- Oracle --><dependency><groupId>com.oracle.database.jdbc</groupId><artifactId>ojdbc8</artifactId></dependency><!-- PostgreSql --><dependency><groupId>org.postgresql</groupId><artifactId>postgresql</artifactId></dependency><!-- SqlServer --><dependency><groupId>com.microsoft.sqlserver</groupId><artifactId>mssql-jdbc</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId></dependency><dependency><groupId>com.zaxxer</groupId><artifactId>HikariCP</artifactId></dependency></dependencies><build><finalName>${project.artifactId}</finalName><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>${spring-boot.version}</version><executions><execution><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build></project>

 

以mysql为例,读取mysql数据库配置信息,(dml时会用到,如果未来使用代码生成功能,用mybatisplus操作数据库,此处可以不需要,包括MysqlHelper.java等)

package org.sharetek.multids.config;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;/*** @author tjm* @title MysqlProperties* @date 2024/4/26 13:27* @description mysql数据库配置信息*/
@Data
@Component
@ConfigurationProperties(prefix = "spring.datasource.mysql")
public class MysqlProperties {private String jdbcUrl;private String userName;private String passWord;private String driverClassName;}
多数据源配置类DataSourceConfig.class,用来读取application.yml里面配置的各种数据库信息,在容器中生成DataSource对象;

package org.sharetek.multids.config;import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import javax.sql.DataSource;/*** @author junlintianxia* @title DataSourceConfig* @date 2024/4/26 10:59* @description 多数据源配置类*/
@Configuration
public class DataSourceConfig {/*** mysql数据源** @Param: []* @Return: javax.sql.DataSource* @Author: junlintianxia* @Date: 2024/4/26 11:19*/@Bean@ConfigurationProperties("spring.datasource.mysql")DataSource dataSourceMysql() {return DataSourceBuilder.create().build();}/*** oracle数据源** @Param: []* @Return: javax.sql.DataSource* @Author: junlintianxia* @Date: 2024/4/26 11:19*/@Bean@ConfigurationProperties("spring.datasource.oracle")DataSource dataSourceOracle() {return DataSourceBuilder.create().build();}//    /**
//     * postgresql数据源
//     *
//     * @Param: []
//     * @Return: javax.sql.DataSource
//     * @Author: junlintianxia
//     * @Date: 2024/4/26 11:19
//     */
//    @Bean
//    @ConfigurationProperties("spring.datasource.postgresql")
//    DataSource dataSourcePostgresql() {
//        return DruidDataSourceBuilder.create().build();
//    }}

 JdbcTemplate配置类JdbcTemplateConfig.class,用DataSource生成JdbcTemplate对象,用于ddl相关操作;

package org.sharetek.multids.config;import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;import javax.sql.DataSource;/*** @author junlintianxia* @title JdbcTemplateConfig* @date 2024/4/25 17:24* @description JdbcTemplate配置类*/
@Configuration
public class JdbcTemplateConfig {/*** mysql数据源** @Param: [dataSource]* @Return: org.springframework.jdbc.core.JdbcTemplate* @Author: junlintianxia* @Date: 2024/4/26 11:25*/@BeanJdbcTemplate getJdbcTemplateMysql(@Qualifier("dataSourceMysql") DataSource dataSource){return new JdbcTemplate(dataSource);}/*** oracle数据源** @Param: [dataSource]* @Return: org.springframework.jdbc.core.JdbcTemplate* @Author: junlintianxia* @Date: 2024/4/26 11:25*/@BeanJdbcTemplate getJdbcTemplateOracle(@Qualifier("dataSourceOracle") DataSource dataSource){return new JdbcTemplate(dataSource);}//    /**
//     * postgresql数据源
//     *
//     * @Param: [dataSource]
//     * @Return: org.springframework.jdbc.core.JdbcTemplate
//     * @Author: junlintianxia
//     * @Date: 2024/4/26 11:25
//     */
//    @Bean
//    JdbcTemplate getJdbcTemplatePostgresql(@Qualifier("dataSourcePostgresql") DataSource dataSource)
//    {
//        return new JdbcTemplate(dataSource);
//    }}

MySQL的crud工具类如下,

package org.sharetek.multids.utils;import cn.hutool.core.util.ObjectUtil;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
//import org.sharetek.multids.config.MysqlProperties;
import org.sharetek.multids.config.MysqlProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** @author tjm* @title MysqlUtils* @date 2024/4/25 14:20* @description mysql操作类,包括增删改查*/
@Slf4j
@Component
public class MysqlHelper {//定义数据库的连接private Connection connection;//定义sql语句的执行对象private PreparedStatement pStatement;//定义查询返回的结果集合private ResultSet resultset;private static final String SQL = "SELECT * FROM ";     // 数据库操作@Autowiredprivate MysqlProperties mysqlProperties;@PostConstructpublic void init() {try {Class.forName(mysqlProperties.getDriverClassName()); //注册驱动connection = DriverManager.getConnection(mysqlProperties.getJdbcUrl(), mysqlProperties.getUserName(), mysqlProperties.getPassWord()); //定义连接} catch (Exception e) {// TODO: handle exceptione.printStackTrace();}}/*** 对数据库的增/删/改** @Param: [sql, params]* @Return: boolean* @Author: tjm* @Date: 2024/4/25 14:30*/public boolean addDeleteModify(String sql, List<Object> params) throws SQLException {int result = -1;//设置为pStatement = connection.prepareStatement(sql);  //填充占位符int index = 1; //从第一个开始添加if (params != null && !params.isEmpty()) {for (int i = 0; i < params.size(); i++) {pStatement.setObject(index++, params.get(i));//填充占位符}}result = pStatement.executeUpdate();    //执行成功将返回大于0的数return result > 0 ? true : false;}/*** 查询单条记录** @Param: [sql: sql语句, params: 占位符]* @Return: java.util.Map<java.lang.String, java.lang.Object>* @Author: tjm* @Date: 2024/4/25 14:32*/public Map<String, Object> getSimpleResult(String sql, List<Object> params) throws SQLException {Map<String, Object> map = new HashMap<>();int index = 1;  //从1开始设置占位符pStatement = connection.prepareStatement(sql);// 判断参数是否为空if (params != null && !params.isEmpty()) {// 循环填充占位符for (int i = 0; i < params.size(); i++) {pStatement.setObject((i+1), params.get(i));}}resultset = pStatement.executeQuery();// 将查询结果封装到map集合ResultSetMetaData metaDate = resultset.getMetaData();   //获取resultSet列的信息int columnLength = metaDate.getColumnCount();   //获得列的长度while (resultset.next()) {for (int i = 0; i < columnLength; i++) {String metaDateKey = metaDate.getColumnName(i + 1); //获得列名Object resultsetValue = resultset.getObject(metaDateKey);   //通过列名获得值if (resultsetValue == null) {resultsetValue = "";    //转成String类型}//添加到map集合(以上代码是为了将从数据库返回的值转换成map的key和value)map.put(metaDateKey, resultsetValue);}break;}return map;}/*** 查询多条记录** @Param: [sql: sql语句, params: 占位符]* @Return: java.util.List<java.util.Map < java.lang.String, java.lang.Object>>* @Author: tjm* @Date: 2024/4/25 14:35*/public List<Map<String, Object>> getMultipleResult(String sql, List<Object> params) throws SQLException {List<Map<String, Object>> list = new ArrayList<>();//填充占位符int index = 1;pStatement = connection.prepareStatement(sql);if (params != null && !params.isEmpty()) {for (int i = 0; i < params.size(); i++) {pStatement.setObject(index++, params.get(i));}}//执行SQL语句resultset = pStatement.executeQuery();//封装resultset成map类型ResultSetMetaData metaDate = resultset.getMetaData();//获取列信息,交给metaDateint columnLength = metaDate.getColumnCount();while (resultset.next()) {Map<String, Object> map = new HashMap<>();for (int i = 0; i < columnLength; i++) {// 获取列名String metaDateKey = metaDate.getColumnName(i + 1);Object resultsetValue = resultset.getObject(metaDateKey);if (resultsetValue == null) {resultsetValue = "";}map.put(metaDateKey, resultsetValue);}list.add(map);}return list;}/*** 应用反射机制返回单条记录** @Param: [sql: sql语句, params: 占位符, javabean,会执行javabean类里面的toString方法]* @Return: T 泛型* @Author: tjm* @Date: 2024/4/25 14:38*/public <T> T getSimpleResult_Ref(String sql, List<Object> params, Class<T> javabean) throws Exception {T result = null;int index = 1;pStatement = connection.prepareStatement(sql);if (ObjectUtil.isNotEmpty(params)) {for (int i = 0; i < params.size(); i++) {pStatement.setObject(index++, params.get(i));}}resultset = pStatement.executeQuery(sql);//封装resultsetResultSetMetaData metaData = resultset.getMetaData();//获得列的信息int columnLength = metaData.getColumnCount();//获得列的长度while (resultset.next())//循环取值{result = javabean.newInstance();//通过反射机制创建一个实例for (int i = 0; i < columnLength; i++) {String metaDateKey = metaData.getColumnName(i + 1);Object resultsetValue = resultset.getObject(metaDateKey);if (resultsetValue == null) {resultsetValue = "";}//获取列的属性,无论是公有。保护还是私有,都可以获取Field field = javabean.getDeclaredField(metaDateKey);field.setAccessible(true);//打开javabean的访问private权限field.set(result, resultsetValue);//给javabean对应的字段赋值}}return result;}/*** 通过反射机制访问数据库,并返回多条记录** @Param: [sql: sql语句, params: 占位符,Javabean,会执行javabean类里面的toString方法]* @Return: java.util.List<T>* @Author: tjm* @Date: 2024/4/25 14:52*/public <T> List<T> getMultipleResult_Ref(String sql, List<Object> params, Class<T> javabean) throws Exception {List<T> list = new ArrayList<T>();int index = 1;pStatement = connection.prepareStatement(sql);if (params != null && !params.isEmpty()) {for (int i = 0; i < params.size(); i++) {pStatement.setObject(index, params.get(i));}}resultset = pStatement.executeQuery(sql);//封装resultsetResultSetMetaData metaData = resultset.getMetaData();//取出列的信息int columnLength = metaData.getColumnCount();//获取列数while (resultset.next()) {T tResult = javabean.newInstance();//通过反射机制创建一个对象for (int i = 0; i < columnLength; i++) {String metaDataKey = metaData.getColumnName(i + 1);Object resultsetValue = resultset.getObject(metaDataKey);if (resultsetValue == null) {resultsetValue = "";}Field field = javabean.getDeclaredField(metaDataKey);field.setAccessible(true);field.set(tResult, resultsetValue);}list.add(tResult);}return list;}/*** 获取数据库下的所有表名** @Param: []* @Return: java.util.List<java.lang.String>* @Author: tjm* @Date: 2024/4/25 15:00*/public List<String> getTableNames() {List<String> tableNames = new ArrayList<>();ResultSet rs = null;try {//获取数据库的元数据DatabaseMetaData db = connection.getMetaData();//从元数据中获取到所有的表名rs = db.getTables(null, null, null, new String[]{"TABLE"});while (rs.next()) {tableNames.add(rs.getString(3));}} catch (SQLException e) {log.error("getTableNames failure", e);} finally {try {rs.close();closeConnection();} catch (SQLException e) {log.error("close ResultSet failure", e);}}return tableNames;}/*** 获取表中所有字段名称** @Param: [tableName]* @Return: java.util.List<java.lang.String>* @Author: tjm* @Date: 2024/4/25 15:01*/public List<String> getColumnNames(String tableName) {List<String> columnNames = new ArrayList<>();PreparedStatement pStemt = null;String tableSql = SQL + tableName;try {pStemt = connection.prepareStatement(tableSql);//结果集元数据ResultSetMetaData rsmd = pStemt.getMetaData();//表列数int size = rsmd.getColumnCount();for (int i = 0; i < size; i++) {columnNames.add(rsmd.getColumnName(i + 1));}} catch (SQLException e) {log.error("getColumnNames failure", e);} finally {if (pStemt != null) {try {pStemt.close();closeConnection();} catch (SQLException e) {log.error("getColumnNames close pstem and connection failure", e);}}}return columnNames;}/*** 获取表中所有字段类型** @Param: [tableName]* @Return: java.util.List<java.lang.String>* @Author: tjm* @Date: 2024/4/25 15:02*/public List<String> getColumnTypes(String tableName) {List<String> columnTypes = new ArrayList<>();PreparedStatement pStemt = null;String tableSql = SQL + tableName;try {pStemt = connection.prepareStatement(tableSql);//结果集元数据ResultSetMetaData rsmd = pStemt.getMetaData();//表列数int size = rsmd.getColumnCount();for (int i = 0; i < size; i++) {columnTypes.add(rsmd.getColumnTypeName(i + 1));}} catch (SQLException e) {log.error("getColumnTypes failure", e);} finally {if (pStemt != null) {try {pStemt.close();closeConnection();} catch (SQLException e) {log.error("getColumnTypes close pstem and connection failure", e);}}}return columnTypes;}/*** 获取表中所有字段的注释** @Param: [tableName]* @Return: java.util.List<java.lang.String>* @Author: tjm* @Date: 2024/4/25 15:02*/public List<String> getColumnComments(String tableName) {List<String> columnTypes = new ArrayList<>();PreparedStatement pStemt = null;String tableSql = SQL + tableName;List<String> columnComments = new ArrayList<>();//列名注释集合ResultSet rs = null;try {pStemt = connection.prepareStatement(tableSql);rs = pStemt.executeQuery("show full columns from " + tableName);while (rs.next()) {columnComments.add(rs.getString("Comment"));}} catch (SQLException e) {e.printStackTrace();} finally {if (rs != null) {try {rs.close();closeConnection();} catch (SQLException e) {log.error("getColumnComments close ResultSet and connection failure", e);}}}return columnComments;}/*** 获取当前数据库下的所有表名称** @Param: []* @Return: java.util.List* @Author: tjm* @Date: 2024/4/25 15:04*/public List getAllTableName() throws Exception {List tables = new ArrayList();Statement stmt = connection.createStatement();ResultSet rs = stmt.executeQuery("SHOW TABLES ");while (rs.next()) {String tableName = rs.getString(1);tables.add(tableName);}rs.close();stmt.close();closeConnection();return tables;}/*** 根据表名获得建表sql语句(批量)** @Param: [tableNames]* @Return: java.util.Map* @Author: tjm* @Date: 2024/4/25 15:05*/public Map<String, String> getCreateSqlByTableNameBatch(List<String> tableNames) throws Exception {Map<String, String> map = new HashMap<>();Statement stmt = connection.createStatement();for (int i = 0; i < tableNames.size(); i++) {String table = tableNames.get(i);ResultSet rs = stmt.executeQuery("SHOW CREATE TABLE " + table);if (rs != null && rs.next()) {String createDDL = rs.getString(2);String comment = parse(createDDL);map.put(table, comment);}rs.close();}stmt.close();closeConnection();return map;}/*** 根据表名获得所有字段名及注释** @Param: [tableName]* @Return: void* @Author: tjm* @Date: 2024/4/25 15:07*/public Map<String, String> getColumnCommentByTableName(String tableName) throws Exception {Map<String, String> map = new HashMap();Statement stmt = connection.createStatement();ResultSet rs = stmt.executeQuery("show full columns from " + tableName);if (rs != null && rs.next()) {map.put(rs.getString("Field"), rs.getString("Comment"));}rs.close();stmt.close();closeConnection();return map;}/*** 返回注释信息** @Param: [all]* @Return: java.lang.String* @Author: tjm* @Date: 2024/4/25 15:04*/public static String parse(String all) {String comment = null;int index = all.indexOf("COMMENT='");if (index < 0) {return "";}comment = all.substring(index + 9);comment = comment.substring(0, comment.length() - 1);return comment;}/*** 关闭连接(注意在finally里面执行以下方法)** @Param: []* @Return: void* @Author: tjm* @Date: 2024/4/25 14:54*/public void closeConnection() {if (resultset != null) {try {resultset.close();} catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace();resultset = null;}}if (pStatement != null) {try {pStatement.close();} catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace();pStatement = null;}}if (connection != null) {try {connection.close();} catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace();connection = null;}}}}

mysql库ddl工具类MysqlUtils.class,用于对表格结构的操作;

package org.sharetek.multids.utils;import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.sharetek.multids.domain.AttributeItemEntity;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;import static org.sharetek.common.core.constant.MultiDbConstants.TABLE_PREFIX;/*** @author junlintianxia* @title MysqlUtils* @date 2024/4/25 10:54* @description mysql 工具类*/
@Slf4j
@Component
public class MysqlUtils {private JdbcTemplate jdbcTemplate;public MysqlUtils(@Qualifier("getJdbcTemplateMysql") JdbcTemplate jdbcTemplate) {this.jdbcTemplate = jdbcTemplate;}/*** 创建数据库** @Param: [dbName]* @Return: void* @Author: junlintianxia* @Date: 2024/4/25 11:12*/public void createDatabase(String dbName) {String query = "CREATE DATABASE IF NOT EXISTS " + dbName;jdbcTemplate.execute(query);}/*** 删除数据库** @Param: [dbName]* @Return: void* @Author: junlintianxia* @Date: 2024/4/25 11:12*/public void dropDatabase(String dbName) {String query = "DROP DATABASE IF EXISTS " + dbName;jdbcTemplate.execute(query);}/*** 创建表** @Param: [id, columns]* @Return: boolean* @Author: junlintianxia* @Date: 2024/4/25 13:21*/public boolean createTable(String definedName, List<AttributeItemEntity> columns) {return doCreateTable(TABLE_PREFIX + definedName, columns);}/*** 批量删除表** @Param: [tableNames]* @Return: boolean* @Author: junlintianxia* @Date: 2024/4/25 13:22*/@Transactional(rollbackFor = Throwable.class)public boolean dropTables(List<String> tableNames) {return dropTable(tableNames);}/*** 添加字段** @Param: [tableName, columns]* @Return: boolean* @Author: junlintianxia* @Date: 2024/4/25 13:22*/public boolean addColumns(String tableName, List<AttributeItemEntity> columns) {return doAddColumns(tableName, columns);}/*** 修改字段名称** @Param: [tableName, columns]* @Return: boolean* @Author: junlintianxia* @Date: 2024/4/25 13:23*/public boolean updateColumns(String tableName, List<AttributeItemEntity> columns) {return doUpdateColumns(tableName, columns);}/*** 删除字段** @Param: [id, columns]* @Return: boolean* @Author: junlintianxia* @Date: 2024/4/25 13:24*/public boolean dropColumns(Long id, List<AttributeItemEntity> columns) {return dropColumns(TABLE_PREFIX + id, columns);}/*** 获取列类型** @Param: [type, length]* @Return: java.lang.String* @Author: junlintianxia* @Date: 2024/4/25 13:39*/private String columnType(String type, Integer length) {if ("varchar".equalsIgnoreCase(type)) {if (ObjectUtil.isEmpty(length) || length == 0) {length = 255;}return type + "(" + length + ")";}return type;}/*** 创建表(详细)** @Param: [tableName, columns]* @Return: boolean* @Author: junlintianxia* @Date: 2024/4/25 13:26*/private boolean doCreateTable(String tableName, List<AttributeItemEntity> columns) {StringBuffer sb = new StringBuffer();sb.append("CREATE TABLE IF NOT EXISTS `" + tableName + "` (\n");sb.append("`id` bigint NOT NULL COMMENT '主键',\n");columns.forEach(e -> {sb.append("`" + e.getName() + "` " + columnType(e.getType(), e.getLength()) + " " + isNullSql(e.getEnableNull()) + " " + isDefaultSql(e.getDefaultValue()) + " COMMENT '" + e.getComment() + "',\n");if (e.getIsUnique()) {sb.append("UNIQUE KEY " + "uniq_" + e.getName() + " (`" + e.getName() + "`),\n");}});sb.append("`create_time` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '创建时间', \n");sb.append("PRIMARY KEY (`id`) \n");sb.append(");\n");return execute(sb);}/*** 添加字段(详细)** @Param: [tableName, columns]* @Return: boolean* @Author: junlintianxia* @Date: 2024/4/25 13:33*/private boolean doAddColumns(String tableName, List<AttributeItemEntity> columns) {StringBuffer sb = new StringBuffer();columns.forEach(e -> {sb.append("ALTER TABLE `" + tableName + "`  ADD COLUMN " + e.getName() + " " + columnType(e.getType(), e.getLength()) + " "+ isNullSql(e.getEnableNull()) + " " + isDefaultSql(e.getDefaultValue()) + " COMMENT '" + e.getComment() + "';\n");});return execute(sb);}/*** 修改字段(详细),只能改名称** @Param: [tableName, columns]* @Return: boolean* @Author: junlintianxia* @Date: 2024/4/25 13:34*/private boolean doUpdateColumns(String tableName, List<AttributeItemEntity> columns) {StringBuffer sb = new StringBuffer();columns.forEach(e -> {if (StrUtil.isNotEmpty(e.getNewName()) && !e.getName().equalsIgnoreCase(e.getNewName())) {sb.append("ALTER TABLE `" + tableName + "` RENAME COLUMN " + e.getName() + " TO " + e.getNewName() + ";\n");}});return execute(sb);}/*** 删除字段(详细)** @Param: [tableName, columns]* @Return: boolean* @Author: junlintianxia* @Date: 2024/4/25 13:34*/private boolean dropColumns(String tableName, List<AttributeItemEntity> columns) {StringBuffer sb = new StringBuffer();columns.forEach(e -> {sb.append("ALTER TABLE `" + tableName + "`  DROP COLUMN \"" + e.getName() + "\";\n");});return execute(sb);}/*** 批量删除表(详细)** @Param: [tableNames]* @Return: boolean* @Author: junlintianxia* @Date: 2024/4/25 13:34*/private boolean dropTable(List<String> tableNames) {StringBuffer sb = new StringBuffer();tableNames.forEach(tableName -> {sb.append("DROP TABLE IF EXISTS `" + tableName + "`;");});return execute(sb);}/*** 获取所有表名称** @Param: []* @Return: java.util.List<java.lang.String>* @Author: junlintianxia* @Date: 2024/4/25 13:21*/public List<String> tableNames() {List<String> tableNames = new ArrayList<>();try {Connection getConnection = jdbcTemplate.getDataSource().getConnection();DatabaseMetaData metaData = getConnection.getMetaData();ResultSet rs = metaData.getTables(getConnection.getCatalog(), null, null, new String[]{"TABLE"});while (rs.next()) {String tableName = rs.getString("TABLE_NAME");tableNames.add(tableName);}} catch (Exception e) {e.printStackTrace();}return tableNames;}/*** 执行sql** @Param: [sb]* @Return: boolean* @Author: junlintianxia* @Date: 2024/4/25 13:35*/private boolean execute(StringBuffer sb) {try {jdbcTemplate.execute(sb.toString());return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 是否可为空** @Param: [boo]* @Return: java.lang.String* @Author: junlintianxia* @Date: 2024/4/25 13:35*/private String isNullSql(Boolean boo) {if (boo) {return "NULL";}return "NOT NULL";}/*** 是否有默认值** @Param: [obj]* @Return: java.lang.String* @Author: junlintianxia* @Date: 2024/4/25 13:35*/private String isDefaultSql(Object obj) {if (ObjectUtil.isNotEmpty(obj)) {if (obj instanceof String) {return "DEFAULT '" + obj + "'";}return "DEFAULT " + obj;}return "";}/*** 获取所有表名** @Param: []* @Return: java.util.List<java.lang.String>* @Author: junlintianxia* @Date: 2024/4/25 11:11*/public List<String> getAllTableNames() {String query = "SHOW TABLES;";List<String> tableNames = new ArrayList<>();jdbcTemplate.query(query, rs -> {while (rs.next()) {tableNames.add(rs.getString(1));}});return tableNames;}/*** 获取表数据** @Param: [tableName]* @Return: java.util.List<java.util.Map < java.lang.String, java.lang.Object>>* @Author: junlintianxia* @Date: 2024/4/25 11:11*/public List<Map<String, Object>> getTableData(String tableName) {String query = "SELECT * FROM " + tableName + ";";return jdbcTemplate.queryForList(query);}}
数据源DML接口IDmlService.class,数据的增删改查,所有数据源通用;

package org.sharetek.multids.service;import java.sql.SQLException;
import java.util.List;
import java.util.Map;/*** @author tjm* @title IDmlService* @date 2024/4/29 10:58* @description 数据源DML接口*/
public interface IDmlService {/*** 对数据库的增/删/改** @Param: [sql, params]* @Return: boolean* @Author: tjm* @Date: 2024/4/29 11:16*/public boolean addDeleteModify(String sql, List<Object> params) throws SQLException;/*** 查询单条记录** @Param: [sql, params]* @Return: T* @Author: tjm* @Date: 2024/4/29 11:16*/Map<String, Object> getSimpleResult(String sql, List<Object> params) throws SQLException;/*** 查询多条记录** @Param: [sql, params]* @Return: java.util.List<T>* @Author: tjm* @Date: 2024/4/29 11:17*/public List<Map<String, Object>> getMultipleResult(String sql, List<Object> params) throws SQLException;/*** 应用反射机制返回单条记录** @Param: [sql, params, javabean]* @Return: T* @Author: tjm* @Date: 2024/4/29 11:17*/public <T> T getSimpleResult_Ref(String sql, List<Object> params, Class<T> javabean) throws Exception;/*** 通过反射机制访问数据库,并返回多条记录** @Param: [sql, params, javabean]* @Return: java.util.List<T>* @Author: tjm* @Date: 2024/4/29 11:17*/public <T> List<T> getMultipleResult_Ref(String sql, List<Object> params, Class<T> javabean) throws Exception;/*** 获取数据库下的所有表名** @Param: []* @Return: java.util.List<java.lang.String>* @Author: tjm* @Date: 2024/4/29 11:18*/public List<String> getTableNames();/*** 获取表下的所有列名** @Param: [tableName]* @Return: java.util.List<java.lang.String>* @Author: tjm* @Date: 2024/4/29 11:18*/public List<String> getColumnNames(String tableName);/*** 获取表中所有字段类型** @Param: [tableName]* @Return: java.util.List<java.lang.String>* @Author: tjm* @Date: 2024/4/29 11:19*/public List<String> getColumnTypes(String tableName);/*** 获取表中所有字段的注释** @Param: [tableName]* @Return: java.util.List<java.lang.String>* @Author: tjm* @Date: 2024/4/29 11:19*/public List<String> getColumnComments(String tableName);/*** 根据表名获得建表sql语句(批量)** @Param: [tableName]* @Return: java.util.List<java.lang.String>* @Author: tjm* @Date: 2024/4/29 11:19*/public Map<String, String> getCreateSqlByTableNameBatch(List<String> tableNames) throws Exception;/*** 根据表名获得所有字段名及注释** @Param: [tableName]* @Return: void* @Author: tjm* @Date: 2024/4/29 11:29*/public Map<String, String> getColumnCommentByTableName(String tableName) throws Exception;}

数据源DDL接口IDdlService.class,多数据库表结构的相关操作,所有数据源通用;

package org.sharetek.multids.service;import org.sharetek.multids.domain.AttributeItemEntity;import java.util.List;/*** @author junlintianxia* @title IDdlService* @date 2024/4/25 15:25* @description 数据源DDL接口*/
public interface IDdlService {/*** 创建数据库** @Param: [dbName]* @Return: boolean* @Author: junlintianxia* @Date: 2024/4/28 17:21*/public boolean createDatabase(String dbName);/*** 删除数据库** @Param: [dbName]* @Return: boolean* @Author: junlintianxia* @Date: 2024/4/28 17:21*/public void dropDatabase(String dbName);/*** 创建表** @Param: [definedName, columns]* @Return: boolean* @Author: junlintianxia* @Date: 2024/4/28 17:21*/public boolean createTable(String definedName, List<AttributeItemEntity> columns);/*** 删除表** @Param: [tableNames]* @Return: boolean* @Author: junlintianxia* @Date: 2024/4/28 17:22*/public boolean dropTables(List<String> tableNames);/*** 添加列** @Param: [tableName, columns]* @Return: boolean* @Author: junlintianxia* @Date: 2024/4/28 17:22*/public boolean addColumns(String tableName, List<AttributeItemEntity> columns);/*** 修改列** @Param: [tableName, columns]* @Return: boolean* @Author: junlintianxia* @Date: 2024/4/28 17:22*/public boolean updateColumns(String tableName, List<AttributeItemEntity> columns);}

mysql库ddl操作service层实现类,对表结构的处理;

package org.sharetek.multids.service.impl.mysql;import lombok.RequiredArgsConstructor;
import org.sharetek.multids.domain.AttributeItemEntity;
import org.sharetek.multids.service.IDdlService;
import org.sharetek.multids.utils.MysqlUtils;
import org.springframework.stereotype.Service;import java.util.List;/*** @author junlintianxia* @title MysqlDdlServiceImpl* @date 2024/4/25 15:25* @description mysql数据库DDL服务实现类*/
@Service
@RequiredArgsConstructor
public class MysqlDdlServiceImpl implements IDdlService {private final MysqlUtils mysqlUtils;@Overridepublic boolean createDatabase(String dbName) {try {mysqlUtils.createDatabase(dbName);return true;}catch (Exception e) {e.printStackTrace();}return false;}@Overridepublic void dropDatabase(String dbName) {}/*** 创建表** @Param: [definedName, columns]* @Return: boolean* @Author: junlintianxia* @Date: 2024/4/29 17:45*/@Overridepublic boolean createTable(String definedName, List<AttributeItemEntity> columns) {return mysqlUtils.createTable(definedName, columns);}/*** 删除表** @Param: [tableNames]* @Return: boolean* @Author: junlintianxia* @Date: 2024/4/29 17:45*/@Overridepublic boolean dropTables(List<String> tableNames) {return mysqlUtils.dropTables(tableNames);}/*** 添加列** @Param: [tableName, columns]* @Return: boolean* @Author: junlintianxia* @Date: 2024/4/29 17:46*/@Overridepublic boolean addColumns(String tableName, List<AttributeItemEntity> columns) {return mysqlUtils.addColumns(tableName, columns);}/*** 修改列** @param tableName* @param columns* @Param: [tableName, columns]* @Return: boolean* @Author: junlintianxia* @Date: 2024/4/28 17:22*/@Overridepublic boolean updateColumns(String tableName, List<AttributeItemEntity> columns) {return mysqlUtils.updateColumns(tableName, columns);}
}

mysql库dml操作service层实现类MysqlDmlServiceImpl.class;

package org.sharetek.multids.service.impl.mysql;import lombok.RequiredArgsConstructor;
import org.sharetek.multids.service.IDmlService;
import org.sharetek.multids.utils.MysqlHelper;
import org.springframework.stereotype.Service;import java.sql.SQLException;
import java.util.List;
import java.util.Map;/*** @author junlintianxia* @title MysqlDmlServiceImpl* @date 2024/4/29 11:37* @description mysql数据库DML服务实现类*/
@Service
@RequiredArgsConstructor
public class MysqlDmlServiceImpl implements IDmlService {private final MysqlHelper mysqlHelper;/*** 对数据库的增/删/改** @param sql* @param params* @Param: [sql, params]* @Return: boolean* @Author: junlintianxia* @Date: 2024/4/29 11:16*/@Overridepublic boolean addDeleteModify(String sql, List<Object> params) throws SQLException {return mysqlHelper.addDeleteModify(sql, params);}/*** 查询单条记录** @param sql* @param params* @Param: [sql, params]* @Return: T* @Author: junlintianxia* @Date: 2024/4/29 11:16*/@Overridepublic Map<String, Object> getSimpleResult(String sql, List<Object> params) throws SQLException {return mysqlHelper.getSimpleResult(sql, params);}/*** 查询多条记录** @param sql* @param params* @Param: [sql, params]* @Return: java.util.List<T>* @Author: junlintianxia* @Date: 2024/4/29 11:17*/@Overridepublic List<Map<String, Object>> getMultipleResult(String sql, List<Object> params) throws SQLException {return mysqlHelper.getMultipleResult(sql, params);}/*** 应用反射机制返回单条记录** @param sql* @param params* @param javabean* @Param: [sql, params, javabean]* @Return: T* @Author: junlintianxia* @Date: 2024/4/29 11:17*/@Overridepublic <T> T getSimpleResult_Ref(String sql, List<Object> params, Class<T> javabean) throws Exception {return mysqlHelper.getSimpleResult_Ref(sql, params, javabean);}/*** 通过反射机制访问数据库,并返回多条记录** @param sql* @param params* @param javabean* @Param: [sql, params, javabean]* @Return: java.util.List<T>* @Author: junlintianxia* @Date: 2024/4/29 11:17*/@Overridepublic <T> List<T> getMultipleResult_Ref(String sql, List<Object> params, Class<T> javabean) throws Exception {return mysqlHelper.getMultipleResult_Ref(sql, params, javabean);}/*** 获取数据库下的所有表名** @Param: []* @Return: java.util.List<java.lang.String>* @Author: junlintianxia* @Date: 2024/4/29 11:18*/@Overridepublic List<String> getTableNames() {return mysqlHelper.getTableNames();}/*** 获取表下的所有列名** @param tableName* @Param: [tableName]* @Return: java.util.List<java.lang.String>* @Author: junlintianxia* @Date: 2024/4/29 11:18*/@Overridepublic List<String> getColumnNames(String tableName) {return mysqlHelper.getColumnNames(tableName);}/*** 获取表中所有字段类型** @param tableName* @Param: [tableName]* @Return: java.util.List<java.lang.String>* @Author: junlintianxia* @Date: 2024/4/29 11:19*/@Overridepublic List<String> getColumnTypes(String tableName) {return mysqlHelper.getColumnTypes(tableName);}/*** 获取表中所有字段的注释** @param tableName* @Param: [tableName]* @Return: java.util.List<java.lang.String>* @Author: junlintianxia* @Date: 2024/4/29 11:19*/@Overridepublic List<String> getColumnComments(String tableName) {return mysqlHelper.getColumnComments(tableName);}/*** 根据表名获得建表sql语句(批量)** @param tableNames* @Param: [tableName]* @Return: java.util.List<java.lang.String>* @Author: junlintianxia* @Date: 2024/4/29 11:19*/@Overridepublic Map<String, String> getCreateSqlByTableNameBatch(List<String> tableNames) throws Exception {return mysqlHelper.getCreateSqlByTableNameBatch(tableNames);}/*** 根据表名获得所有字段名及注释** @param tableName* @Param: [tableName]* @Return: void* @Author: junlintianxia* @Date: 2024/4/29 11:29*/@Overridepublic Map<String, String> getColumnCommentByTableName(String tableName) throws Exception {return mysqlHelper.getColumnCommentByTableName(tableName);}}

package org.sharetek.multids.domain;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;/*** @author junlintianxia* @title AttributeItemEntity* @date 2024/4/25 11:48* @description 数据库表的field实体*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AttributeItemEntity {public AttributeItemEntity(String name, String type, String comment, Integer length, Boolean enableNull, Boolean isUnique) {this.name = name;this.type = type;this.comment = comment;this.length = length;this.enableNull = enableNull;this.isUnique = isUnique;}// 当前名称private String name;// 新名称private String newName;// 类型private String type;// 注释private String comment;// 默认值private String defaultValue = "";// 长度private Integer length = 32;// 是否允许为空private Boolean enableNull = true;// 别名private String alias;// 是否唯一private Boolean isUnique = false;}

用户操作入口DDLController.class,表结构相关操作,

package org.sharetek.multids.controller;import jakarta.annotation.Resource;
import jakarta.validation.constraints.NotEmpty;
import org.sharetek.common.log.annotation.Log;
import org.sharetek.common.log.enums.BusinessType;
import org.sharetek.multids.domain.AttributeItemEntity;
import org.sharetek.multids.service.IDdlService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;import java.util.ArrayList;
import java.util.List;/*** ddl控制器** @Param:* @Return:* @Author: junlintianxia* @Date: 2024/4/25 15:42*/
@Validated
@RestController
@RequestMapping("/ddl")
public class DDLController {@Resource(name = "mysqlDdlServiceImpl")private IDdlService ddlService;/*** 新建数据库*/@Log(title = "新建数据库", businessType = BusinessType.INSERT)@PostMapping(value = "/addDB")public boolean addDB(String dbName) {return ddlService.createDatabase(dbName);}/*** 新建表*/@Log(title = "新建表", businessType = BusinessType.INSERT)@PostMapping(value = "/addTable")
//    public boolean addTable(String tableName, List<AttributeItemEntity> columns) {public boolean addTable() {String definedName = "student2";List<AttributeItemEntity> columns = new ArrayList<>();AttributeItemEntity column = new AttributeItemEntity();column.setName("stuNo");column.setType("VARCHAR");column.setLength(11);column.setDefaultValue("");column.setEnableNull(true);column.setComment("学号");columns.add(column);return ddlService.createTable(definedName, columns);}/*** 删除表(批量)*/@Log(title = "删除表", businessType = BusinessType.DELETE)@DeleteMapping("/delTables/{tableNames}")public boolean delTables(@NotEmpty(message = "表名称不能为空")@PathVariable String[] tableNames) {return ddlService.dropTables(List.of(tableNames));}/*** 添加列(批量)*/@Log(title = "添加列", businessType = BusinessType.INSERT)@PostMapping("/addColumns/{tableName}")
//    public boolean addColumns(@PathVariable String tableName, @RequestBody List<AttributeItemEntity> columns) {public boolean addColumns() {String tableName = "table_student";List<AttributeItemEntity> columns = new ArrayList<>();columns.add(new AttributeItemEntity("stuName", "VARCHAR", "姓名", 32, true, false));return ddlService.addColumns(tableName, columns);}/*** 修改列名*/@Log(title = "修改列名", businessType = BusinessType.UPDATE)@PutMapping("/updateColumns/{tableName}")
//    public boolean updateColumns(@PathVariable String tableName, @RequestBody List<AttributeItemEntity> columns) {public boolean updateColumns() {String tableName = "table_student";List<AttributeItemEntity> columns = new ArrayList<>();AttributeItemEntity column = new AttributeItemEntity();column.setName("stuName");column.setNewName("stuName2");columns.add(column);return ddlService.updateColumns(tableName, columns);}}

用户crud操作控制器入口,DMLController.class,所有数据源通用;

package org.sharetek.multids.controller;import cn.hutool.core.date.DateTime;
import jakarta.annotation.Resource;
import org.sharetek.common.log.annotation.Log;
import org.sharetek.common.log.enums.BusinessType;
import org.sharetek.multids.service.IDmlService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.sql.SQLException;
import java.util.List;
import java.util.Map;/*** @author junlintianxia* @title DMLController* @date 2024/4/29 10:54* @description dml控制器*/
@Validated
@RestController
@RequestMapping("/dml")
public class DMLController {@Resource(name = "mysqlDmlServiceImpl")private IDmlService dmlService;/*** 对数据库的增/删/改*/@Log(title = "对数据库的增/删/改", businessType = BusinessType.OTHER)@PostMapping(value = "/addDeleteModify")
//    public boolean addDeleteModify(String sql, List<Object> params) throws SQLException {public boolean addDeleteModify() throws SQLException {// testString sql = "insert into table_student(id,stuNo,addr,create_time) values(?,?,?,?)";List<Object> params = List.of(1, "no001", "xi'an", new DateTime());return dmlService.addDeleteModify(sql, params);}/*** 查询单条记录*/@Log(title = "查询单条记录", businessType = BusinessType.QUERY)@GetMapping(value = "/getSimpleResult")
//    public Map<String, Object> getSimpleResult(String sql, List<Object> params) throws SQLException {public Map<String, Object> getSimpleResult() throws SQLException {// test 查询String sql = "select * from table_student where id = ?";List<Object> params = List.of(1L);return dmlService.getSimpleResult(sql, params);}/*** 查询多条记录*/@Log(title = "查询多条记录", businessType = BusinessType.QUERY)@GetMapping(value = "/getMultipleResult")
//    public List<Map<String, Object>> getMultipleResult(String sql, List<Object> params) throws SQLException {public List<Map<String, Object>> getMultipleResult() throws SQLException {// test 查询String sql = "select * from table_student where addr = ?";List<Object> params = List.of("xi'an");return dmlService.getMultipleResult(sql, params);}}

2.2.微服务方案,数据库配置信息通过nacos读取

pom.xml补充如下,

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://maven.apache.org/POM/4.0.0"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><groupId>org.sharetek</groupId><artifactId>sharetek-visual</artifactId><version>${revision}</version></parent><modelVersion>4.0.0</modelVersion><artifactId>sharetek-multi-datasource</artifactId><description>sharetek-multi-datasource 多数据源管理</description><dependencies><!-- SpringCloud Alibaba Nacos --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!-- SpringCloud Alibaba Nacos Config --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency><dependency><groupId>org.sharetek</groupId><artifactId>sharetek-common-web</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.23</version></dependency><!-- 数据库连接池--><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.16</version></dependency><!-- sharetek Common Log --><dependency><groupId>org.sharetek</groupId><artifactId>sharetek-common-log</artifactId></dependency><!-- Mysql Connector --><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId></dependency><!-- Oracle --><dependency><groupId>com.oracle.database.jdbc</groupId><artifactId>ojdbc8</artifactId></dependency><!-- PostgreSql --><dependency><groupId>org.postgresql</groupId><artifactId>postgresql</artifactId></dependency><!-- SqlServer --><dependency><groupId>com.microsoft.sqlserver</groupId><artifactId>mssql-jdbc</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId></dependency><dependency><groupId>com.zaxxer</groupId><artifactId>HikariCP</artifactId></dependency></dependencies><build><finalName>${project.artifactId}</finalName><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>${spring-boot.version}</version><executions><execution><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build></project>

在application.yml中注释掉数据库配置信息,

# Tomcat
server:port: 9305# Spring
spring:application:# 应用名称name: sharetek-multi-datasourceprofiles:# 环境配置active: @profiles.active@--- # nacos 配置
spring:cloud:nacos:# nacos 服务地址server-addr: @nacos.server@discovery:# 注册组group: @nacos.discovery.group@namespace: ${spring.profiles.active}cluster-name: xaconfig:# 配置组group: @nacos.config.group@namespace: ${spring.profiles.active}config:import:- optional:nacos:application-common.yml- optional:nacos:datasource.yml- optional:nacos:${spring.application.name}.yml#  datasource:
#    type: com.zaxxer.hikari.HikariDataSource
#    # 数据源
#    mysql:
#      type: ${spring.datasource.type}
#      driver-class-name: com.mysql.cj.jdbc.Driver
#      jdbc-url: jdbc:mysql://localhost:3306/sharetek-demodb?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
#      username: xxx
#      password: xxx
#    oracle:
#      type: ${spring.datasource.type}
#      driverClassName: oracle.jdbc.OracleDriver
#      url: jdbc:oracle:thin:@//localhost:1521/XE
#      username: xxx
#      password: xxx
#      hikari:
#        connectionTestQuery: SELECT 1 FROM DUAL
#    postgres:
#      type: ${spring.datasource.type}
#      driverClassName: org.postgresql.Driver
#      url: ${datasource.system-postgres.url}
#      username: ${datasource.system-postgres.username}
#      password: ${datasource.system-postgres.password}

 

 

 如果启动项目时,提示NullPointException,如下,

解决方法如下, 

3.测试效果


http://www.mrgr.cn/p/25163805

相关文章

【数据结构】C++语言实现栈(详细解读)

c语言中的小小白-CSDN博客c语言中的小小白关注算法,c,c语言,贪心算法,链表,mysql,动态规划,后端,线性回归,数据结构,排序算法领域.https://blog.csdn.net/bhbcdxb123?spm1001.2014.3001.5343 给大家分享一句我很喜欢我话&#xff1a; 知不足而奋进&#xff0c;望远山而前行&am…

ai写作工具推荐:如何用AI人工智能进行写作

AI写作工具&#xff1a;提升创作效率的秘密武器 在科技日新月异的今天&#xff0c;人工智能&#xff08;AI&#xff09;已经渗透到我们生活的方方面面&#xff0c;包括写作。AI写作工具&#xff0c;就是利用人工智能技术&#xff0c;帮助我们进行文本生成、语言优化等工作的工…

基于EWT联合SVD去噪

一、代码原理 &#xff08;1&#xff09;基于EWT-SVD的信号去噪算法原理 经验小波变换&#xff08;Empirical Wavelet Transform&#xff0c;EWT&#xff09;&#xff1a;EWT是一种基于信号局部特征的小波变换方法&#xff0c;能够更好地适应非线性和非平稳信号的特性。奇异值…

ChatGPT的真实能力如何?七大NLP任务一探究竟!

文章链接&#xff1a;https://arxiv.org/pdf/2405.00704 ChatGPT已经改变了人工智能社区&#xff0c;一个活跃的研究方向是ChatGPT的性能评估。评估的一个关键挑战是ChatGPT仍然是闭源的&#xff0c;传统的基准数据集可能已被ChatGPT用作训练数据。在本文中: 调查了最近的研究…

2024年CMS市场的份额趋势和使用统计

目前市面上有超过一半的网站都是使用CMS来搭建的&#xff0c;据不完全统计&#xff0c;现在大概有900多种CDM可供选择&#xff0c;以下是最常见的CMS的市场份额和使用率信息&#xff1a; 除了WordPress以外&#xff0c;Shopify和Wix也是比较流行的内容管理系统&#xff0c;尤其…

jupyter notebook使用与本地位置设置

本地安装好Anaconda之后&#xff0c;自带的有Jupter notebook。 使用jupyter notebook 使用jupyter notebook时&#xff0c;可以直接打开或者搜索打开&#xff1a; 打开后&#xff0c;我们生成的或者编辑的一些文件&#xff0c;都可以看到&#xff0c;如下&#xff1a; j…

一起了解开源自定义表单的优势表现

随着社会的进步和科技的发展&#xff0c;越来越多的中小企业希望采用更为先进的软件平台&#xff0c;助力企业实现高效率的流程化管理。低代码技术平台、开源自定义表单已经慢慢走入大众视野&#xff0c;成为一款灵活、高效的数字化转型工具。流辰信息专注于低代码技术平台的研…

QT的TcpServer

Server服务器端 QT版本5.6.1 界面设计 工程文件&#xff1a; 添加 network 模块 头文件引入TcpServer类和TcpSocket&#xff1a;QTcpServer和QTcpSocket #include <QTcpServer> #include <QTcpSocket>创建server对象并实例化&#xff1a; /*h文件中*/QTcpServer…

38-1 防火墙了解

一、防火墙的概念: 防火墙(Firewall),也称防护墙,是由Check Point创立者Gil Shwed于1993年发明并引入国际互联网(US5606668 [A]1993-12-15)。它是一种位于内部网络与外部网络之间的网络安全系统,是一项信息安全的防护系统,依照特定的规则,允许或是限制传输的数据通过。…

java-函数式编程-函数对象

定义 什么是合格的函数&#xff1f;无论多少次执行函数&#xff0c;只要输入一样&#xff0c;输出就不会改变 对象方法的简写 其实在类中&#xff0c;我们很多参数中都有一个this&#xff0c;被隐藏传入了 函数也可以作为对象传递&#xff0c;lambda就是很好的例子 函数式接口中…

监控操作台为生活提供安全保障

在科技日新月异的现代社会&#xff0c;监控操作台已成为我们生活中不能缺少的一部分。它犹如一座城市的守护神&#xff0c;默默无闻地守护着我们的安全&#xff0c;确保着每一刻的平安。今天&#xff0c;和北京嘉德立一同走进这个神秘的世界&#xff0c;揭开监控操作台的神秘面…

03_Redis

文章目录 Redis介绍安装及使用redis的核心配置数据结构常用命令stringlistsethashzset(sortedset) 内存淘汰策略Redis的Java客户端JedisRedisson Redis 介绍 Redis是一个NoSQL数据库。 NoSQL: not only SQL。表示非关系型数据库&#xff08;不支持SQL标准语法&#xff09;。 …

Mybatis逆向工程笔记小结

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java全栈-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 目录 1.前言 2.实现方案 2.1. mybatis-generator生成 2.1.1. 环境说明 2.1.2. 数…

算法学习:二分查找

&#x1f525; 引言 在现代计算机科学与软件工程的实践中&#xff0c;高效数据检索是众多应用程序的核心需求之一。二分查找算法&#xff0c;作为解决有序序列查询问题的高效策略&#xff0c;凭借其对数时间复杂度的优越性能&#xff0c;占据着算法领域里举足轻重的地位。本篇内…

【配置】Docker搭建JSON在线解析网站

一个python朋友需要&#xff0c;顺便做一下笔记 正常用菜鸟的就够了&#xff0c;点下面 JSON在线解析 云服务器打开端口8787 连接上docker运行 docker run -id --name jsonhero -p 8787:8787 -e SESSION_SECRETabc123 henryclw/jsonhero-webhttp://ip:8787访问 Github&…

合泰杯(HT32F52352)RTC的应用(计时)--->掉电不丢失VBAT(代码已经实现附带源码)

摘要 在HT32F52352合泰单片机开发中&#xff0c;rtc在网上还是挺少人应用的&#xff0c;找了很久没什么资料&#xff0c;现在我根据手册和官方的代码进行配置理解。 RTC在嵌入式单片机中是一个很重要的应用资源。 记录事件时间戳&#xff1a;RTC可以记录事件发生的精确时间&…

【高校科研前沿】中国科学院地理资源所钟帅副研究员研究组博士生朱屹东为一作在Top期刊发文:从潜力到利用:探索西藏风能资源开发的技术路径优化布局

01 文章简介 论文名称&#xff1a;From potential to utilization: Exploring the optimal layout with the technical path of wind resource development in Tibet&#xff08;从潜力到利用:探索西藏风能资源开发的技术路径优化布局&#xff09; 文章发表期刊&#xff1a;《…

【无标题】场外个股期权多少钱才能做?个人能做吗?

场外个股期权的交易门槛相对较高&#xff0c;主要面向符合特定条件的机构投资者。一般来说&#xff0c;法人或合伙企业等组织参与的&#xff0c;需要满足最近1年末净资产不低于5000万元人民币、金融资产不低于2000万元人民币的条件&#xff0c;并具备3年以上证券、基金、期货、…

(六)SQL系列练习题(下)#CDA学习打卡

目录 三. 查询信息 16&#xff09;检索"1"课程分数小于60&#xff0c;按分数降序排列的学生信息​ 17&#xff09;*按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩 18&#xff09;*查询各科成绩最高分、最低分和平均分 19&#xff09;*按各科成绩…

ORAN C平面优化

使用section扩展6的C平面优化 在时域和频域中&#xff0c;都可以使用section扩展6进行非连续PRB分配。Section扩展6有两个位掩码&#xff1a;symbolMask和rbgMask。使用symbolMask可以选择一个slot内任意的symbol子集。使用rbgMask可以选择startPrbc和&#xff08;startPrbc …