插件版本功能更新完毕

This commit is contained in:
张家训
2026-06-17 19:11:16 +08:00
parent 0e95bf30a0
commit 15157acd4e
10 changed files with 715 additions and 647 deletions
+7
View File
@@ -46,6 +46,13 @@
<version>5.1.47</version>
</dependency>
<!-- OceanBase JDBC Driver(OB MySQL 模式元数据 API 兼容) -->
<dependency>
<groupId>com.oceanbase</groupId>
<artifactId>oceanbase-client</artifactId>
<version>2.4.12</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
@@ -493,10 +493,23 @@ public class GitSyncPlugin implements Plugin, InitializingBean {
log.warn("fetch 失败: {} -> {}", repoPath, r1.error);
}
// 2. checkout
// 2. checkout:先 stash 本地修改,避免因未提交修改导致 checkout 失败
boolean didStash = false;
CmdResult r2 = runGit(repoPath, "git checkout " + branch);
if (!r2.ok) {
// checkout 失败常见原因:有未提交修改 → stash 后重试
CmdResult stashResult = runGit(repoPath, "git stash push -m \"workutils-auto-stash\"");
if (stashResult.ok) {
didStash = true;
r2 = runGit(repoPath, "git checkout " + branch);
}
}
if (!r2.ok) {
log.warn("checkout 失败: {} -> {},标记为损坏", repoPath, r2.error);
// 如果 stash 了但 checkout 仍失败,恢复 stash
if (didStash) {
runGit(repoPath, "git stash pop");
}
SyncResult r = SyncResult.DAMAGED;
r.error = "checkout 失败: " + r2.error;
r.remoteUrl = getRemoteUrl(repoPath, baseUrlMap);
@@ -507,12 +520,24 @@ public class GitSyncPlugin implements Plugin, InitializingBean {
CmdResult r3 = runGit(repoPath, "git pull origin " + branch);
if (!r3.ok) {
log.warn("pull 失败: {} -> {},标记为损坏", repoPath, r3.error);
// pull 失败时恢复 stash(如果 stash 过)
if (didStash) {
runGit(repoPath, "git stash pop");
}
SyncResult r = SyncResult.DAMAGED;
r.error = "pull 失败: " + r3.error;
r.remoteUrl = getRemoteUrl(repoPath, baseUrlMap);
return r;
}
// 4. 恢复之前 stash 的修改(忽略冲突,保留 stash 让用户手动处理)
if (didStash) {
CmdResult popResult = runGit(repoPath, "git stash pop");
if (!popResult.ok) {
log.warn("stash pop 有冲突: {} -> {},已保留在 stash 中", repoPath, popResult.error);
}
}
SyncResult result = SyncResult.OK;
result.output = r3.output;
return result;
@@ -158,8 +158,9 @@ public class MyBatisGeneratorWindow {
ToggleButton btn = new ToggleButton(label);
btn.setToggleGroup(fillTargetGroup);
btn.setUserData(userData);
btn.setPrefWidth(85);
btn.setStyle("-fx-font-size: " + fsButton() + "px; -fx-cursor: hand;");
btn.setMinWidth(Region.USE_PREF_SIZE);
btn.setStyle("-fx-font-size: " + fsButton() + "px; -fx-cursor: hand; "
+ "-fx-padding: 6 16;");
// 点击按钮时,将当前树节点路径填充到对应字段
btn.setOnAction(e -> {
TreeItem<String> selected = projectTree.getSelectionModel().getSelectedItem();
@@ -176,71 +177,103 @@ public class MyBatisGeneratorWindow {
boolean active = (btn == cur);
btn.setStyle(active
? "-fx-background-color: #4a90d9; -fx-text-fill: white; -fx-font-size: " + fsButton() + "px; "
+ "-fx-background-radius: 4; -fx-padding: 6 18;"
+ "-fx-background-radius: 4; -fx-padding: 6 16;"
: "-fx-background-color: #e0e0e0; -fx-text-fill: #333; -fx-font-size: " + fsButton() + "px; "
+ "-fx-background-radius: 4; -fx-padding: 6 18;");
+ "-fx-background-radius: 4; -fx-padding: 6 16;");
});
// 初始化默认选中样式
btn.fire();
return btn;
}
private HBox createDbInfoSection() {
HBox box = new HBox(15);
box.setPadding(new Insets(8, 12, 8, 12));
box.setStyle("-fx-background-color: #e8f0fe; -fx-background-radius: 4;");
box.setAlignment(Pos.CENTER_LEFT);
private boolean refreshingDatasource = false;
private HBox createDbInfoSection() {
// 与 SplitPane 相同的比例:左侧 28% = 左面板宽度,右侧 72%
GridPane grid = new GridPane();
grid.setHgap(10);
grid.setPadding(new Insets(8, 12, 8, 12));
grid.setStyle("-fx-background-color: #e8f0fe; -fx-background-radius: 4;");
ColumnConstraints colLeft = new ColumnConstraints();
colLeft.setPercentWidth(28);
ColumnConstraints colRight = new ColumnConstraints();
colRight.setPercentWidth(72);
grid.getColumnConstraints().addAll(colLeft, colRight);
// --- 左侧:数据源选择,宽度与下方左面板对齐 ---
Label dsLabel = new Label("数据源:");
dsLabel.setFont(Font.font("Microsoft YaHei", fsBody()));
dsLabel.setMinWidth(Region.USE_PREF_SIZE);
datasourceCmb = new ComboBox<>();
datasourceCmb.setPrefWidth(160);
datasourceCmb.setStyle("-fx-font-size: " + fsBody() + "px;");
HBox.setHgrow(datasourceCmb, Priority.ALWAYS);
Button refreshDsBtn = new Button("刷新");
refreshDsBtn.setMinWidth(Region.USE_PREF_SIZE);
refreshDsBtn.setStyle("-fx-font-size: " + (fsBody() - 1) + "px; -fx-padding: 2 8;");
// 加载数据源列表
refreshDatasourceList();
datasourceCmb.setOnAction(e -> {
if (refreshingDatasource) return;
String selected = datasourceCmb.getValue();
if (selected != null) {
callback.switchDatasource("默认配置".equals(selected) ? null : selected);
updateDbInfo();
}
});
dbUrlLabel = new Label();
dbUrlLabel.setFont(Font.font("Consolas", fsBody() - 1));
dbUserLabel = new Label();
dbUserLabel.setFont(Font.font("Microsoft YaHei", fsBody() - 1));
updateDbInfo();
Button refreshDsBtn = new Button("刷新");
refreshDsBtn.setStyle("-fx-font-size: " + (fsBody() - 1) + "px; -fx-padding: 2 8;");
refreshDsBtn.setOnAction(e -> {
refreshDatasourceList();
updateDbInfo();
});
VBox infoBox = new VBox(3, dbUrlLabel, dbUserLabel);
HBox leftBox = new HBox(8, dsLabel, datasourceCmb, refreshDsBtn);
leftBox.setAlignment(Pos.CENTER_LEFT);
box.getChildren().addAll(dsLabel, datasourceCmb, refreshDsBtn, infoBox);
return box;
// --- 右侧:连接信息,白底灰框 ---
dbUrlLabel = new Label();
dbUrlLabel.setFont(Font.font("Consolas", fsBody() - 1));
dbUrlLabel.setMaxWidth(Double.MAX_VALUE);
dbUrlLabel.setTextOverrun(OverrunStyle.CENTER_ELLIPSIS);
dbUserLabel = new Label();
dbUserLabel.setFont(Font.font("Microsoft YaHei", fsBody() - 1));
updateDbInfo();
VBox infoBox = new VBox(3, dbUrlLabel, dbUserLabel);
infoBox.setPadding(new Insets(4, 10, 4, 10));
infoBox.setStyle("-fx-border-color: #b0c4de; -fx-border-width: 1; -fx-border-radius: 3; "
+ "-fx-background-color: #ffffff; -fx-background-radius: 3;");
infoBox.setAlignment(Pos.CENTER_LEFT);
infoBox.setMaxWidth(Double.MAX_VALUE);
grid.add(leftBox, 0, 0);
grid.add(infoBox, 1, 0);
HBox wrapper = new HBox(grid);
HBox.setHgrow(grid, Priority.ALWAYS);
return wrapper;
}
private void refreshDatasourceList() {
String current = datasourceCmb.getValue();
datasourceCmb.getItems().clear();
datasourceCmb.getItems().add("默认配置");
datasourceCmb.getItems().addAll(callback.getDatasourceNames());
if (current != null && datasourceCmb.getItems().contains(current)) {
datasourceCmb.setValue(current);
} else {
String dsName = callback.getCurrentDatasourceName();
if (dsName != null && datasourceCmb.getItems().contains(dsName)) {
datasourceCmb.setValue(dsName);
refreshingDatasource = true;
try {
String current = datasourceCmb.getValue();
datasourceCmb.getItems().clear();
datasourceCmb.getItems().add("默认配置");
datasourceCmb.getItems().addAll(callback.getDatasourceNames());
if (current != null && datasourceCmb.getItems().contains(current)) {
datasourceCmb.setValue(current);
} else {
datasourceCmb.setValue("默认配置");
String dsName = callback.getCurrentDatasourceName();
if (dsName != null && datasourceCmb.getItems().contains(dsName)) {
datasourceCmb.setValue(dsName);
} else {
datasourceCmb.setValue("默认配置");
}
}
} finally {
refreshingDatasource = false;
}
}
@@ -279,22 +312,7 @@ public class MyBatisGeneratorWindow {
VBox.setVgrow(projectTree, Priority.ALWAYS);
// 填充目标选择(按钮组)
Label quickLabel = new Label("点击按钮填充到:");
quickLabel.setFont(Font.font("Microsoft YaHei", fsBody()));
quickLabel.setPadding(new Insets(10, 0, 2, 0));
this.fillTargetGroup = new ToggleGroup();
ToggleButton btnModel = createFillButton("Model", "model");
btnModel.setSelected(true);
ToggleButton btnXml = createFillButton("XML", "xml");
ToggleButton btnMapper = createFillButton("Mapper", "mapper");
HBox rbBox = new HBox(10, btnModel, btnXml, btnMapper);
rbBox.setPadding(new Insets(5, 0, 5, 0));
panel.getChildren().addAll(treeTitle, projectTree, quickLabel, rbBox);
panel.getChildren().addAll(treeTitle, projectTree);
return panel;
}
@@ -410,9 +428,27 @@ public class MyBatisGeneratorWindow {
VBox box = new VBox(8);
box.setPadding(new Insets(10, 0, 0, 0));
// 标题行:左侧"输出目录设置",右侧快速填充按钮
Label dirLabel = new Label("输出目录设置:");
dirLabel.setFont(Font.font("Microsoft YaHei", fsSection()));
this.fillTargetGroup = new ToggleGroup();
Label quickLabel = new Label("快速填充:");
quickLabel.setFont(Font.font("Microsoft YaHei", fsBody()));
quickLabel.setMinWidth(Region.USE_PREF_SIZE);
ToggleButton btnModel = createFillButton("Model", "model");
btnModel.setSelected(true);
ToggleButton btnXml = createFillButton("XML", "xml");
ToggleButton btnMapper = createFillButton("Mapper", "mapper");
Region spacer = new Region();
HBox.setHgrow(spacer, Priority.ALWAYS);
HBox headerRow = new HBox(10, dirLabel, spacer, quickLabel, btnModel, btnXml, btnMapper);
headerRow.setAlignment(Pos.CENTER_LEFT);
HBox modelRow = createDirRow("Model (PO)", fm -> modelDirField = fm);
modelDirField = (TextField) modelRow.getChildren().get(1);
HBox xmlRow = createDirRow("XML Mapper", fm -> xmlDirField = fm);
@@ -422,7 +458,7 @@ public class MyBatisGeneratorWindow {
loadDefaultDirs();
box.getChildren().addAll(dirLabel, modelRow, xmlRow, mapperRow);
box.getChildren().addAll(headerRow, modelRow, xmlRow, mapperRow);
return box;
}
@@ -101,7 +101,7 @@ public class MyBatisGeneratorService {
DbConnection db = getDbConnection();
String dbName = extractDatabaseName(db.url);
try (Connection conn = DriverManager.getConnection(db.url, db.username, db.password)) {
try (Connection conn = openConnection(db)) {
String sql = "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = ? AND TABLE_NAME LIKE ?";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, dbName);
@@ -124,7 +124,7 @@ public class MyBatisGeneratorService {
DbConnection db = getDbConnection();
String dbName = extractDatabaseName(db.url);
try (Connection conn = DriverManager.getConnection(db.url, db.username, db.password)) {
try (Connection conn = openConnection(db)) {
String sql = "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = ? ORDER BY TABLE_NAME";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, dbName);
@@ -138,6 +138,39 @@ public class MyBatisGeneratorService {
return tables;
}
/**
* 打开数据库连接。先尝试配置的驱动,失败则回退 MySQL 驱动并改写 URL。
*/
private Connection openConnection(DbConnection db) throws SQLException {
// 第一优先级:尝试用配置的驱动连接
try {
Class.forName(db.driverClass);
return DriverManager.getConnection(db.url, db.username, db.password);
} catch (Exception e) {
log.warn("驱动 {} 不可用或连接失败,尝试 MySQL 驱动回退: {}", db.driverClass, e.getMessage());
}
// 第二优先级:MySQL 驱动回退
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException ignored) {
}
// 第三优先级:尝试 MySQL 5.1 之后的驱动类名
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException ignored) {
}
// 将 jdbc:oceanbase:// 替换为 jdbc:mysql://(OB 4.x MySQL 模式兼容 MySQL 协议)
String fallbackUrl = db.url;
if (fallbackUrl.startsWith("jdbc:oceanbase://")) {
fallbackUrl = fallbackUrl.replace("jdbc:oceanbase://", "jdbc:mysql://");
log.info("URL 已改写为 MySQL 协议: {}", fallbackUrl);
}
return DriverManager.getConnection(fallbackUrl, db.username, db.password);
}
/**
* 获取当前生效的数据库连接信息
* 优先级: 选中的数据源 > 外部配置(workutils.config) > 内置classpath属性
@@ -145,11 +178,12 @@ public class MyBatisGeneratorService {
private DbConnection getDbConnection() {
DatasourceConfig ds = getCurrentDatasource();
if (ds != null) {
return new DbConnection(ds.getUrl(), ds.getUsername(), ds.getPassword());
return new DbConnection(ds.getDriver(), ds.getUrl(), ds.getUsername(), ds.getPassword());
}
// 回退: data/ 外部配置 → classpath 内置配置
Properties props = loadMybatisProperties();
return new DbConnection(
props.getProperty("db_driver", "com.mysql.jdbc.Driver"),
props.getProperty("db_url", ""),
props.getProperty("db_username", ""),
props.getProperty("db_password", "")
@@ -158,10 +192,12 @@ public class MyBatisGeneratorService {
/** 内部数据类:数据库连接信息 */
private static class DbConnection {
final String driverClass;
final String url;
final String username;
final String password;
DbConnection(String url, String username, String password) {
DbConnection(String driverClass, String url, String username, String password) {
this.driverClass = driverClass;
this.url = url;
this.username = username;
this.password = password;
@@ -264,6 +300,10 @@ public class MyBatisGeneratorService {
"<properties url=\"" + fileUrl + "\" />");
}
// ★ 关键修复:将 JDBC 连接属性直接注入到 XML 中(绕过 properties 文件),
// 并自动执行 OB→MySQL 驱动/URL 兜底,确保 MBG 总能连上数据库
content = injectJdbcFallback(content);
// 截掉 </generatorConfiguration> 之后的内容(防止尾随垃圾导致 XML 解析失败)
int endIdx = content.lastIndexOf("</generatorConfiguration>");
if (endIdx >= 0) {
@@ -335,9 +375,25 @@ public class MyBatisGeneratorService {
"$1" + java.util.regex.Matcher.quoteReplacement(mapperPkg) + "$2");
}
// 从 JDBC URL 中提取数据库名作为 schema,避免 MyBatis Generator 找不到表
String schema = "";
try {
Properties props = getConfigurationProperties();
String url = props.getProperty("db_url", "");
if (!url.isEmpty()) {
schema = extractDatabaseName(url);
}
} catch (Exception ignored) {
}
StringBuilder tableElements = new StringBuilder();
for (String tableName : tableNames) {
tableElements.append(" <table schema=\"\" tableName=\"")
// 必须同时设置 catalog 和 schema,OceanBase 驱动仅靠 schema 可能匹配不上
tableElements.append(" <table catalog=\"")
.append(schema)
.append("\" schema=\"")
.append(schema)
.append("\" tableName=\"")
.append(tableName)
.append("\"\n")
.append(" domainObjectName=\"")
@@ -387,6 +443,53 @@ public class MyBatisGeneratorService {
return new String[]{projectRoot, packageName};
}
/**
* 将 JDBC 连接属性直接注入到 XML 中,并自动执行 OB→MySQL 驱动/URL 兜底。
* 这样 MBG 不需要从外部 properties 文件读取连接信息,避免因 properties
* 文件中写的是 OB 驱动而运行时类路径中只有 MySQL 驱动导致的连接失败。
*/
private String injectJdbcFallback(String xmlContent) {
Properties props = getConfigurationProperties();
String driver = props.getProperty("db_driver", "com.mysql.jdbc.Driver");
String url = props.getProperty("db_url", "");
String username = props.getProperty("db_username", "");
String password = props.getProperty("db_password", "");
// 检测 OB 驱动是否可用,不可用则回退到 MySQL 驱动并改写 URL
boolean obAvailable;
try {
Class.forName(driver);
obAvailable = true;
} catch (ClassNotFoundException e) {
obAvailable = false;
}
if (!obAvailable && url.startsWith("jdbc:oceanbase://")) {
log.info("MBG 配置: OB 驱动不可用,URL 改写为 MySQL 协议");
url = url.replace("jdbc:oceanbase://", "jdbc:mysql://");
driver = "com.mysql.jdbc.Driver";
}
// 直接替换 XML 中的 ${db_driver} / ${db_url} / ${db_username} / ${db_password} 占位符
// 注意: 只替换 jdbcConnection 元素中的属性值,避免影响其他地方
// 对可能含 & 等 XML 特殊字符的值做转义
return xmlContent
.replace("${db_driver}", xmlEscape(driver))
.replace("${db_url}", xmlEscape(url))
.replace("${db_username}", xmlEscape(username))
.replace("${db_password}", xmlEscape(password));
}
/** 对注入到 XML 属性中的值做特殊字符转义 */
private static String xmlEscape(String value) {
if (value == null) return "";
return value
.replace("&", "&amp;")
.replace("\"", "&quot;")
.replace("'", "&apos;")
.replace("<", "&lt;")
.replace(">", "&gt;");
}
/**
* 根据表名生成实体类名
*/
@@ -58,7 +58,7 @@ public class ExternalConfigManager {
public static final String KEY_FONT_BODY = "font.size.body"; // 正文/提示 默认14
public static final String KEY_FONT_MONO = "font.size.mono"; // 等宽/代码 默认14
/** 配置节定义:名称 → 键列表 */
/** 配置节定义:名称 → 键列表(MYBATIS 相关配置已迁移到 data/mybatis-generator.properties 独立管理) */
private static final LinkedHashMap<String, String[]> SECTIONS = new LinkedHashMap<>();
static {
@@ -68,13 +68,6 @@ public class ExternalConfigManager {
SECTIONS.put("===== 数据文件路径 =====", new String[]{
KEY_PROJECT_PATHS, KEY_DAMAGED_REPOS
});
SECTIONS.put("===== MyBatis 数据库连接(覆盖内置 mybatis-generator.properties)=====", new String[]{
KEY_MYBATIS_DRIVER, KEY_MYBATIS_URL, KEY_MYBATIS_USERNAME,
KEY_MYBATIS_PASSWORD
});
SECTIONS.put("===== MyBatis 代码生成包路径 =====", new String[]{
KEY_MYBATIS_MODEL_PKG, KEY_MYBATIS_DAO_PKG, KEY_MYBATIS_SQLMAPPER_PKG
});
SECTIONS.put("===== 字体大小(显示设置) =====", new String[]{
KEY_FONT_SECTION_TITLE, KEY_FONT_FIELD_LABEL, KEY_FONT_BUTTON,
KEY_FONT_BODY, KEY_FONT_MONO
@@ -22,6 +22,7 @@ public class SettingsService {
private final WorkUtilsProperties properties;
private final ExternalConfigManager configManager;
private final DatasourceConfigManager datasourceConfigManager;
/**
* 打开系统设置 GUI 窗口
@@ -30,7 +31,7 @@ public class SettingsService {
*/
public void showSettings(Scanner scanner, Runnable onConfigChanged) {
try {
SettingsWindow window = new SettingsWindow(properties, configManager, onConfigChanged);
SettingsWindow window = new SettingsWindow(properties, configManager, datasourceConfigManager, onConfigChanged);
window.showAndWait();
} catch (Exception e) {
log.error("打开设置窗口失败,回退到控制台模式", e);
File diff suppressed because it is too large Load Diff
@@ -69,11 +69,11 @@ public class WorkUtilsProperties {
private final Map<String, String> mybatisOverrides = new LinkedHashMap<>();
// ---- 字体大小(显示设置) ----
private int fontSectionTitle = 20; // 节标题
private int fontFieldLabel = 18; // 字段标签
private int fontButton = 18; // 按钮
private int fontBody = 18; // 正文/提示
private int fontMono = 18; // 等宽/代码
private int fontSectionTitle = 24; // 节标题
private int fontFieldLabel = 20; // 字段标签
private int fontButton = 20; // 按钮
private int fontBody = 20; // 正文/提示
private int fontMono = 20; // 等宽/代码
// ==================== 外部配置加载 ====================
@@ -167,11 +167,11 @@ public class WorkUtilsProperties {
projectPathsFile = defaults.get(ExternalConfigManager.KEY_PROJECT_PATHS);
damagedReposFile = defaults.get(ExternalConfigManager.KEY_DAMAGED_REPOS);
mybatisOverrides.clear();
fontSectionTitle = 20;
fontFieldLabel = 18;
fontButton = 18;
fontBody = 18;
fontMono = 18;
fontSectionTitle = 24;
fontFieldLabel = 20;
fontButton = 20;
fontBody = 20;
fontMono = 20;
log.info("配置已重置为默认值");
}
@@ -106,8 +106,8 @@ public class PluginManager {
break;
}
// 'S' / 's' 字符(readChoiceOrChar 将单字母转成负ASCII码)
if (choice == -'S') {
// '?' 字符(readChoiceOrChar 将单字母转成负ASCII码)
if (choice == -'?') {
settingsService.showSettings(scanner, pluginContext::resetConfigChecks);
// JavaFX 后台线程在 Windows 上可能污染 System.in,
// 清理残留字节并重建 Scanner,确保后续输入正常
@@ -126,7 +126,7 @@ public class PluginManager {
PluginDescriptor desc = enabledPlugins.get(i).getDescriptor();
System.out.println((i + 1) + ". " + desc.getName() + " [v" + desc.getVersion() + "]");
}
System.out.println("S. 系统设置");
System.out.println("?. 系统设置");
System.out.println(EXIT_CODE + ". 退出系统");
}
@@ -145,7 +145,7 @@ public class PluginManager {
System.out.println("插件 [" + desc.getName() + "] 配置异常:");
System.out.println(configError);
System.out.println();
System.out.println("请进入「系统设置(S)」进行配置后再使用。");
System.out.println("请进入「系统设置(?)」进行配置后再使用。");
System.out.println("==============================");
return;
}
+11 -11
View File
@@ -1,23 +1,23 @@
[
{
"name": "开发环境",
"name": "本地环境-loacalhost-db",
"driver": "com.mysql.jdbc.Driver",
"url": "jdbc:mysql://localhost:3306/disburse?useUnicode=true&characterEncoding=utf8&useSSL=false",
"username": "root",
"password": "1Qaz@4772"
},
{
"name": "SIT环境",
"driver": "com.mysql.jdbc.Driver",
"url": "jdbc:mysql://sit-server:3306/disburse?useUnicode=true&characterEncoding=utf8&useSSL=false",
"username": "sit_user",
"password": ""
"name": "ft_loancapital_core_db",
"driver": "com.oceanbase.jdbc.Driver",
"url": "jdbc:oceanbase://ob4x-xyd.bestpay.dev-db:2883/ft_loancapital_core_db?rewriteBatchedStatements=true&allowMultiQueries=true&useLocalSessionState=true&useUnicode=true&characterEncoding=utf-8&socketTimeout=6000000",
"username": "ft_loancapital_core_app@xydcs_gcz_tnt#ob4x_cluster_xyd",
"password": "FT_LOANCAPITAL_CORE@123"
},
{
"name": "生产环境(只读)",
"driver": "com.mysql.jdbc.Driver",
"url": "jdbc:mysql://prod-server:3306/disburse?useUnicode=true&characterEncoding=utf8&useSSL=false",
"username": "readonly",
"password": ""
"name": "cf_loan_core_db",
"driver": "com.oceanbase.jdbc.Driver",
"url": "jdbc:oceanbase://ob4x-ky.bestpay.46-db:2883/cf_loan_core_db?useSSL=false&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&allowMultiQueries=true&serverTimezone=Asia/Shanghai&socketTimeout=30000&connectTimeout=10000",
"username": "cf_loan_core_app@obtest_tnt#ob4x_cluster_test",
"password": "CF_LOAN_CORE@123"
}
]