一键建库,零操心!让数据库初始化像搭积木一样简单


告别手动建库、用户授权与脚本维护——一个智能、安全、多数据库兼容的自动化初始化引擎

从开发到测试再到部署,每次新建环境都意味着重复的手动操作:创建数据库、分配用户、授予权限、执行初始化脚本……不仅繁琐,还容易出错。

DatabaseInitializer 专为自动创建和初始化数据库环境而设计。无论你使用的是 MySQL、PostgreSQL、SQL Server 还是 Oracle,只需一行代码,即可完成整个数据库“从无到有”的搭建流程。

DatabaseInitializer 的核心能力:

✅ 自动识别 JDBC URL 类型,无需硬编码数据库类型

✅ 安全地以管理员身份连接,自动创建目标数据库与专属用户

✅ 精细权限控制:仅授予必要权限,遵循最小权限原则

✅ 内置重试与幂等机制,支持 CI/CD 与容器化部署

✅ 无缝集成 Spring Boot、Testcontainers 与单元测试

无论是快速启动本地开发环境,还是在 Kubernetes 中动态部署微服务,DatabaseInitializer 都能让你的数据库初始化变得可靠、可重复、可编程。

“基础设施即代码”不应止步于服务器——数据库也该如此。

话不多说,直接上石马:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 数据库自动创建工具类(支持非嵌入式数据库 MySQL / PostgreSQL / SQL Server / Oracle)
 * <p>
 * 功能:检查目标数据库是否存在,若不存在则自动创建。
 * - 对 MySQL/PostgreSQL/SQL Server:创建逻辑数据库
 * - 对 Oracle:创建用户(User),因为 Oracle 中用户 = Schema(等价于其他数据库的“数据库”)
 * </p>
 *
 * <h3>SQL 模板文件位置(可选)</h3>
 * <pre>
 * classpath:sql/create-database-mysql.sql
 * classpath:sql/create-database-postgresql.sql
 * classpath:sql/create-database-sqlserver.sql
 * classpath:sql/create-database-oracle.sql
 * </pre>
 * <p>
 * 模板中使用 {@code ${dbName}} 作为占位符(对 Oracle 即用户名),例如:
 * <pre>
 * CREATE USER "${dbName}" IDENTIFIED BY "secure_password";
 * GRANT CREATE SESSION, CREATE TABLE TO "${dbName}";
 * ALTER USER "${dbName}" QUOTA UNLIMITED ON USERS;
 * </pre>
 * </p>
 *
 * <h3>Oracle 特别说明</h3>
 * <ul>
 *   <li>必须使用具有 DBA 权限的账户(如 SYSTEM)连接</li>
 *   <li>不支持创建 Oracle 实例(Instance),仅创建用户(Schema)</li>
 *   <li>默认授予基本权限并设置 USERS 表空间无限配额</li>
 *   <li>密码在内置逻辑中生成随机值;建议通过模板自定义或由 DBA 预置</li>
 * </ul>
 *
 * <h3>使用示例</h3>
 * <pre>
 * // Oracle 示例(连接到 SYSTEM,创建用户 myapp)
 * DatabaseInitializer initializer = new DatabaseInitializer(
 *     DatabaseType.ORACLE,
 *     "localhost",
 *     1521,
 *     "system",
 *     "oracle"
 * );
 * initializer.ensureDatabaseExists("myapp");
 * </pre>
 *
 * @author seallon
 * @since JDK 8
 */
public class DatabaseInitializer {

    private static final Logger log = LoggerFactory.getLogger(DatabaseInitializer.class);

    private final DatabaseType dbType;
    private final String host;
    private final int port;
    private final String username;
    private final String password;
    private final java.util.Properties connectionProperties;

    /**
     * 构造函数
     *
     * @param dbType     数据库类型(MYSQL, POSTGRESQL, SQLSERVER, ORACLE)
     * @param host       数据库主机(不能为 null)
     * @param port       端口
     * @param username   系统用户(需有 CREATE DATABASE 或 CREATE USER 权限,不能为 null)
     * @param password   密码
     */
    public DatabaseInitializer(DatabaseType dbType, String host, int port, String username, String password) {
        this(dbType, host, port, username, password, new java.util.Properties());
    }

    /**
     * 带额外连接属性的构造函数(如 useSSL=false, sslmode=disable 等)
     *
     * @param dbType                数据库类型
     * @param host                  主机
     * @param port                  端口
     * @param username              用户名
     * @param password              密码
     * @param connectionProperties  额外 JDBC 连接参数(可为 null)
     */
    public DatabaseInitializer(DatabaseType dbType, String host, int port, String username, String password,
                               java.util.Properties connectionProperties) {
        if (dbType == null || host == null || username == null) {
            throw new IllegalArgumentException("dbType, host, username cannot be null");
        }
        this.dbType = dbType;
        this.host = host;
        this.port = port;
        this.username = username;
        this.password = password;
        this.connectionProperties = connectionProperties != null ? connectionProperties : new java.util.Properties();
    }

    /**
     * 确保目标“数据库”存在:
     * - MySQL/PG/SQLServer:确保逻辑数据库存在
     * - Oracle:确保用户(Schema)存在
     *
     * @param databaseName 目标名称(不能为 null 或空字符串)
     * @throws RuntimeException 若数据库操作失败
     */
    public void ensureDatabaseExists(String databaseName) {
        if (databaseName == null || databaseName.trim().isEmpty()) {
            throw new IllegalArgumentException("databaseName must not be null or empty");
        }
        String dbName = databaseName.trim();

        try (java.sql.Connection sysConn = createSystemConnection()) {
            if (!isDatabaseExists(sysConn, dbName)) {
                log.info("Target '{}' does not exist for {}. Creating...", dbName, dbType);
                createDatabase(sysConn, dbName);
                log.info("Target '{}' created successfully for {}.", dbName, dbType);
            } else {
                log.info("Target '{}' already exists for {}. Skipping creation.", dbName, dbType);
            }
        } catch (java.sql.SQLException e) {
            throw new RuntimeException("Failed to ensure target '" + dbName + "' exists for " + dbType, e);
        }
    }

    // ------------------- 连接管理 -------------------

    /**
     * 创建连接到系统数据库(或数据库服务器)的连接。
     * - MySQL: 可不指定数据库(连接到服务器)
     * - PostgreSQL: 连接到 'postgres' 系统库
     * - SQL Server: 连接到 'master' 系统库
     * - Oracle: 不指定服务名/SID,但需通过 connectionProperties 提供 service 或 sid
     */
    private java.sql.Connection createSystemConnection() throws java.sql.SQLException {
        String systemDbName = getSystemDatabaseName();
        String jdbcUrl = buildJdbcUrl(systemDbName);
        return java.sql.DriverManager.getConnection(jdbcUrl, username, password);
    }

    /**
     * 获取用于初始连接的系统数据库名称。
     * Oracle 不需要此值,返回空字符串。
     */
    private String getSystemDatabaseName() {
        switch (dbType) {
            case MYSQL:
                return ""; // MySQL 允许连接时不指定数据库
            case POSTGRESQL:
                return "postgres";
            case SQLSERVER:
                return "master";
            case ORACLE:
                return ""; // Oracle URL 不在此处指定服务名
            default:
                throw new UnsupportedOperationException("Unsupported database type: " + dbType);
        }
    }

    /**
     * 根据数据库类型构建完整的 JDBC URL。
     *
     * @param databaseName 仅对非 Oracle 有效;Oracle 忽略此参数
     * @return 完整的 JDBC URL 字符串
     */
    private String buildJdbcUrl(String databaseName) {
        StringBuilder url = new StringBuilder("jdbc:");
        switch (dbType) {
            case MYSQL:
                url.append("mysql://").append(host).append(":").append(port).append("/");
                if (!databaseName.isEmpty()) {
                    url.append(databaseName);
                }
                break;
            case POSTGRESQL:
                url.append("postgresql://").append(host).append(":").append(port).append("/").append(databaseName);
                break;
            case SQLSERVER:
                url.append("sqlserver://").append(host).append(":").append(port)
                    .append(";databaseName=").append(databaseName);
                break;
            case ORACLE:
                // Oracle URL 格式取决于使用 SID 还是 Service Name
                // 推荐使用 Service Name: jdbc:oracle:thin:@//host:port/service_name
                // 如果 connectionProperties 包含 "service",使用它;否则尝试 "sid"
                String service = connectionProperties.getProperty("service");
                String sid = connectionProperties.getProperty("sid");
                if (service != null && !service.isEmpty()) {
                    url.append("oracle:thin:@//").append(host).append(":").append(port).append("/").append(service);
                } else if (sid != null && !sid.isEmpty()) {
                    url.append("oracle:thin:@").append(host).append(":").append(port).append(":").append(sid);
                } else {
                    // 默认使用 ORCL(常见默认服务名)
                    url.append("oracle:thin:@//").append(host).append(":").append(port).append("/ORCL");
                }
                break;
            default:
                throw new UnsupportedOperationException("Unsupported database type: " + dbType);
        }

        // 追加额外连接属性(如 useSSL=false)
        java.util.Properties props = new java.util.Properties();
        props.putAll(connectionProperties);
        // 移除 Oracle 专用参数,避免拼接到 URL 查询串
        if (dbType == DatabaseType.ORACLE) {
            props.remove("service");
            props.remove("sid");
        }

        if (!props.isEmpty()) {
            url.append("?");
            boolean first = true;
            for (String key : props.stringPropertyNames()) {
                if (!first) {
                    url.append("&");
                }
                url.append(key).append("=").append(props.getProperty(key));
                first = false;
            }
        }
        return url.toString();
    }

    // ------------------- 数据库存在性检查 -------------------

    /**
     * 检查指定名称的“数据库”是否已存在。
     * - Oracle:检查用户(User)是否存在
     */
    private boolean isDatabaseExists(java.sql.Connection conn, String dbName) throws java.sql.SQLException {
        String sql;
        switch (dbType) {
            case MYSQL:
                sql = "SELECT COUNT(*) FROM information_schema.schemata WHERE schema_name = ?";
                break;
            case POSTGRESQL:
                sql = "SELECT COUNT(*) FROM pg_database WHERE datname = ?";
                break;
            case SQLSERVER:
                sql = "SELECT COUNT(*) FROM sys.databases WHERE name = ?";
                break;
            case ORACLE:
                // 检查用户是否存在(忽略大小写,Oracle 默认大写)
                sql = "SELECT COUNT(*) FROM all_users WHERE username = UPPER(?)";
                break;
            default:
                throw new UnsupportedOperationException("Unsupported database type: " + dbType);
        }

        try (java.sql.PreparedStatement ps = conn.prepareStatement(sql)) {
            ps.setString(1, dbName);
            try (java.sql.ResultSet rs = ps.executeQuery()) {
                if (rs.next()) {
                    return rs.getLong(1) > 0;
                }
            }
        }
        return false;
    }

    // ------------------- 创建数据库(或用户)-------------------

    /**
     * 执行创建操作。
     * 注意:PostgreSQL 要求在 autocommit=true 且非事务中执行。
     */
    private void createDatabase(java.sql.Connection conn, String dbName) throws java.sql.SQLException {
        if (dbType == DatabaseType.POSTGRESQL) {
            conn.setAutoCommit(true);
        }

        String sql = buildCreateDatabaseSql(dbName);
        try (java.sql.Statement stmt = conn.createStatement()) {
            stmt.execute(sql);
        }
    }

    /**
     * 生成创建语句。
     * 优先尝试从 classpath 加载模板文件,失败则使用内置默认逻辑。
     */
    private String buildCreateDatabaseSql(String dbName) {
        String fileName = "sql/create-database-" + dbType.name().toLowerCase() + ".sql";
        java.io.InputStream is = getClass().getClassLoader().getResourceAsStream(fileName);

        if (is != null) {
            try (java.util.Scanner scanner = new java.util.Scanner(is, java.nio.charset.StandardCharsets.UTF_8.name())) {
                scanner.useDelimiter("\\A");
                String template = scanner.hasNext() ? scanner.next() : "";
                return template.replace("${dbName}", dbName);
            } catch (Exception e) {
                log.warn("Failed to load SQL template from '{}', falling back to default SQL. Reason: {}", fileName, e.getMessage());
            }
        }

        // 回退到内置默认逻辑
        switch (dbType) {
            case MYSQL:
                return "CREATE DATABASE `" + escapeMySqlIdentifier(dbName) + "` " +
                       "DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci";
            case POSTGRESQL:
                return "CREATE DATABASE " + escapePgIdentifier(dbName);
            case SQLSERVER:
                return "CREATE DATABASE [" + escapeSqlServerIdentifier(dbName) + "]";
            case ORACLE:
                // 内置 Oracle 逻辑:创建用户 + 授予权限
                String password = generateRandomPassword(16); // 生成强密码
                return "CREATE USER " + escapeOracleIdentifier(dbName) + " IDENTIFIED BY \"" + escapeOraclePassword(password) + "\";\n" +
                       "GRANT CREATE SESSION, CREATE TABLE, CREATE VIEW, CREATE SEQUENCE, CREATE PROCEDURE TO " + escapeOracleIdentifier(dbName) + ";\n" +
                       "ALTER USER " + escapeOracleIdentifier(dbName) + " QUOTA UNLIMITED ON USERS;";
            default:
                throw new UnsupportedOperationException("Unsupported database type: " + dbType);
        }
    }

    /**
     * 生成随机密码(仅用于内置 Oracle 逻辑,生产环境建议使用模板或预置用户)
     */
    private String generateRandomPassword(int length) {
        String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#%^&*";
        java.security.SecureRandom random = new java.security.SecureRandom();
        StringBuilder sb = new StringBuilder(length);
        for (int i = 0; i < length; i++) {
            sb.append(chars.charAt(random.nextInt(chars.length())));
        }
        return sb.toString();
    }

    // ------------------- 标识符转义 -------------------

    private String escapeMySqlIdentifier(String identifier) {
        return identifier.replace("`", "``");
    }

    private String escapePgIdentifier(String identifier) {
        return "\"" + identifier.replace("\"", "\"\"") + "\"";
    }

    private String escapeSqlServerIdentifier(String identifier) {
        return identifier.replace("]", "]]");
    }

    /**
     * 转义 Oracle 用户名(用双引号包围以支持大小写混合)
     */
    private String escapeOracleIdentifier(String identifier) {
        return "\"" + identifier.replace("\"", "\"\"") + "\"";
    }

    /**
     * 转义 Oracle 密码中的特殊字符(主要是双引号)
     */
    private String escapeOraclePassword(String password) {
        return password.replace("\"", "\"\"");
    }

    // ------------------- 支持的数据库类型 -------------------
    public enum DatabaseType {
        MYSQL,
        POSTGRESQL,
        SQLSERVER,
        ORACLE
    }
}


当你第一次在新环境部署的时候,就可以直接像下面这样使用:

public class AppConfig extends JFinalConfig {
       /**
	 * 配置JFinal插件
	 * 数据库连接池
	 * ActiveRecordPlugin
	 */
	@Override
	public void configPlugin(Plugins me) {
	
	        // 如果数据库不存在, 则创建,否则跳过: 必须放在真正的数据库连接插件之前
		DatabaseInitializer initializer = new DatabaseInitializer(
			DB_TYPE, DB_HOST, DB_PORT, 
			DB_USER, DB_PASSWORD);
		initializer.ensureDatabaseExists(DatabaseName); //不存在则创建数据库
		
		// 初始化数据库和数据(首次部署没有数据库和数据,或者数据库结构改变需要打补丁)...
		DruidPlugin migration = new DruidPlugin(DB_JDBC_URL, DB_USER, DB_PASSWORD, DB_DRIVER);
		migration.start();
	        DbMigrationPlugin migrationPlugin = new DbMigrationPlugin(migration)
	            .setEnableAutoInit(true)
	            .setAutoMigrateOnStart(true);
	        me.add(migrationPlugin);
		
		// 创建 DruidPlugin
		DruidPlugin dbPlugin = new DruidPlugin(DB_JDBC_URL, DB_USER, DB_PASSWORD, DB_DRIVER);
		
		...
	}
}


如果你不想手动写入数据库类型,数据库host,数据库端口,想复用已有DB_JDBC_URL,

下面附带上解析DB_JDBC_URL连接直接返回以上数据的类,只需把连接丢给它,自动给你解析出以上数据:

// DB_JDBC_URL 自己定义的变量或者从配置文件获取的数据库连接
JdbcUrlInfo info = JdbcUrlParser.parse(DB_JDBC_URL); 
DatabaseInitializer initializer = new DatabaseInitializer(
	info.getType(), info.getHost(), info.getPort(), 
	DB_USER, DB_PASSWORD);
initializer.ensureDatabaseExists(info.getDatabase());


JdbcUrlParser类

/**
 * 通用 JDBC URL 解析器,支持带代理前缀的 URL(如 p6spy、log4jdbc)
 * <p>
 * 支持格式示例:
 * <ul>
 *   <li>jdbc:mysql://localhost:3306/db</li>
 *   <li>jdbc:p6spy:mysql://localhost:3306/db</li>
 *   <li>jdbc:log4jdbc:postgresql://pg:5432/app</li>
 *   <li>jdbc:sqlserver://host;databaseName=db</li>
 *   <li>jdbc:datasource-proxy:sqlserver://db.example.com;databaseName=MyApp</li>
 *   <li>jdbc:oracle:thin:@localhost:1521:XE</li>
 *   <li>jdbc:p6spy:oracle:thin:@//myhost:1521/ORCLPDB1</li>
 * </ul>
 */
public class JdbcUrlParser {

    // 正则:匹配 "jdbc:(?:xxx:)*([a-zA-Z0-9_]+)://"
    private static final Pattern REAL_DRIVER_PATTERN = Pattern.compile(
        "^jdbc:(?:[a-zA-Z0-9_-]+:)*([a-zA-Z0-9_]+)://",
        Pattern.CASE_INSENSITIVE
    );

    // 各数据库的解析正则(只匹配 "driver://..." 部分)
    private static final Pattern MYSQL_SUB_PATTERN = Pattern.compile(
        "^mysql://([^:/?\\[\\]]+)(?::(\\d+))?/([^?]*)(?:\\?.*)?$",
        Pattern.CASE_INSENSITIVE
    );

    private static final Pattern POSTGRESQL_SUB_PATTERN = Pattern.compile(
        "^postgresql://([^:/?\\[\\]]+)(?::(\\d+))?/([^?]*)(?:\\?.*)?$",
        Pattern.CASE_INSENSITIVE
    );

    private static final Pattern SQLSERVER_SUB_PATTERN = Pattern.compile(
        "^sqlserver://([^;:/\\[\\]]+)(?::(\\d+))?(.*)$",
        Pattern.CASE_INSENSITIVE
    );

    // Oracle 特殊格式:不使用 "//",而是 "@host:port:sid" 或 "@//host:port/service"
    // 我们提取从 "oracle:" 开始的整个子串,然后单独解析
    private static final Pattern ORACLE_SUB_PATTERN = Pattern.compile(
        "^oracle:[^@]*@(?://)?([^:/]+)(?::(\\d+))?[:/]([^?]*)(?:\\?.*)?$",
        Pattern.CASE_INSENSITIVE
    );

    /**
     * 解析可能包含代理前缀的 JDBC URL
     *
     * @param jdbcUrl 原始 JDBC URL
     * @return JdbcUrlInfo
     * @throws IllegalArgumentException 如果无法识别或格式错误
     */
    public static JdbcUrlInfo parse(String jdbcUrl) {
        if (jdbcUrl == null || jdbcUrl.trim().isEmpty()) {
            throw new IllegalArgumentException("JDBC URL cannot be null or empty");
        }
        String url = jdbcUrl.trim();

        // 提取真实驱动名和子 URL
        Matcher driverMatcher = REAL_DRIVER_PATTERN.matcher(url);
        if (!driverMatcher.find()) {
            // 尝试匹配 Oracle(因为 Oracle URL 不以 "oracle://" 开头,而是 "oracle:...@")
            // 所以 REAL_DRIVER_PATTERN 无法捕获,需特殊处理
            if (url.toLowerCase().startsWith("jdbc:") && url.contains(":oracle:")) {
                // 手动提取 oracle 子串
                int oracleIdx = url.toLowerCase().indexOf(":oracle:");
                String subUrl = url.substring(oracleIdx + 1); // "oracle:thin:@..."
                return parseOracleSub(subUrl);
            } else {
                throw new IllegalArgumentException("Unable to detect real database driver in JDBC URL: " + url);
            }
        }

        String realDriver = driverMatcher.group(1).toLowerCase();
        int startIdx = driverMatcher.start(1); // 如 "mysql" 的起始位置
        String subUrl = url.substring(startIdx); // 如"mysql://..."

        // 根据真实驱动类型解析
        switch (realDriver) {
            case "mysql":
                return parseMysqlSub(subUrl);
            case "postgresql":
                return parsePostgresqlSub(subUrl);
            case "sqlserver":
                return parseSqlServerSub(subUrl);
            case "oracle":
                return parseOracleSub(subUrl);
            default:
                throw new IllegalArgumentException("Unsupported real database driver: '" + realDriver + "' in URL: " + url);
        }
    }

    // ------------------ 子 URL 解析 ------------------

    private static JdbcUrlInfo parseMysqlSub(String subUrl) {
        Matcher m = MYSQL_SUB_PATTERN.matcher(subUrl);
        if (!m.matches()) {
            throw new IllegalArgumentException("Invalid MySQL sub-URL: " + subUrl);
        }
        String host = m.group(1);
        String portStr = m.group(2);
        String db = m.group(3);
        int port = (portStr != null && !portStr.isEmpty()) ? Integer.parseInt(portStr) : 3306;
        db = normalizeDatabase(db);
        return new JdbcUrlInfo(DatabaseType.MYSQL, host, port, db);
    }

    private static JdbcUrlInfo parsePostgresqlSub(String subUrl) {
        Matcher m = POSTGRESQL_SUB_PATTERN.matcher(subUrl);
        if (!m.matches()) {
            throw new IllegalArgumentException("Invalid PostgreSQL sub-URL: " + subUrl);
        }
        String host = m.group(1);
        String portStr = m.group(2);
        String db = m.group(3);
        int port = (portStr != null && !portStr.isEmpty()) ? Integer.parseInt(portStr) : 5432;
        db = normalizeDatabase(db);
        return new JdbcUrlInfo(DatabaseType.POSTGRESQL, host, port, db);
    }

    private static JdbcUrlInfo parseSqlServerSub(String subUrl) {
        Matcher m = SQLSERVER_SUB_PATTERN.matcher(subUrl);
        if (!m.matches()) {
            throw new IllegalArgumentException("Invalid SQL Server sub-URL: " + subUrl);
        }
        String host = m.group(1);
        String portStr = m.group(2);
        String rest = m.group(3);

        int port = (portStr != null && !portStr.isEmpty()) ? Integer.parseInt(portStr) : 1433;
        String db = "";

        if (rest != null && !rest.isEmpty()) {
            String[] parts = rest.split(";");
            for (String part : parts) {
                part = part.trim();
                if (part.toLowerCase().startsWith("databasename=")) {
                    db = part.substring("databasename=".length());
                    break;
                }
            }
        }

        db = normalizeDatabase(db);
        return new JdbcUrlInfo(DatabaseType.SQLSERVER, host, port, db);
    }

    /**
     * 解析 Oracle 子 URL(如 "oracle:thin:@host:1521:SID" 或 "oracle:thin:@//host:1521/SERVICE")
     * <p>
     * 注意:Oracle JDBC URL 不符合标准 "//host:port/db" 模式,因此需单独处理。
     * 本方法将 SID 或 Service Name 作为 "database" 返回,便于后续使用(如连接 PDB)。
     * </p>
     */
    private static JdbcUrlInfo parseOracleSub(String subUrl) {
        // subUrl 示例: "oracle:thin:@localhost:1521:XE"
        // 或: "oracle:thin:@//myhost:1521/ORCLPDB1"

        Matcher m = ORACLE_SUB_PATTERN.matcher(subUrl);
        if (!m.matches()) {
            throw new IllegalArgumentException("Invalid Oracle sub-URL: " + subUrl +
                ". Expected format: oracle:...@host:port:sid or oracle:...@//host:port/service");
        }

        String host = m.group(1);
        String portStr = m.group(2);
        String serviceOrSid = m.group(3); // 这里统一称为 "service",实际可能是 SID

        int port = (portStr != null && !portStr.isEmpty()) ? Integer.parseInt(portStr) : 1521;
        String database = normalizeDatabase(serviceOrSid);

        return new JdbcUrlInfo(DatabaseType.ORACLE, host, port, database);
    }

    private static String normalizeDatabase(String db) {
        if (db == null || "/".equals(db) || "".equals(db.trim())) {
            return "";
        }
        return db.replaceAll("/+$", "").trim();
    }

    // ========================
    // 返回结构
    // ========================

    public static class JdbcUrlInfo {
        private final DatabaseType type;
        private final String host;
        private final int port;
        private final String database;

        public JdbcUrlInfo(DatabaseType type, String host, int port, String database) {
            this.type = type;
            this.host = host;
            this.port = port;
            this.database = database != null ? database : "";
        }

        public DatabaseType getType() { return type; }
        public String getHost() { return host; }
        public int getPort() { return port; }
        public String getDatabase() { return database; }

        @Override
        public String toString() {
            return "JdbcUrlInfo{" +
                   "type=" + type +
                   ", host='" + host + '\'' +
                   ", port=" + port +
                   ", database='" + database + '\'' +
                   '}';
        }
    }
    
    // ------------------ 测试用例 ------------------

    public static void main(String[] args) {
        String[] urls = {
            "jdbc:mysql://localhost:3306/taoffice",
            "jdbc:p6spy:mysql://192.168.1.10:3306/mydb",
            "jdbc:log4jdbc:postgresql://pg.local:5432/app_db?sslmode=disable",
            "jdbc:datasource-proxy:sqlserver://db.example.com;databaseName=MyApp",
            "jdbc:p6spy:sqlserver://localhost:1433;databaseName=TestDB",
            // Oracle 示例
            "jdbc:oracle:thin:@localhost:1521:XE",
            "jdbc:oracle:thin:@//localhost:1521/ORCLCDB",
            "jdbc:p6spy:oracle:thin:@mydb.example.com:1521:PROD",
            "jdbc:log4jdbc:oracle:thin:@//pdb1.local:1521/PDB1"
        };

        for (String url : urls) {
            try {
                JdbcUrlParser.JdbcUrlInfo info = JdbcUrlParser.parse(url);
                System.out.println(url + " → " + info);
            } catch (Exception e) {
                System.err.println("ERROR: " + url + " → " + e.getMessage());
            }
        }
    }
}


评论区

热门分享

扫码入社