已测试生成功能正常
This commit is contained in:
+219
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user