From 1e389c46018375e7ac15b2f2af2bdd22d1b81e01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=AE=B6=E8=AE=AD?= Date: Tue, 28 Apr 2026 13:41:23 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B7=B2=E6=B5=8B=E8=AF=95=E7=94=9F=E6=88=90?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E6=AD=A3=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../generator/MyBatisGeneratorService.java | 219 ++++++++++++++++++ .../example/workutils/menu/api/MenuItem.java | 66 ++++++ .../menu/handler/MyBatisGeneratorHandler.java | 186 +++++++++++++++ 3 files changed, 471 insertions(+) create mode 100644 src/main/java/com/example/workutils/biz/mybatis/generator/MyBatisGeneratorService.java create mode 100644 src/main/java/com/example/workutils/menu/api/MenuItem.java create mode 100644 src/main/java/com/example/workutils/menu/handler/MyBatisGeneratorHandler.java diff --git a/src/main/java/com/example/workutils/biz/mybatis/generator/MyBatisGeneratorService.java b/src/main/java/com/example/workutils/biz/mybatis/generator/MyBatisGeneratorService.java new file mode 100644 index 0000000..5828331 --- /dev/null +++ b/src/main/java/com/example/workutils/biz/mybatis/generator/MyBatisGeneratorService.java @@ -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 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 patterns = new ArrayList<>(); + patterns.add(pattern); + return generateInternal(patterns, overwrite); + } + + /** + * 内部生成方法 + * + * @param tableNames 表名列表 + * @param overwrite 是否覆盖已有文件 + * @return 生成结果信息 + */ + private String generateInternal(List tableNames, boolean overwrite) throws Exception { + // 动态修改配置文件中的表配置 + File tempConfigFile = createTempConfigWithTables(tableNames); + + try { + // 解析临时配置文件 + List 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 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 元素 + // 匹配 或
+ Pattern tablePattern = Pattern.compile( + "]*/>|]*>.*?", + Pattern.DOTALL + ); + content = tablePattern.matcher(content).replaceAll(""); + + // 构建新的 table 元素 + StringBuilder tableElements = new StringBuilder(); + for (String tableName : tableNames) { + tableElements.append(" \n\n"); + } + + // 在 标签前插入新的 table 元素 + content = content.replace("", tableElements.toString() + " "); + + // 写入临时文件 + 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", ""); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/workutils/menu/api/MenuItem.java b/src/main/java/com/example/workutils/menu/api/MenuItem.java new file mode 100644 index 0000000..ff59203 --- /dev/null +++ b/src/main/java/com/example/workutils/menu/api/MenuItem.java @@ -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 handlerClass; + + MenuItem(int code, String name, Class 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 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; + } +} diff --git a/src/main/java/com/example/workutils/menu/handler/MyBatisGeneratorHandler.java b/src/main/java/com/example/workutils/menu/handler/MyBatisGeneratorHandler.java new file mode 100644 index 0000000..d65e8e8 --- /dev/null +++ b/src/main/java/com/example/workutils/menu/handler/MyBatisGeneratorHandler.java @@ -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 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; + } + } +}