告别手动建库、用户授权与脚本维护——一个智能、安全、多数据库兼容的自动化初始化引擎
从开发到测试再到部署,每次新建环境都意味着重复的手动操作:创建数据库、分配用户、授予权限、执行初始化脚本……不仅繁琐,还容易出错。
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());
}
}
}
}