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

1.背景

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

2.实现方案

完整项目文件结构;

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

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

# Tomcat
server:
  port: 9305

# Spring
spring:
  application:
    # 应用名称
    name: sharetek-multi-datasource
  profiles:
    # 环境配置
    active: @profiles.active@

--- # nacos 配置
spring:
  cloud:
    nacos:
      # nacos 服务地址
      server-addr: @nacos.server@
      discovery:
        # 注册组
        group: @nacos.discovery.group@
        namespace: ${spring.profiles.active}
        cluster-name: xa
      config:
        # 配置组
        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}

 

启动类,

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
     */
    @Bean
    JdbcTemplate 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
     */
    @Bean
    JdbcTemplate 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 ";     // 数据库操作

    @Autowired
    private MysqlProperties mysqlProperties;

    @PostConstruct
    public void init() {
        try {
            Class.forName(mysqlProperties.getDriverClassName()); //注册驱动
            connection = DriverManager.getConnection(mysqlProperties.getJdbcUrl(), mysqlProperties.getUserName(), mysqlProperties.getPassWord()); //定义连接
        } catch (Exception e) {
            // TODO: handle exception
            e.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();//获取列信息,交给metaDate
        int 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);
        //封装resultset
        ResultSetMetaData 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);
        //封装resultset
        ResultSetMetaData 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 block
                e.printStackTrace();
                resultset = null;
            }
        }
        if (pStatement != null) {
            try {
                pStatement.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                pStatement = null;
            }
        }
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.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;

    @Override
    public boolean createDatabase(String dbName) {
        try {
            mysqlUtils.createDatabase(dbName);
            return true;
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public void dropDatabase(String dbName) {

    }

    /**
     * 创建表
     *
     * @Param: [definedName, columns]
     * @Return: boolean
     * @Author: junlintianxia
     * @Date: 2024/4/29 17:45
     */
    @Override
    public boolean createTable(String definedName, List<AttributeItemEntity> columns) {
        return mysqlUtils.createTable(definedName, columns);
    }

    /**
     * 删除表
     *
     * @Param: [tableNames]
     * @Return: boolean
     * @Author: junlintianxia
     * @Date: 2024/4/29 17:45
     */
    @Override
    public boolean dropTables(List<String> tableNames) {
        return mysqlUtils.dropTables(tableNames);
    }

    /**
     * 添加列
     *
     * @Param: [tableName, columns]
     * @Return: boolean
     * @Author: junlintianxia
     * @Date: 2024/4/29 17:46
     */
    @Override
    public 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
     */
    @Override
    public 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
     */
    @Override
    public 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
     */
    @Override
    public 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
     */
    @Override
    public 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
     */
    @Override
    public <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
     */
    @Override
    public <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
     */
    @Override
    public 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
     */
    @Override
    public 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
     */
    @Override
    public 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
     */
    @Override
    public 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
     */
    @Override
    public 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
     */
    @Override
    public 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 {
        // test
        String 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-datasource
  profiles:
    # 环境配置
    active: @profiles.active@

--- # nacos 配置
spring:
  cloud:
    nacos:
      # nacos 服务地址
      server-addr: @nacos.server@
      discovery:
        # 注册组
        group: @nacos.discovery.group@
        namespace: ${spring.profiles.active}
        cluster-name: xa
      config:
        # 配置组
        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.测试效果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/589144.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

IDEA启动项目报错:Error running ‘‘: Command line is too long.

1、在workspace.xml 2、 在标签 <component name"PropertiesComponent"> 添加 <property name"dynamic.classpath" value"true" />

MySQL 运维篇

回顾基本语句&#xff1a; 数据定义语言(DDL) 这类语言用于定义和修改数据库的结构&#xff0c;包括创建、删除和修改数据库、 表、视图和索引等对象。 主要的语句关键字包括 CREATE 、 DROP 、 ALTER 、 RENAME 、 TRUNCATE 等。 create database 数据库 &#xff1b; cr…

【MySQL | 第十一篇】一条SQL语句在MySQL的执行过程

文章目录 11.一条SQL语句在MySQL的执行过程11.1MySQL整体架构11.2SQL语句在Server层执行流程11.3拓展&#xff1a;InnoDB存储引擎的更新操作11.3.1问题&#xff1a;为什么写了redolog日志还要写binlog日志&#xff1f;11.3.2问题&#xff1a;为什么要两阶段提交&#xff1f;11.…

《QT实用小工具·四十七》可交互的创意动态按钮

1、概述 源码放在文章末尾 该项目实现了可交互的创意动态按钮&#xff0c;包含如下功能&#xff1a; 所有颜色自定义 鼠标悬浮渐变 两种点击效果&#xff1a;鼠标点击渐变 / 水波纹动画&#xff08;可多层波纹叠加&#xff09; 额外鼠标移入/移出/按下/弹起的实时/延迟共8种事…

springboot 自动配置源码解读

什么是自动装配 当我们程序依赖第三方功能组件时&#xff0c;不需要手动将这些组件类加载到IOC容器中。例如 当程序需要用到redis时&#xff0c;在pom.xml文件中引入依赖&#xff0c;然后使用依赖注入的方式直接从IOC容器中拿到相应RedisTemplate实例。 SpringBootApplication …

【已解决】json文件太大无法打开怎么办+ijson报错

下载了一个json文档&#xff0c;尝试了全部的编辑器都打不开。。。 搜了搜或许可以使用ijson GitHub - ICRAR/ijson: Iterative JSON parser with Pythonic interfaces "Ijson is an iterative JSON parser with standard Python iterator interfaces." 示例代码&…

【C++ —— 多态】

C —— 多态 多态的概念多态的定义和实现多态的构成条件虚函数虚函数的重写虚函数重写的两个例外协变&#xff1a;析构函数的重写 C11 override和final重载、覆盖(重写)、隐藏(重定义)的对比 抽象类概念接口继承和实现继承 多态的继承虚函数表多态的原理动态绑定和静态绑定 单继…

VTK 的可视化方法:Glyph

VTK 的可视化方法&#xff1a;Glyph VTK 的可视化方法&#xff1a;Glyph标量、向量、张量将多边形数据的采集点法向量标记成锥形符号参考 VTK 的可视化方法&#xff1a;Glyph 模型的法向量数据是向量数据&#xff0c;因此法向量不能像前面讲到的通过颜色映射来显示。但是可以通…

25 JavaScript学习:var let const

JavaScript全局变量 JavaScript中全局变量存在多种情况和定义方式&#xff0c;下面详细解释并提供相应的举例&#xff1a; 使用var关键字声明的全局变量&#xff1a; var globalVar "我是全局变量";未使用var关键字声明的变量会成为全局变量&#xff08;不推荐使用&…

【前端】-【防止接口重复请求】

文章目录 需求实现方案方案一方案二方案三 需求 对整个的项目都做一下接口防止重复请求的处理 实现方案 方案一 思路&#xff1a;通过使用axios拦截器&#xff0c;在请求拦截器中开启全屏Loading&#xff0c;然后在响应拦截器中将Loading关闭。 代码&#xff1a; 问题&…

(刷题记录2)随机链表的复制

[刷题记录2]随机链表的复制 题目信息&#xff1a;题目思路(环境来自力扣OJ的C语言)&#xff1a;复杂度&#xff1a;代码和解释&#xff1a;1.遍历一遍原链表的同时&#xff0c;在每个原节点后面插入一个相同的新节点&#xff0c;共插入 n 个新节点。2.利用两者联系&#xff0c;…

神奇的Vue3 - 组件探索

神奇的Vue3 第一章 神奇的Vue3—基础篇 第二章 神奇的Vue3—Pinia 文章目录 神奇的Vue3了解组件一、注册组件1. 全局注册​2. 局部注册3. 组件命名 二、属性详解1. Props&#xff08;1&#xff09;基础使用方法&#xff08;2&#xff09;数据流向&#xff1a;单项绑定原则&…

ThreeJS:Mesh网格与三维变换

Mesh网格 ThreeJS中&#xff0c;Mesh表示基于以三角形为多边形网格(polygon mesh)的物体的类&#xff0c;同时也作为其它类的基类。 通过Mesh网格&#xff0c;我们可以组合Geometry几何体与Material材质属性&#xff0c;在3D世界中&#xff0c;定义一个物体。例如&#xff1a;之…

vue2(4)之scoped解决样式冲突/组件通信/非父子通信/ref和$refs/异步更新/.sync/事件总线/provide和inject

vue2 一、学习目标1.组件的三大组成部分&#xff08;结构/样式/逻辑&#xff09;2.组件通信3.综合案例&#xff1a;小黑记事本&#xff08;组件版&#xff09;4.进阶语法 二、scoped解决样式冲突**1.默认情况**&#xff1a;2.代码演示3.scoped原理4.总结 三、data必须是一个函数…

Copilot Venture Studio創始合伙人楊林苑確認出席“邊緣智能2024 - AI開發者峰會”

隨著AI技術的迅猛發展&#xff0c;全球正逐步進入邊緣計算智能化與分布式AI深度融合的新時代&#xff0c;共同書寫著分布式智能創新應用的壯麗篇章。邊緣智能&#xff0c;作為融合邊緣計算和智能技術的新興領域&#xff0c;正逐漸成為推動AI發展的關鍵力量。借助分布式和去中心…

由于找不到mfc140u.dll,无法继续执行的多种解决方法

在我们日常与计算机的密切互动中&#xff0c;或许不少用户都曾遇到过这样一个棘手的问题&#xff1a;系统突然弹出一个提示窗口&#xff0c;告知我们“找不到mfc140u.dll文件”。这个文件是Microsoft Foundation Class&#xff08;MFC&#xff09;库的一部分&#xff0c;用于支…

提升编码技能:学习如何使用 C# 和 Fizzler 获取特价机票

引言 五一假期作为中国的传统节日&#xff0c;也是旅游热门的时段之一&#xff0c;特价机票往往成为人们关注的焦点。在这个数字化时代&#xff0c;利用爬虫技术获取特价机票信息已成为一种常见的策略。通过结合C#和Fizzler库&#xff0c;我们可以更加高效地实现这一目标&…

20240502在WIN10下给X99平台上的M6000显卡安装驱动程序

20240502在WIN10下给X99平台上的M6000显卡安装驱动程序 2024/5/2 9:32 人工智能计算领域的领导者 | NVIDIA https://www.nvidia.cn/ C:\NVIDIA\DisplayDriver\552.22\Win11_Win10-DCH_64\International IMPORTANT NOTICE -- READ CAREFULLY: -------------------------------…

pmp培训机构哪个比较好,求推荐-

寻找一个自己认为比较好的PMP培训机构千万不要盲目&#xff0c;先在网上看看大家都推荐什么&#xff0c;看一下各个机构的老学员反馈&#xff0c;这些对我们的选择有非常大的帮助&#xff0c;最起码有了一些风评上的参考&#xff0c;现状就是目前线上机构的竞争比较大&#xff…

c语言从入门到函数速成(1)

温馨提醒&#xff1a;本篇文章适合人群&#xff1a;刚学c又感觉那个地方不怎么懂的同学以及以及学了一些因为自身原因停学一段时间后又继续学c的同学 好&#xff0c;正片开始。 主函数 学c时最先学的是我们c语言程序的主体函数&#xff0c;c的主函数有两种写法&#xff0c;这…