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

1.背景

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

2.实现方案

完整项目文件结构;

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

启动类,

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);}}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构】算法的效率(时间复杂度和空间复杂度)

目录 一.算法的效率 二.时间复杂度 1.概念 2.大O的渐进表示法 3.常见时间复杂度计算举例 三.空间复杂度 四.常见复杂度对比 五. 复杂度的oj练习 1.消失的数字 2.轮转数字&#xff1a; 一.算法的效率 算法在编写成可执行程序后&#xff0c;运行时需要耗费时间资源和空…

【C++初阶】string

✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅ ✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨ &#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1…

【Linux】信号的产生

目录 一. 信号的概念signal() 函数 二. 信号的产生1. 键盘发送2. 系统调用kill()raise()abort() 3. 软件条件alarm() 4. 硬件异常除零错误:野指针: 三. 核心转储 一. 信号的概念 信号是消息的载体, 标志着不同的行为; 是进程间发送异步信息的一种方式, 属于软中断. 信号随时都…

智能优化算法及 MATLAB 实现(书籍推荐)

智能优化算法及 MATLAB 实现&#xff08;书籍推荐&#xff09; 介绍前言目录第1章 粒子群优化算法原理及其MATLAB实现第2章 哈里斯鹰优化算法原理及其MATLAB实现第3章 沙丘猫群优化算法原理及其MATLAB实现第4章 鲸鱼优化算法原理及其MATLAB实现第5章 大猩猩部队优化算法原理及其…

20232801 2023-2024-2 《网络攻防实践》实践八报告

20232801 2023-2024-2 《网络攻防实践》实践八报告 1.实践内容 1.动手实践任务: 对提供的rada恶意代码样本&#xff0c;进行文件类型识别&#xff0c;脱壳与字符串提取&#xff0c;以获得rada恶意代码的编写作者. 2.动手实践任务二&#xff1a;分析Crackme程序 在WinXP Attac…

Leetcode - 周赛395

目录 一&#xff0c;3131. 找出与数组相加的整数 I 二&#xff0c;3132. 找出与数组相加的整数 II 三&#xff0c;3133. 数组最后一个元素的最小值 四&#xff0c;3134. 找出唯一性数组的中位数 一&#xff0c;3131. 找出与数组相加的整数 I 本题本质就是求两个数组最小值的…

[iOS]组件化开发

一、组件化开发基础 1.组件定义 在软件开发中&#xff0c;一个组件是指一个独立的、可替换的软件单元&#xff0c;它封装了一组相关的功能。组件通过定义的接口与外界交互&#xff0c;并且这些接口隔离了组件内部的实现细节。在Swift语言中&#xff0c;组件可以是一个模块、一…

文献速递:肺癌早期诊断---低剂量胸部计算机断层扫描上的三维深度学习端到端肺癌筛查

Title 题目 End-to-end lung cancer screening with three-dimensional deep learning on low-dose chest computed tomography 低剂量胸部计算机断层扫描上的三维深度学习端到端肺癌筛查 01文献速递介绍 2018年估计有160,000例死亡病例&#xff0c;肺癌是美国最常见的癌症…

青少年软件编程(Scratch一级)等级考试试卷(2019年12月)

客观题 题目总数&#xff1a;35 总分数&#xff1a;70 时间&#xff1a;不限时 CSDN添加图片自带水印&#xff0c;可至文末获取无水印版word文档 第 1 题 单选题 下列关于舞台的描述&#xff0c;不正确的是&#xff1f;&#xff08; &#xff09; A.Scratch只能…

MVVM框架

LifeCycle 翻译为生命周期。意义就是为了监控某个事件的生命周期。 此处用了addObserver函数&#xff0c;代表添加监控者。监控该activity的变化。activity的状态有onCreate,onStop,onDestory等&#xff0c;该函数表示对所有状态进行监听。 该方法使用了标签的方法&#xff0c…

护航智慧交通安全 | 聚铭精彩亮相2024交通科技创新及信创产品推广交流会

4月26日&#xff0c;石家庄希尔顿酒店内&#xff0c;河北省智能交通协会盛大举办2024年度交通科技创新及信创产品推广交流会。聚铭网络受邀参与&#xff0c;携旗下安全产品及解决方案精彩亮相&#xff0c;为智慧交通安全保驾护航。 为深化高速公路创新驱动发展战略&#xff0…

2024 java easyexcel poi word模板填充数据,多个word合成一个word

先看效果 一、准备工作 1.word模版 2.文件路径 二、pom依赖 <!-- easyexcel --><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.1.7</version></dependency><depe…

如何申请免费SSL证书,把网站升级成HTTPS

HTTPS&#xff08;Hyper Text Transfer Protocol Secure&#xff09;是一种用于安全数据传输的网络协议&#xff0c;它可以有效地保护网站和用户之间的通信安全。然而&#xff0c;要使一个网站从HTTP升级到HTTPS&#xff0c;就需要一个SSL证书。那么&#xff0c;如何申请免费的…

一文解读 SQL 生成工具

SQL 生成工具可用于测试 Parser 与其他数据库产品的兼容性&#xff0c;通过解析 YACC 语法文件中的产生式&#xff0c;生成对应的 SQL 语句&#xff0c;再使用数据库执行该 SQL&#xff0c;根据结果判断语句是否与其他数据库语法兼容。 01工具使用 语法文件预处理 预处理目的…

notion使用小tip(待补充)

可以替代思维导图是一个很棒的软件 公式编辑&#xff1a;latex 网站链接&#xff1a;LATEX语法 一些常用的用法&#xff1a; 下标&#xff1a;a_{Si} 分数&#xff1a;\frac{}{} 乘&#xff1a;\times 向量&#xff1a;\vec{} pai (3.14159…) : \pi 直接用公式编辑器&#…

【kettle005】kettle访问Oracle数据库并处理数据至execl文件(已更新)

1.一直以来想写下基于kettle的系列文章&#xff0c;作为较火的数据ETL工具&#xff0c;也是日常项目开发中常用的一款工具&#xff0c;最近刚好挤时间梳理、总结下这块儿的知识体系。 2.熟悉、梳理、总结下Oracle数据库相关知识体系 3.欢迎批评指正&#xff0c;跪谢一键三连&am…

全栈从0到1 3D旅游地图标记和轨迹生成

功能演示 演示视频 体验地址 Vercel App 开发技术栈&#xff1a; NextJs&#xff08;前端框架&#xff09;React&#xff08;前端框架&#xff09;TailwindCSS &#xff08;CSS样式&#xff09;echart echart gl &#xff08;地图生成&#xff09;shadui&#xff08;UI组件…

C语言阶段性测试错题纠正与拓展

引言&#xff1a;在2024年4月26日&#xff0c;我进行了C语言知识的“期末考试”。通过这次考试&#xff0c;我发现了我的知识漏洞。所以&#xff0c;我写下这篇博客来记录我的错题&#xff0c;并进行纠正&#xff0c;然后对于以前遗忘知识的回顾。 更多有关C语言的知识详解可前…

HarmonyOS编程实践系列:第一节 - 创建健康App欢迎页

系列文章目录 &#xff08;零&#xff09;鸿蒙HarmonyOS入门&#xff1a;如何配置环境&#xff0c;输出“Hello World“ &#xff08;一&#xff09;鸿蒙HarmonyOS开发基础 &#xff08;二&#xff09;鸿蒙HarmonyOS主力开发语言ArkTS-基本语法 &#xff08;三&#xff09;鸿蒙…

梳理一下低代码的真正价值!另推荐超好用的5款低代码开发平台

一、先来聊聊低代码的真实价值&#xff01; 在回答这个问题之前&#xff0c;我们不妨先来看两个案例&#xff1a; 某连锁商超企业在发展中产生了新的业务需求&#xff0c;一是希望能够快速展示门店销售数据&#xff0c;满足高层的管理需求&#xff1b;二是希望巡店、上架商品…