已测试生成功能正常

This commit is contained in:
张家训
2026-04-28 13:41:23 +08:00
parent ee127d8fb2
commit 1e389c4601
3 changed files with 471 additions and 0 deletions
@@ -0,0 +1,219 @@
package com.example.workutils.biz.mybatis.generator;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;
import org.springframework.stereotype.Service;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* MyBatis逆向工程服务
* 用于根据数据库表结构生成Mapper文件
*/
@Slf4j
@Service
public class MyBatisGeneratorService {
private static final String CONFIG_FILE = "mybatis-generator.xml";
private static final String PROPERTIES_FILE = "mybatis-generator.properties";
/**
* 根据表名列表生成Mapper文件
*
* @param tableNames 表名列表
* @param overwrite 是否覆盖已有文件
* @return 生成结果信息
*/
public String generateByTableNames(List<String> tableNames, boolean overwrite) throws Exception {
return generateInternal(tableNames, overwrite);
}
/**
* 根据表名通配符生成Mapper文件
*
* @param pattern 表名通配符(如 user_%)
* @param overwrite 是否覆盖已有文件
* @return 生成结果信息
*/
public String generateByPattern(String pattern, boolean overwrite) throws Exception {
List<String> patterns = new ArrayList<>();
patterns.add(pattern);
return generateInternal(patterns, overwrite);
}
/**
* 内部生成方法
*
* @param tableNames 表名列表
* @param overwrite 是否覆盖已有文件
* @return 生成结果信息
*/
private String generateInternal(List<String> tableNames, boolean overwrite) throws Exception {
// 动态修改配置文件中的表配置
File tempConfigFile = createTempConfigWithTables(tableNames);
try {
// 解析临时配置文件
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.SAXParserFactory", "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl");
ConfigurationParser parser = new ConfigurationParser(warnings);
Configuration configuration = parser.parseConfiguration(tempConfigFile);
// 执行生成
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator generator = new MyBatisGenerator(configuration, callback, warnings);
generator.generate(null, null, null, true);
// 收集结果信息
StringBuilder result = new StringBuilder();
if (!warnings.isEmpty()) {
result.append("生成完成,有以下警告:\n");
for (String warning : warnings) {
result.append("- ").append(warning).append("\n");
}
} else {
result.append("生成成功!");
}
return result.toString();
} finally {
// 删除临时文件
if (tempConfigFile.exists()) {
tempConfigFile.delete();
}
}
}
/**
* 创建临时配置文件,包含指定的表配置
*/
private File createTempConfigWithTables(List<String> tableNames) throws Exception {
// 读取原始配置文件内容
InputStream is = getClass().getClassLoader().getResourceAsStream(CONFIG_FILE);
if (is == null) {
throw new RuntimeException("无法找到配置文件: " + CONFIG_FILE);
}
String content;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line).append("\n");
}
content = sb.toString();
}
// 使用正则表达式移除原有的 table 元素
// 匹配 <table ... /> 或 <table ...></table>
Pattern tablePattern = Pattern.compile(
"<table[^>]*/>|<table[^>]*>.*?</table>",
Pattern.DOTALL
);
content = tablePattern.matcher(content).replaceAll("");
// 构建新的 table 元素
StringBuilder tableElements = new StringBuilder();
for (String tableName : tableNames) {
tableElements.append(" <table schema=\"\" tableName=\"")
.append(tableName)
.append("\"\n")
.append(" domainObjectName=\"")
.append(generateDomainObjectName(tableName))
.append("\"\n")
.append(" enableCountByExample=\"false\" enableUpdateByExample=\"false\"\n")
.append(" enableDeleteByExample=\"false\" enableSelectByExample=\"false\"\n")
.append(" selectByExampleQueryId=\"false\"/>\n\n");
}
// 在 </context> 标签前插入新的 table 元素
content = content.replace("</context>", tableElements.toString() + " </context>");
// 写入临时文件
File tempFile = File.createTempFile("mybatis-generator-temp", ".xml");
try (FileOutputStream fos = new FileOutputStream(tempFile)) {
fos.write(content.getBytes(StandardCharsets.UTF_8));
}
return tempFile;
}
/**
* 根据表名生成实体类名
* 例如:user_info -> UserInfoPO
*/
private String generateDomainObjectName(String tableName) {
StringBuilder result = new StringBuilder();
String[] parts = tableName.split("_");
for (String part : parts) {
if (part.length() > 0) {
result.append(part.substring(0, 1).toUpperCase())
.append(part.substring(1).toLowerCase());
}
}
result.append("PO");
return result.toString();
}
/**
* 获取配置属性
*/
public Properties getConfigurationProperties() {
Properties props = new Properties();
try {
props.load(getClass().getClassLoader().getResourceAsStream(PROPERTIES_FILE));
} catch (Exception e) {
log.warn("无法加载配置属性文件", e);
}
return props;
}
/**
* 获取数据库URL
*/
public String getDatabaseUrl() {
return getConfigurationProperties().getProperty("db_url", "");
}
/**
* 获取数据库用户名
*/
public String getDatabaseUsername() {
return getConfigurationProperties().getProperty("db_username", "");
}
/**
* 获取Model包路径
*/
public String getModelPackage() {
return getConfigurationProperties().getProperty("model_package", "");
}
/**
* 获取Mapper包路径
*/
public String getMapperPackage() {
return getConfigurationProperties().getProperty("dao_package", "");
}
/**
* 获取SQL Mapper包路径
*/
public String getSqlMapperPackage() {
return getConfigurationProperties().getProperty("sqlmapper_package", "");
}
}
@@ -0,0 +1,66 @@
package com.example.workutils.menu.api;
import com.example.workutils.menu.handler.AbstractMenuHandler;
import com.example.workutils.menu.handler.BatchHandler;
import com.example.workutils.menu.handler.GitHealthHandler;
import com.example.workutils.menu.handler.MyBatisGeneratorHandler;
import com.example.workutils.menu.handler.MyBatisHandler;
import com.example.workutils.menu.handler.ScanHandler;
/**
* 菜单项枚举
* 统一管理菜单序号、名称和处理器
*/
public enum MenuItem {
SCAN(1, "关键词扫描", ScanHandler.class),
MYBATIS(2, "MyBatis导出", MyBatisHandler.class),
BATCH(3, "批量处理", BatchHandler.class),
GIT_HEALTH(4, "Git仓库健康检测", GitHealthHandler.class),
MYBATIS_GENERATOR(5, "MyBatis逆向工程", MyBatisGeneratorHandler.class),
EXIT(0, "退出系统", null);
private final int code;
private final String name;
private final Class<? extends AbstractMenuHandler> handlerClass;
MenuItem(int code, String name, Class<? extends AbstractMenuHandler> handlerClass) {
this.code = code;
this.name = name;
this.handlerClass = handlerClass;
}
public int getCode() {
return code;
}
public String getName() {
return name;
}
public String getDisplayText() {
return code + ". " + name;
}
public Class<? extends AbstractMenuHandler> getHandlerClass() {
return handlerClass;
}
public boolean hasHandler() {
return handlerClass != null;
}
/**
* 根据序号获取菜单项
*
* @param code 菜单序号
* @return 菜单项,未找到返回null
*/
public static MenuItem fromCode(int code) {
for (MenuItem item : values()) {
if (item.code == code) {
return item;
}
}
return null;
}
}
@@ -0,0 +1,186 @@
package com.example.workutils.menu.handler;
import com.example.workutils.biz.mybatis.generator.MyBatisGeneratorService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;
/**
* MyBatis逆向工程菜单处理器
* 用于根据数据库表结构生成Mapper文件
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class MyBatisGeneratorHandler extends AbstractMenuHandler {
private static final String[] MENU_OPTIONS = {
"1. 根据表名生成(单表)",
"2. 根据表名生成(多表,逗号分隔)",
"3. 根据表名通配符生成(如 user_%)",
"4. 查看当前配置信息",
"0. 返回上级菜单"
};
private final MyBatisGeneratorService generatorService;
@Override
public String getMenuName() {
return "MyBatis逆向工程";
}
@Override
public String[] getMenuOptions() {
return MENU_OPTIONS;
}
@Override
public void execute(Scanner scanner) {
while (true) {
printMenuTitle(getMenuName());
displayMenu(getMenuOptions());
int choice = readChoice(scanner);
scanner.nextLine();
if (choice == 0) {
info("返回上级菜单");
return;
}
try {
switch (choice) {
case 1:
generateBySingleTable(scanner);
break;
case 2:
generateByMultipleTables(scanner);
break;
case 3:
generateByPattern(scanner);
break;
case 4:
showConfiguration();
break;
default:
printInvalidOption();
break;
}
} catch (Exception e) {
error("逆向工程执行失败", e);
printError(e.getMessage());
}
}
}
/**
* 根据单表名生成
*/
private void generateBySingleTable(Scanner scanner) throws Exception {
System.out.print("请输入表名: ");
String tableName = scanner.nextLine().trim();
if (tableName.isEmpty()) {
System.out.println("表名不能为空");
return;
}
info("开始生成表 [{}] 的Mapper文件", tableName);
String result = generatorService.generateByTableNames(Arrays.asList(tableName), true);
System.out.println(result);
printSuccess("生成完成");
}
/**
* 根据多表名生成
*/
private void generateByMultipleTables(Scanner scanner) throws Exception {
System.out.print("请输入表名(多个用逗号分隔): ");
String input = scanner.nextLine().trim();
if (input.isEmpty()) {
System.out.println("表名不能为空");
return;
}
List<String> tableNames = Arrays.asList(input.split(","));
// 去除空格
for (int i = 0; i < tableNames.size(); i++) {
tableNames.set(i, tableNames.get(i).trim());
}
info("开始生成 {} 个表的Mapper文件", tableNames.size());
String result = generatorService.generateByTableNames(tableNames, true);
System.out.println(result);
printSuccess("生成完成");
}
/**
* 根据通配符生成
*/
private void generateByPattern(Scanner scanner) throws Exception {
System.out.print("请输入表名通配符(如 user_%): ");
String pattern = scanner.nextLine().trim();
if (pattern.isEmpty()) {
System.out.println("通配符不能为空");
return;
}
info("开始根据通配符 [{}] 生成Mapper文件", pattern);
String result = generatorService.generateByPattern(pattern, true);
System.out.println(result);
printSuccess("生成完成");
}
/**
* 显示当前配置信息
*/
private void showConfiguration() {
System.out.println();
System.out.println("========== 当前配置信息 ==========");
System.out.println("数据库URL: " + maskPassword(generatorService.getDatabaseUrl()));
System.out.println("数据库用户: " + generatorService.getDatabaseUsername());
System.out.println("Model包路径: " + generatorService.getModelPackage());
System.out.println("Mapper接口包路径: " + generatorService.getMapperPackage());
System.out.println("SQL Mapper包路径: " + generatorService.getSqlMapperPackage());
System.out.println("=================================");
}
/**
* 遮蔽URL中的密码信息
*/
private String maskPassword(String url) {
if (url == null) {
return "";
}
// 简单处理,移除可能存在的密码参数
return url.replaceAll("password=[^&]*", "password=***");
}
/**
* 显示菜单选项
*/
private void displayMenu(String[] options) {
System.out.println("请选择操作:");
for (String option : options) {
System.out.println(option);
}
}
/**
* 读取用户选择
*/
private int readChoice(Scanner scanner) {
try {
System.out.print("请输入选项编号: ");
return scanner.nextInt();
} catch (Exception e) {
return -1;
}
}
}