插件版本功能更新完毕
This commit is contained in:
@@ -46,6 +46,13 @@
|
|||||||
<version>5.1.47</version>
|
<version>5.1.47</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- OceanBase JDBC Driver(OB MySQL 模式元数据 API 兼容) -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.oceanbase</groupId>
|
||||||
|
<artifactId>oceanbase-client</artifactId>
|
||||||
|
<version>2.4.12</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.alibaba</groupId>
|
<groupId>com.alibaba</groupId>
|
||||||
<artifactId>fastjson</artifactId>
|
<artifactId>fastjson</artifactId>
|
||||||
|
|||||||
@@ -493,10 +493,23 @@ public class GitSyncPlugin implements Plugin, InitializingBean {
|
|||||||
log.warn("fetch 失败: {} -> {}", repoPath, r1.error);
|
log.warn("fetch 失败: {} -> {}", repoPath, r1.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. checkout
|
// 2. checkout:先 stash 本地修改,避免因未提交修改导致 checkout 失败
|
||||||
|
boolean didStash = false;
|
||||||
CmdResult r2 = runGit(repoPath, "git checkout " + branch);
|
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) {
|
if (!r2.ok) {
|
||||||
log.warn("checkout 失败: {} -> {},标记为损坏", repoPath, r2.error);
|
log.warn("checkout 失败: {} -> {},标记为损坏", repoPath, r2.error);
|
||||||
|
// 如果 stash 了但 checkout 仍失败,恢复 stash
|
||||||
|
if (didStash) {
|
||||||
|
runGit(repoPath, "git stash pop");
|
||||||
|
}
|
||||||
SyncResult r = SyncResult.DAMAGED;
|
SyncResult r = SyncResult.DAMAGED;
|
||||||
r.error = "checkout 失败: " + r2.error;
|
r.error = "checkout 失败: " + r2.error;
|
||||||
r.remoteUrl = getRemoteUrl(repoPath, baseUrlMap);
|
r.remoteUrl = getRemoteUrl(repoPath, baseUrlMap);
|
||||||
@@ -507,12 +520,24 @@ public class GitSyncPlugin implements Plugin, InitializingBean {
|
|||||||
CmdResult r3 = runGit(repoPath, "git pull origin " + branch);
|
CmdResult r3 = runGit(repoPath, "git pull origin " + branch);
|
||||||
if (!r3.ok) {
|
if (!r3.ok) {
|
||||||
log.warn("pull 失败: {} -> {},标记为损坏", repoPath, r3.error);
|
log.warn("pull 失败: {} -> {},标记为损坏", repoPath, r3.error);
|
||||||
|
// pull 失败时恢复 stash(如果 stash 过)
|
||||||
|
if (didStash) {
|
||||||
|
runGit(repoPath, "git stash pop");
|
||||||
|
}
|
||||||
SyncResult r = SyncResult.DAMAGED;
|
SyncResult r = SyncResult.DAMAGED;
|
||||||
r.error = "pull 失败: " + r3.error;
|
r.error = "pull 失败: " + r3.error;
|
||||||
r.remoteUrl = getRemoteUrl(repoPath, baseUrlMap);
|
r.remoteUrl = getRemoteUrl(repoPath, baseUrlMap);
|
||||||
return r;
|
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;
|
SyncResult result = SyncResult.OK;
|
||||||
result.output = r3.output;
|
result.output = r3.output;
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
+87
-51
@@ -158,8 +158,9 @@ public class MyBatisGeneratorWindow {
|
|||||||
ToggleButton btn = new ToggleButton(label);
|
ToggleButton btn = new ToggleButton(label);
|
||||||
btn.setToggleGroup(fillTargetGroup);
|
btn.setToggleGroup(fillTargetGroup);
|
||||||
btn.setUserData(userData);
|
btn.setUserData(userData);
|
||||||
btn.setPrefWidth(85);
|
btn.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btn.setStyle("-fx-font-size: " + fsButton() + "px; -fx-cursor: hand;");
|
btn.setStyle("-fx-font-size: " + fsButton() + "px; -fx-cursor: hand; "
|
||||||
|
+ "-fx-padding: 6 16;");
|
||||||
// 点击按钮时,将当前树节点路径填充到对应字段
|
// 点击按钮时,将当前树节点路径填充到对应字段
|
||||||
btn.setOnAction(e -> {
|
btn.setOnAction(e -> {
|
||||||
TreeItem<String> selected = projectTree.getSelectionModel().getSelectedItem();
|
TreeItem<String> selected = projectTree.getSelectionModel().getSelectedItem();
|
||||||
@@ -176,71 +177,103 @@ public class MyBatisGeneratorWindow {
|
|||||||
boolean active = (btn == cur);
|
boolean active = (btn == cur);
|
||||||
btn.setStyle(active
|
btn.setStyle(active
|
||||||
? "-fx-background-color: #4a90d9; -fx-text-fill: white; -fx-font-size: " + fsButton() + "px; "
|
? "-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-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();
|
btn.fire();
|
||||||
return btn;
|
return btn;
|
||||||
}
|
}
|
||||||
|
|
||||||
private HBox createDbInfoSection() {
|
private boolean refreshingDatasource = false;
|
||||||
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 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("数据源:");
|
Label dsLabel = new Label("数据源:");
|
||||||
dsLabel.setFont(Font.font("Microsoft YaHei", fsBody()));
|
dsLabel.setFont(Font.font("Microsoft YaHei", fsBody()));
|
||||||
|
dsLabel.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
|
|
||||||
datasourceCmb = new ComboBox<>();
|
datasourceCmb = new ComboBox<>();
|
||||||
datasourceCmb.setPrefWidth(160);
|
|
||||||
datasourceCmb.setStyle("-fx-font-size: " + fsBody() + "px;");
|
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();
|
refreshDatasourceList();
|
||||||
datasourceCmb.setOnAction(e -> {
|
datasourceCmb.setOnAction(e -> {
|
||||||
|
if (refreshingDatasource) return;
|
||||||
String selected = datasourceCmb.getValue();
|
String selected = datasourceCmb.getValue();
|
||||||
if (selected != null) {
|
if (selected != null) {
|
||||||
callback.switchDatasource("默认配置".equals(selected) ? null : selected);
|
callback.switchDatasource("默认配置".equals(selected) ? null : selected);
|
||||||
updateDbInfo();
|
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 -> {
|
refreshDsBtn.setOnAction(e -> {
|
||||||
refreshDatasourceList();
|
refreshDatasourceList();
|
||||||
updateDbInfo();
|
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() {
|
private void refreshDatasourceList() {
|
||||||
String current = datasourceCmb.getValue();
|
refreshingDatasource = true;
|
||||||
datasourceCmb.getItems().clear();
|
try {
|
||||||
datasourceCmb.getItems().add("默认配置");
|
String current = datasourceCmb.getValue();
|
||||||
datasourceCmb.getItems().addAll(callback.getDatasourceNames());
|
datasourceCmb.getItems().clear();
|
||||||
if (current != null && datasourceCmb.getItems().contains(current)) {
|
datasourceCmb.getItems().add("默认配置");
|
||||||
datasourceCmb.setValue(current);
|
datasourceCmb.getItems().addAll(callback.getDatasourceNames());
|
||||||
} else {
|
if (current != null && datasourceCmb.getItems().contains(current)) {
|
||||||
String dsName = callback.getCurrentDatasourceName();
|
datasourceCmb.setValue(current);
|
||||||
if (dsName != null && datasourceCmb.getItems().contains(dsName)) {
|
|
||||||
datasourceCmb.setValue(dsName);
|
|
||||||
} else {
|
} 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);
|
VBox.setVgrow(projectTree, Priority.ALWAYS);
|
||||||
|
|
||||||
// 填充目标选择(按钮组)
|
panel.getChildren().addAll(treeTitle, projectTree);
|
||||||
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);
|
|
||||||
return panel;
|
return panel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -410,9 +428,27 @@ public class MyBatisGeneratorWindow {
|
|||||||
VBox box = new VBox(8);
|
VBox box = new VBox(8);
|
||||||
box.setPadding(new Insets(10, 0, 0, 0));
|
box.setPadding(new Insets(10, 0, 0, 0));
|
||||||
|
|
||||||
|
// 标题行:左侧"输出目录设置",右侧快速填充按钮
|
||||||
Label dirLabel = new Label("输出目录设置:");
|
Label dirLabel = new Label("输出目录设置:");
|
||||||
dirLabel.setFont(Font.font("Microsoft YaHei", fsSection()));
|
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);
|
HBox modelRow = createDirRow("Model (PO)", fm -> modelDirField = fm);
|
||||||
modelDirField = (TextField) modelRow.getChildren().get(1);
|
modelDirField = (TextField) modelRow.getChildren().get(1);
|
||||||
HBox xmlRow = createDirRow("XML Mapper", fm -> xmlDirField = fm);
|
HBox xmlRow = createDirRow("XML Mapper", fm -> xmlDirField = fm);
|
||||||
@@ -422,7 +458,7 @@ public class MyBatisGeneratorWindow {
|
|||||||
|
|
||||||
loadDefaultDirs();
|
loadDefaultDirs();
|
||||||
|
|
||||||
box.getChildren().addAll(dirLabel, modelRow, xmlRow, mapperRow);
|
box.getChildren().addAll(headerRow, modelRow, xmlRow, mapperRow);
|
||||||
return box;
|
return box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+108
-5
@@ -101,7 +101,7 @@ public class MyBatisGeneratorService {
|
|||||||
DbConnection db = getDbConnection();
|
DbConnection db = getDbConnection();
|
||||||
String dbName = extractDatabaseName(db.url);
|
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 ?";
|
String sql = "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = ? AND TABLE_NAME LIKE ?";
|
||||||
try (PreparedStatement ps = conn.prepareStatement(sql)) {
|
try (PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||||
ps.setString(1, dbName);
|
ps.setString(1, dbName);
|
||||||
@@ -124,7 +124,7 @@ public class MyBatisGeneratorService {
|
|||||||
DbConnection db = getDbConnection();
|
DbConnection db = getDbConnection();
|
||||||
String dbName = extractDatabaseName(db.url);
|
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";
|
String sql = "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = ? ORDER BY TABLE_NAME";
|
||||||
try (PreparedStatement ps = conn.prepareStatement(sql)) {
|
try (PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||||
ps.setString(1, dbName);
|
ps.setString(1, dbName);
|
||||||
@@ -138,6 +138,39 @@ public class MyBatisGeneratorService {
|
|||||||
return tables;
|
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属性
|
* 优先级: 选中的数据源 > 外部配置(workutils.config) > 内置classpath属性
|
||||||
@@ -145,11 +178,12 @@ public class MyBatisGeneratorService {
|
|||||||
private DbConnection getDbConnection() {
|
private DbConnection getDbConnection() {
|
||||||
DatasourceConfig ds = getCurrentDatasource();
|
DatasourceConfig ds = getCurrentDatasource();
|
||||||
if (ds != null) {
|
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 内置配置
|
// 回退: data/ 外部配置 → classpath 内置配置
|
||||||
Properties props = loadMybatisProperties();
|
Properties props = loadMybatisProperties();
|
||||||
return new DbConnection(
|
return new DbConnection(
|
||||||
|
props.getProperty("db_driver", "com.mysql.jdbc.Driver"),
|
||||||
props.getProperty("db_url", ""),
|
props.getProperty("db_url", ""),
|
||||||
props.getProperty("db_username", ""),
|
props.getProperty("db_username", ""),
|
||||||
props.getProperty("db_password", "")
|
props.getProperty("db_password", "")
|
||||||
@@ -158,10 +192,12 @@ public class MyBatisGeneratorService {
|
|||||||
|
|
||||||
/** 内部数据类:数据库连接信息 */
|
/** 内部数据类:数据库连接信息 */
|
||||||
private static class DbConnection {
|
private static class DbConnection {
|
||||||
|
final String driverClass;
|
||||||
final String url;
|
final String url;
|
||||||
final String username;
|
final String username;
|
||||||
final String password;
|
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.url = url;
|
||||||
this.username = username;
|
this.username = username;
|
||||||
this.password = password;
|
this.password = password;
|
||||||
@@ -264,6 +300,10 @@ public class MyBatisGeneratorService {
|
|||||||
"<properties url=\"" + fileUrl + "\" />");
|
"<properties url=\"" + fileUrl + "\" />");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ★ 关键修复:将 JDBC 连接属性直接注入到 XML 中(绕过 properties 文件),
|
||||||
|
// 并自动执行 OB→MySQL 驱动/URL 兜底,确保 MBG 总能连上数据库
|
||||||
|
content = injectJdbcFallback(content);
|
||||||
|
|
||||||
// 截掉 </generatorConfiguration> 之后的内容(防止尾随垃圾导致 XML 解析失败)
|
// 截掉 </generatorConfiguration> 之后的内容(防止尾随垃圾导致 XML 解析失败)
|
||||||
int endIdx = content.lastIndexOf("</generatorConfiguration>");
|
int endIdx = content.lastIndexOf("</generatorConfiguration>");
|
||||||
if (endIdx >= 0) {
|
if (endIdx >= 0) {
|
||||||
@@ -335,9 +375,25 @@ public class MyBatisGeneratorService {
|
|||||||
"$1" + java.util.regex.Matcher.quoteReplacement(mapperPkg) + "$2");
|
"$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();
|
StringBuilder tableElements = new StringBuilder();
|
||||||
for (String tableName : tableNames) {
|
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(tableName)
|
||||||
.append("\"\n")
|
.append("\"\n")
|
||||||
.append(" domainObjectName=\"")
|
.append(" domainObjectName=\"")
|
||||||
@@ -387,6 +443,53 @@ public class MyBatisGeneratorService {
|
|||||||
return new String[]{projectRoot, packageName};
|
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("&", "&")
|
||||||
|
.replace("\"", """)
|
||||||
|
.replace("'", "'")
|
||||||
|
.replace("<", "<")
|
||||||
|
.replace(">", ">");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据表名生成实体类名
|
* 根据表名生成实体类名
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ public class ExternalConfigManager {
|
|||||||
public static final String KEY_FONT_BODY = "font.size.body"; // 正文/提示 默认14
|
public static final String KEY_FONT_BODY = "font.size.body"; // 正文/提示 默认14
|
||||||
public static final String KEY_FONT_MONO = "font.size.mono"; // 等宽/代码 默认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<>();
|
private static final LinkedHashMap<String, String[]> SECTIONS = new LinkedHashMap<>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
@@ -68,13 +68,6 @@ public class ExternalConfigManager {
|
|||||||
SECTIONS.put("===== 数据文件路径 =====", new String[]{
|
SECTIONS.put("===== 数据文件路径 =====", new String[]{
|
||||||
KEY_PROJECT_PATHS, KEY_DAMAGED_REPOS
|
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[]{
|
SECTIONS.put("===== 字体大小(显示设置) =====", new String[]{
|
||||||
KEY_FONT_SECTION_TITLE, KEY_FONT_FIELD_LABEL, KEY_FONT_BUTTON,
|
KEY_FONT_SECTION_TITLE, KEY_FONT_FIELD_LABEL, KEY_FONT_BUTTON,
|
||||||
KEY_FONT_BODY, KEY_FONT_MONO
|
KEY_FONT_BODY, KEY_FONT_MONO
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ public class SettingsService {
|
|||||||
|
|
||||||
private final WorkUtilsProperties properties;
|
private final WorkUtilsProperties properties;
|
||||||
private final ExternalConfigManager configManager;
|
private final ExternalConfigManager configManager;
|
||||||
|
private final DatasourceConfigManager datasourceConfigManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 打开系统设置 GUI 窗口
|
* 打开系统设置 GUI 窗口
|
||||||
@@ -30,7 +31,7 @@ public class SettingsService {
|
|||||||
*/
|
*/
|
||||||
public void showSettings(Scanner scanner, Runnable onConfigChanged) {
|
public void showSettings(Scanner scanner, Runnable onConfigChanged) {
|
||||||
try {
|
try {
|
||||||
SettingsWindow window = new SettingsWindow(properties, configManager, onConfigChanged);
|
SettingsWindow window = new SettingsWindow(properties, configManager, datasourceConfigManager, onConfigChanged);
|
||||||
window.showAndWait();
|
window.showAndWait();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("打开设置窗口失败,回退到控制台模式", 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 final Map<String, String> mybatisOverrides = new LinkedHashMap<>();
|
||||||
|
|
||||||
// ---- 字体大小(显示设置) ----
|
// ---- 字体大小(显示设置) ----
|
||||||
private int fontSectionTitle = 20; // 节标题
|
private int fontSectionTitle = 24; // 节标题
|
||||||
private int fontFieldLabel = 18; // 字段标签
|
private int fontFieldLabel = 20; // 字段标签
|
||||||
private int fontButton = 18; // 按钮
|
private int fontButton = 20; // 按钮
|
||||||
private int fontBody = 18; // 正文/提示
|
private int fontBody = 20; // 正文/提示
|
||||||
private int fontMono = 18; // 等宽/代码
|
private int fontMono = 20; // 等宽/代码
|
||||||
|
|
||||||
// ==================== 外部配置加载 ====================
|
// ==================== 外部配置加载 ====================
|
||||||
|
|
||||||
@@ -167,11 +167,11 @@ public class WorkUtilsProperties {
|
|||||||
projectPathsFile = defaults.get(ExternalConfigManager.KEY_PROJECT_PATHS);
|
projectPathsFile = defaults.get(ExternalConfigManager.KEY_PROJECT_PATHS);
|
||||||
damagedReposFile = defaults.get(ExternalConfigManager.KEY_DAMAGED_REPOS);
|
damagedReposFile = defaults.get(ExternalConfigManager.KEY_DAMAGED_REPOS);
|
||||||
mybatisOverrides.clear();
|
mybatisOverrides.clear();
|
||||||
fontSectionTitle = 20;
|
fontSectionTitle = 24;
|
||||||
fontFieldLabel = 18;
|
fontFieldLabel = 20;
|
||||||
fontButton = 18;
|
fontButton = 20;
|
||||||
fontBody = 18;
|
fontBody = 20;
|
||||||
fontMono = 18;
|
fontMono = 20;
|
||||||
log.info("配置已重置为默认值");
|
log.info("配置已重置为默认值");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -106,8 +106,8 @@ public class PluginManager {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 'S' / 's' 字符(readChoiceOrChar 将单字母转成负ASCII码)
|
// '?' 字符(readChoiceOrChar 将单字母转成负ASCII码)
|
||||||
if (choice == -'S') {
|
if (choice == -'?') {
|
||||||
settingsService.showSettings(scanner, pluginContext::resetConfigChecks);
|
settingsService.showSettings(scanner, pluginContext::resetConfigChecks);
|
||||||
// JavaFX 后台线程在 Windows 上可能污染 System.in,
|
// JavaFX 后台线程在 Windows 上可能污染 System.in,
|
||||||
// 清理残留字节并重建 Scanner,确保后续输入正常
|
// 清理残留字节并重建 Scanner,确保后续输入正常
|
||||||
@@ -126,7 +126,7 @@ public class PluginManager {
|
|||||||
PluginDescriptor desc = enabledPlugins.get(i).getDescriptor();
|
PluginDescriptor desc = enabledPlugins.get(i).getDescriptor();
|
||||||
System.out.println((i + 1) + ". " + desc.getName() + " [v" + desc.getVersion() + "]");
|
System.out.println((i + 1) + ". " + desc.getName() + " [v" + desc.getVersion() + "]");
|
||||||
}
|
}
|
||||||
System.out.println("S. 系统设置");
|
System.out.println("?. 系统设置");
|
||||||
System.out.println(EXIT_CODE + ". 退出系统");
|
System.out.println(EXIT_CODE + ". 退出系统");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,7 +145,7 @@ public class PluginManager {
|
|||||||
System.out.println("插件 [" + desc.getName() + "] 配置异常:");
|
System.out.println("插件 [" + desc.getName() + "] 配置异常:");
|
||||||
System.out.println(configError);
|
System.out.println(configError);
|
||||||
System.out.println();
|
System.out.println();
|
||||||
System.out.println("请进入「系统设置(S)」进行配置后再使用。");
|
System.out.println("请进入「系统设置(?)」进行配置后再使用。");
|
||||||
System.out.println("==============================");
|
System.out.println("==============================");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"name": "开发环境",
|
"name": "本地环境-loacalhost-db",
|
||||||
"driver": "com.mysql.jdbc.Driver",
|
"driver": "com.mysql.jdbc.Driver",
|
||||||
"url": "jdbc:mysql://localhost:3306/disburse?useUnicode=true&characterEncoding=utf8&useSSL=false",
|
"url": "jdbc:mysql://localhost:3306/disburse?useUnicode=true&characterEncoding=utf8&useSSL=false",
|
||||||
"username": "root",
|
"username": "root",
|
||||||
"password": "1Qaz@4772"
|
"password": "1Qaz@4772"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "SIT环境",
|
"name": "ft_loancapital_core_db",
|
||||||
"driver": "com.mysql.jdbc.Driver",
|
"driver": "com.oceanbase.jdbc.Driver",
|
||||||
"url": "jdbc:mysql://sit-server:3306/disburse?useUnicode=true&characterEncoding=utf8&useSSL=false",
|
"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": "sit_user",
|
"username": "ft_loancapital_core_app@xydcs_gcz_tnt#ob4x_cluster_xyd",
|
||||||
"password": ""
|
"password": "FT_LOANCAPITAL_CORE@123"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "生产环境(只读)",
|
"name": "cf_loan_core_db",
|
||||||
"driver": "com.mysql.jdbc.Driver",
|
"driver": "com.oceanbase.jdbc.Driver",
|
||||||
"url": "jdbc:mysql://prod-server:3306/disburse?useUnicode=true&characterEncoding=utf8&useSSL=false",
|
"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": "readonly",
|
"username": "cf_loan_core_app@obtest_tnt#ob4x_cluster_test",
|
||||||
"password": ""
|
"password": "CF_LOAN_CORE@123"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user