优化通配符逆向工程

This commit is contained in:
张家训
2026-04-28 16:40:49 +08:00
parent 78268522d6
commit 72315c991f
2 changed files with 135 additions and 31 deletions
@@ -9,11 +9,10 @@ import org.springframework.stereotype.Service;
import java.io.*; import java.io.*;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.sql.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
/** /**
@@ -40,58 +39,130 @@ public class MyBatisGeneratorService {
/** /**
* 根据表名通配符生成Mapper文件 * 根据表名通配符生成Mapper文件
* 先查询数据库中匹配的表,再进行逆向工程
* *
* @param pattern 表名通配符(如 user_%) * @param pattern 表名通配符(如 user_%)
* @param overwrite 是否覆盖已有文件 * @param overwrite 是否覆盖已有文件
* @return 生成结果信息 * @return 生成结果信息
*/ */
public String generateByPattern(String pattern, boolean overwrite) throws Exception { public String generateByPattern(String pattern, boolean overwrite) throws Exception {
List<String> patterns = new ArrayList<>(); // 查询数据库中匹配通配符的表
patterns.add(pattern); List<String> matchedTables = queryTablesByPattern(pattern);
return generateInternal(patterns, overwrite);
if (matchedTables.isEmpty()) {
return "未找到匹配通配符 [" + pattern + "] 的表";
}
log.info("通配符 [{}] 匹配到 {} 张表: {}", pattern, matchedTables.size(), matchedTables);
return generateInternal(matchedTables, overwrite);
}
/**
* 根据通配符查询数据库中的表
*
* @param pattern 表名通配符(支持 % 和 _ 通配符)
* @return 匹配的表名列表
*/
public List<String> queryTablesByPattern(String pattern) throws Exception {
List<String> tables = new ArrayList<>();
Properties props = getConfigurationProperties();
String url = props.getProperty("db_url");
String username = props.getProperty("db_username");
String password = props.getProperty("db_password");
// 从URL中提取数据库名
String dbName = extractDatabaseName(url);
try (Connection conn = DriverManager.getConnection(url, username, password)) {
// 查询匹配的表名
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);
ps.setString(2, pattern);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
tables.add(rs.getString("TABLE_NAME"));
}
}
}
}
return tables;
}
/**
* 查询数据库中所有表
*
* @return 所有表名列表
*/
public List<String> queryAllTables() throws Exception {
List<String> tables = new ArrayList<>();
Properties props = getConfigurationProperties();
String url = props.getProperty("db_url");
String username = props.getProperty("db_username");
String password = props.getProperty("db_password");
String dbName = extractDatabaseName(url);
try (Connection conn = DriverManager.getConnection(url, username, password)) {
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);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
tables.add(rs.getString("TABLE_NAME"));
}
}
}
}
return tables;
}
/**
* 从JDBC URL中提取数据库名
*/
private String extractDatabaseName(String url) {
// jdbc:mysql://localhost:3306/disburse?xxx -> disburse
int start = url.lastIndexOf('/');
int end = url.indexOf('?', start);
if (end == -1) {
end = url.length();
}
return url.substring(start + 1, end);
} }
/** /**
* 内部生成方法 * 内部生成方法
*
* @param tableNames 表名列表
* @param overwrite 是否覆盖已有文件
* @return 生成结果信息
*/ */
private String generateInternal(List<String> tableNames, boolean overwrite) throws Exception { private String generateInternal(List<String> tableNames, boolean overwrite) throws Exception {
// 动态修改配置文件中的表配置
File tempConfigFile = createTempConfigWithTables(tableNames); File tempConfigFile = createTempConfigWithTables(tableNames);
try { try {
// 解析临时配置文件
List<String> warnings = new ArrayList<>(); List<String> warnings = new ArrayList<>();
// 设置使用 JDK 内置 XML 解析器
System.setProperty("javax.xml.parsers.DocumentBuilderFactory", "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl"); System.setProperty("javax.xml.parsers.DocumentBuilderFactory", "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl");
System.setProperty("javax.xml.parsers.SAXParserFactory", "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl"); System.setProperty("javax.xml.parsers.SAXParserFactory", "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl");
ConfigurationParser parser = new ConfigurationParser(warnings); ConfigurationParser parser = new ConfigurationParser(warnings);
Configuration configuration = parser.parseConfiguration(tempConfigFile); Configuration configuration = parser.parseConfiguration(tempConfigFile);
// 执行生成
DefaultShellCallback callback = new DefaultShellCallback(overwrite); DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator generator = new MyBatisGenerator(configuration, callback, warnings); MyBatisGenerator generator = new MyBatisGenerator(configuration, callback, warnings);
generator.generate(null, null, null, true); generator.generate(null, null, null, true);
// 收集结果信息
StringBuilder result = new StringBuilder(); StringBuilder result = new StringBuilder();
result.append("成功生成 ").append(tableNames.size()).append(" 张表的Mapper文件\n");
if (!warnings.isEmpty()) { if (!warnings.isEmpty()) {
result.append("生成完成,有以下警告:\n"); result.append("警告信息:\n");
for (String warning : warnings) { for (String warning : warnings) {
result.append("- ").append(warning).append("\n"); result.append("- ").append(warning).append("\n");
} }
} else {
result.append("生成成功!");
} }
return result.toString(); return result.toString();
} finally { } finally {
// 删除临时文件
if (tempConfigFile.exists()) { if (tempConfigFile.exists()) {
tempConfigFile.delete(); tempConfigFile.delete();
} }
@@ -99,10 +170,9 @@ public class MyBatisGeneratorService {
} }
/** /**
* 创建临时配置文件,包含指定的表配置 * 创建临时配置文件
*/ */
private File createTempConfigWithTables(List<String> tableNames) throws Exception { private File createTempConfigWithTables(List<String> tableNames) throws Exception {
// 读取原始配置文件内容
InputStream is = getClass().getClassLoader().getResourceAsStream(CONFIG_FILE); InputStream is = getClass().getClassLoader().getResourceAsStream(CONFIG_FILE);
if (is == null) { if (is == null) {
throw new RuntimeException("无法找到配置文件: " + CONFIG_FILE); throw new RuntimeException("无法找到配置文件: " + CONFIG_FILE);
@@ -118,11 +188,10 @@ public class MyBatisGeneratorService {
content = sb.toString(); content = sb.toString();
} }
// 使用正则表达式移除原有的 table 元素 // 移除原有的 table 元素
// 匹配 <table ... /> 或 <table ...></table> java.util.regex.Pattern tablePattern = java.util.regex.Pattern.compile(
Pattern tablePattern = Pattern.compile(
"<table[^>]*/>|<table[^>]*>.*?</table>", "<table[^>]*/>|<table[^>]*>.*?</table>",
Pattern.DOTALL java.util.regex.Pattern.DOTALL
); );
content = tablePattern.matcher(content).replaceAll(""); content = tablePattern.matcher(content).replaceAll("");
@@ -140,10 +209,8 @@ public class MyBatisGeneratorService {
.append(" selectByExampleQueryId=\"false\"/>\n\n"); .append(" selectByExampleQueryId=\"false\"/>\n\n");
} }
// 在 </context> 标签前插入新的 table 元素
content = content.replace("</context>", tableElements.toString() + " </context>"); content = content.replace("</context>", tableElements.toString() + " </context>");
// 写入临时文件
File tempFile = File.createTempFile("mybatis-generator-temp", ".xml"); File tempFile = File.createTempFile("mybatis-generator-temp", ".xml");
try (FileOutputStream fos = new FileOutputStream(tempFile)) { try (FileOutputStream fos = new FileOutputStream(tempFile)) {
fos.write(content.getBytes(StandardCharsets.UTF_8)); fos.write(content.getBytes(StandardCharsets.UTF_8));
@@ -154,7 +221,6 @@ public class MyBatisGeneratorService {
/** /**
* 根据表名生成实体类名 * 根据表名生成实体类名
* 例如:user_info -> UserInfoPO
*/ */
private String generateDomainObjectName(String tableName) { private String generateDomainObjectName(String tableName) {
StringBuilder result = new StringBuilder(); StringBuilder result = new StringBuilder();
@@ -22,7 +22,8 @@ public class MyBatisGeneratorHandler extends AbstractMenuHandler {
"1. 根据表名生成(单表)", "1. 根据表名生成(单表)",
"2. 根据表名生成(多表,逗号分隔)", "2. 根据表名生成(多表,逗号分隔)",
"3. 根据表名通配符生成(如 user_%)", "3. 根据表名通配符生成(如 user_%)",
"4. 查看当前配置信息", "4. 查看数据库所有表",
"5. 查看当前配置信息",
"0. 返回上级菜单" "0. 返回上级菜单"
}; };
@@ -64,6 +65,9 @@ public class MyBatisGeneratorHandler extends AbstractMenuHandler {
generateByPattern(scanner); generateByPattern(scanner);
break; break;
case 4: case 4:
showAllTables();
break;
case 5:
showConfiguration(); showConfiguration();
break; break;
default: default:
@@ -131,12 +135,46 @@ public class MyBatisGeneratorHandler extends AbstractMenuHandler {
return; return;
} }
info("开始根据通配符 [{}] 生成Mapper文件", pattern); // 先查询匹配的表
String result = generatorService.generateByPattern(pattern, true); List<String> matchedTables = generatorService.queryTablesByPattern(pattern);
if (matchedTables.isEmpty()) {
System.out.println("未找到匹配通配符 [" + pattern + "] 的表");
return;
}
// 展示匹配到的表
System.out.println("匹配到 " + matchedTables.size() + " 张表:");
for (int i = 0; i < matchedTables.size(); i++) {
System.out.println(" " + (i + 1) + ". " + matchedTables.get(i));
}
// 确认是否继续
System.out.print("是否继续生成?(y/n): ");
String confirm = scanner.nextLine().trim().toLowerCase();
if (!"y".equals(confirm)) {
System.out.println("已取消生成");
return;
}
info("开始根据通配符 [{}] 生成 {} 张表的Mapper文件", pattern, matchedTables.size());
String result = generatorService.generateByTableNames(matchedTables, true);
System.out.println(result); System.out.println(result);
printSuccess("生成完成"); printSuccess("生成完成");
} }
/**
* 显示数据库中所有表
*/
private void showAllTables() throws Exception {
List<String> tables = generatorService.queryAllTables();
System.out.println();
System.out.println("========== 数据库所有表(共 " + tables.size() + " 张)==========");
for (int i = 0; i < tables.size(); i++) {
System.out.println(" " + (i + 1) + ". " + tables.get(i));
}
System.out.println("=================================");
}
/** /**
* 显示当前配置信息 * 显示当前配置信息
*/ */