Mybatis代码生成系列(三)-工具适配

前言

在软件开发的世界中,自动化工具的使用可以极大地提高我们的工作效率。特别是在数据库操作方面,手动编写SQL语句和实体类、映射文件等不仅耗时,而且容易出错。MyBatis作为一款广泛使用的持久层框架,其代码生成器mybatis-generator可以帮助我们自动生成这些繁琐的代码。然而,mybatis-generator默认只支持MySQL数据库,对于其他数据库(如Oracle、PostgreSQL等)的支持并不完善。因此,本文将通过分析现有的开源项目mybatis-generator-gui,来探索如何实现一个支持多种数据库的MyBatis代码生成器。

本系列博客将分为以下几个部分:

  1. mybatis-generator-gui 使用介绍:首先,我们将介绍如何使用现有的开源项目mybatis-generator-gui进行可视化代码生成。我们将详细讲解如何配置项目,选择数据库,以及生成代码的过程。
  2. mybatis 代码生成原理探究:在这一部分,我们将深入mybatis-generator的内部,探究其代码生成的原理。我们将分析其配置文件的解析过程,以及如何根据配置信息生成对应的实体类、映射文件和XML配置文件。
  3. 自定义代码生成器实现:在前两部分的基础上,我们将实现自己的代码生成器。我们将重写mybatis-generator的核心代码,使其支持其他数据库。同时,我们也将优化生成的代码,使其更符合我们的项目需求。

通过本系列博客的学习,你将能够掌握MyBatis代码生成器的使用方法和原理,同时也能够根据自己的需求定制代码生成器。无论你是MyBatis初学者,还是有一定经验的开发者,我相信这都将对你的学习和工作有所帮助。让我们一起开始这个旅程吧!

其他文章

Mybatis代码生成系列(一)- mybatis-generator-gui开源项目介绍 - 掘金 (juejin.cn)

Mybatis代码生成系列(二) - mybatis-generator-gui使用原理 - 掘金 (juejin.cn)

正文

本文是对GUI页面的一些适配修改,主要新增如下功能:

  1. 支持配置service和controller生成代码的包名称和目录地址
  2. 支持配置的导入导出,主要是方便工具的使用,使用者可以直接根据配置生成代码,0成本配置;

FXML

属性

PreHeight

控件的初始高度,如果其值超过maxHeight,则控件的初始高度为maxHeight

主页面Service和Controller配置

修改后效果图:

主要是修改如下文件: 首先新增如下行,新增的行分别在第8和第9行;同时在rowContraints中新增两个RowConstrants(一定要新增这个行限制,否者页面布局会乱)

xml 复制代码
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES"/>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES"/>
xml 复制代码
<Label text="Service包名" GridPane.rowIndex="7"/>
<TextField fx:id="serviceTargetPackage" prefHeight="27.0" prefWidth="248.0"
           promptText="com.example.service" GridPane.columnIndex="1"
           GridPane.rowIndex="7">
    <HBox.margin>
        <Insets right="5.0"/>
    </HBox.margin>
    <GridPane.margin>
        <Insets left="5.0" right="5.0"/>
    </GridPane.margin>
</TextField>
<Label text="存放目录" GridPane.columnIndex="2" GridPane.rowIndex="7"/>
<TextField fx:id="serviceTargetDirectory" prefHeight="27.0" prefWidth="155.0"
           promptText="src/main/java" text="src/main/java"
           GridPane.columnIndex="3" GridPane.columnSpan="2"
           GridPane.rowIndex="7">
    <GridPane.margin>
        <Insets left="5.0"/>
    </GridPane.margin>
</TextField>
<Label text="Controller包名" GridPane.rowIndex="8"/>
<TextField fx:id="controllerTargetPackage" prefHeight="27.0" prefWidth="248.0"
           promptText="com.example.controller" GridPane.columnIndex="1"
           GridPane.rowIndex="8">
    <HBox.margin>
        <Insets right="5.0"/>
    </HBox.margin>
    <GridPane.margin>
        <Insets left="5.0" right="5.0"/>
    </GridPane.margin>
</TextField>
<Label text="存放目录" GridPane.columnIndex="2" GridPane.rowIndex="8"/>
<TextField fx:id="controllerTargetDirectory" prefHeight="27.0" prefWidth="155.0"
           promptText="src/main/java" text="src/main/java"
           GridPane.columnIndex="3" GridPane.columnSpan="2"
           GridPane.rowIndex="8">
    <GridPane.margin>
        <Insets left="5.0"/>
    </GridPane.margin>
</TextField>
<Label prefHeight="27.0" prefWidth="99.0" text="映射XML文件包名"
       GridPane.rowIndex="9"/>
<TextField fx:id="mapperTargetPackage" prefHeight="27.0" prefWidth="248.0"
           promptText="com.example" GridPane.columnIndex="1"
           GridPane.rowIndex="9">
    <HBox.margin>
        <Insets right="5.0"/>
    </HBox.margin>
    <GridPane.margin>
        <Insets left="5.0" right="5.0"/>
    </GridPane.margin>
</TextField>
<Label text="存放目录" GridPane.columnIndex="2" GridPane.rowIndex="9"/>
<TextField fx:id="mappingTargetProject" prefHeight="27.0" prefWidth="155.0"
           promptText="src/main/resources" text="src/main/resources"
           GridPane.columnIndex="3" GridPane.columnSpan="2"
           GridPane.rowIndex="9">
    <GridPane.margin>
        <Insets left="5.0"/>
    </GridPane.margin>
</TextField>

GeneratorConfig

由于页面属性增多,需要修改对应的实体类Generator

java 复制代码
// service包名
private String serviceTargetPackage;

// service类存放目录
private String serviceTargetDirectory;

// controller包名
private String controllerTargetPackage;

// controller存放目录
private String controllerTargetDirectory;

并且插入基本的set和get方法

java 复制代码
public String getServiceTargetPackage() {
    return serviceTargetPackage;
}

public void setServiceTargetPackage(String serviceTargetPackage) {
    this.serviceTargetPackage = serviceTargetPackage;
}

public String getServiceTargetDirectory() {
    return serviceTargetDirectory;
}

public void setServiceTargetDirectory(String serviceTargetDirectory) {
    this.serviceTargetDirectory = serviceTargetDirectory;
}

public String getControllerTargetPackage() {
    return controllerTargetPackage;
}

public void setControllerTargetPackage(String controllerTargetPackage) {
    this.controllerTargetPackage = controllerTargetPackage;
}

public String getControllerTargetDirectory() {
    return controllerTargetDirectory;
}

public void setControllerTargetDirectory(String controllerTargetDirectory) {
    this.controllerTargetDirectory = controllerTargetDirectory;
}

MainUIController

新增对应是个TextField控件绑定的在控制器中的属性:

java 复制代码
@FXML
private TextField serviceTargetPackage;
@FXML
private TextField serviceTargetDirectory;
@FXML
private TextField controllerTargetPackage;
@FXML
private TextField controllerTargetDirectory;

同时,在主要面点击保存配置和在配置页面点击应用配置时,新增的service和controller属性需要设置和加载,修改对应的方法:

java 复制代码
  public GeneratorConfig getGeneratorConfigFromUI() {
      GeneratorConfig generatorConfig = new GeneratorConfig();
      generatorConfig.setProjectFolder(projectFolderField.getText());
      generatorConfig.setModelPackage(modelTargetPackage.getText());
      generatorConfig.setGenerateKeys(generateKeysField.getText());
      generatorConfig.setModelPackageTargetFolder(modelTargetProject.getText());
      generatorConfig.setDaoPackage(daoTargetPackage.getText());
      generatorConfig.setDaoTargetFolder(daoTargetProject.getText());
      generatorConfig.setMapperName(mapperName.getText());
      generatorConfig.setMappingXMLPackage(mapperTargetPackage.getText());
      generatorConfig.setMappingXMLTargetFolder(mappingTargetProject.getText());
      generatorConfig.setTableName(tableNameField.getText());
      generatorConfig.setDomainObjectName(domainObjectNameField.getText());
      generatorConfig.setOffsetLimit(offsetLimitCheckBox.isSelected());
      generatorConfig.setComment(commentCheckBox.isSelected());
      generatorConfig.setOverrideXML(overrideXML.isSelected());
      generatorConfig.setNeedToStringHashcodeEquals(needToStringHashcodeEquals.isSelected());
      generatorConfig.setUseLombokPlugin(useLombokPlugin.isSelected());
      generatorConfig.setUseTableNameAlias(useTableNameAliasCheckbox.isSelected());
      generatorConfig.setNeedForUpdate(forUpdateCheckBox.isSelected());
      generatorConfig.setAnnotationDAO(annotationDAOCheckBox.isSelected());
      generatorConfig.setAnnotation(annotationCheckBox.isSelected());
      generatorConfig.setUseActualColumnNames(useActualColumnNamesCheckbox.isSelected());
      generatorConfig.setEncoding(encodingChoice.getValue());
      generatorConfig.setUseExample(useExample.isSelected());
      generatorConfig.setUseDAOExtendStyle(useDAOExtendStyle.isSelected());
      generatorConfig.setUseSchemaPrefix(useSchemaPrefix.isSelected());
      generatorConfig.setJsr310Support(jsr310Support.isSelected());
      generatorConfig.setServiceTargetDirectory(serviceTargetDirectory.getText());
      generatorConfig.setServiceTargetPackage(serviceTargetPackage.getText());
      generatorConfig.setControllerTargetDirectory(controllerTargetDirectory.getText());
      generatorConfig.setControllerTargetPackage(controllerTargetPackage.getText());
      return generatorConfig;
  }

  public void setGeneratorConfigIntoUI(GeneratorConfig generatorConfig) {
      projectFolderField.setText(generatorConfig.getProjectFolder());
      modelTargetPackage.setText(generatorConfig.getModelPackage());
      generateKeysField.setText(generatorConfig.getGenerateKeys());
      modelTargetProject.setText(generatorConfig.getModelPackageTargetFolder());
      daoTargetPackage.setText(generatorConfig.getDaoPackage());
daoTargetProject.setText(generatorConfig.getDaoTargetFolder());
mapperTargetPackage.setText(generatorConfig.getMappingXMLPackage());
      mappingTargetProject.setText(generatorConfig.getMappingXMLTargetFolder());
      if (StringUtils.isBlank(tableNameField.getText())) {
          tableNameField.setText(generatorConfig.getTableName());
          mapperName.setText(generatorConfig.getMapperName());
          domainObjectNameField.setText(generatorConfig.getDomainObjectName());
      }
      offsetLimitCheckBox.setSelected(generatorConfig.isOffsetLimit());
      commentCheckBox.setSelected(generatorConfig.isComment());
      overrideXML.setSelected(generatorConfig.isOverrideXML());
      needToStringHashcodeEquals.setSelected(generatorConfig.isNeedToStringHashcodeEquals());
      useLombokPlugin.setSelected(generatorConfig.isUseLombokPlugin());
      useTableNameAliasCheckbox.setSelected(generatorConfig.getUseTableNameAlias());
      forUpdateCheckBox.setSelected(generatorConfig.isNeedForUpdate());
      annotationDAOCheckBox.setSelected(generatorConfig.isAnnotationDAO());
      annotationCheckBox.setSelected(generatorConfig.isAnnotation());
      useActualColumnNamesCheckbox.setSelected(generatorConfig.isUseActualColumnNames());
      encodingChoice.setValue(generatorConfig.getEncoding());
      useExample.setSelected(generatorConfig.isUseExample());
      useDAOExtendStyle.setSelected(generatorConfig.isUseDAOExtendStyle());
      useSchemaPrefix.setSelected(generatorConfig.isUseSchemaPrefix());
      jsr310Support.setSelected(generatorConfig.isJsr310Support());
      serviceTargetPackage.setText(generatorConfig.getServiceTargetPackage());
      serviceTargetDirectory.setText(generatorConfig.getServiceTargetDirectory());
      controllerTargetDirectory.setText(generatorConfig.getControllerTargetDirectory());
      controllerTargetPackage.setText(generatorConfig.getControllerTargetPackage());
  }

MybatisGeneratorBridge

为了让最后页面的service和controller参数在生成代码生效,还需要将参数传入插件:

java 复制代码
// 生成service和controller插件
PluginConfiguration pluginConfiguration = new PluginConfiguration();
pluginConfiguration.addProperty("service", "com.zzg.mybatis.generator.plugins.ServiceAndControllerGeneratorPlugin");
pluginConfiguration.addProperty("targetProject", generatorConfig.getProjectFolder() + "/" + generatorConfig.getServiceTargetDirectory());
pluginConfiguration.addProperty("servicePackage", generatorConfig.getServiceTargetPackage());
pluginConfiguration.addProperty("serviceImplPackage",  generatorConfig.getControllerTargetDirectory());
pluginConfiguration.addProperty("controllerPackage", generatorConfig.getControllerTargetPackage());
pluginConfiguration.addProperty("serviceSuffix", "Service");
pluginConfiguration.addProperty("serviceImplSuffix", "ServiceImpl");
pluginConfiguration.addProperty("controllerSuffix", "Controller");
pluginConfiguration.setConfigurationType("com.zzg.mybatis.generator.plugins.ServiceAndControllerGeneratorPlugin");
context.addPluginConfiguration(pluginConfiguration);

配置页面导入导出

修改后效果:

主要修改文件如下:

generatorConfigs

新增导入配置按钮:

xml 复制代码
<Button mnemonicParsing="false" onAction="#importGeneratorConfig"
        text="导入配置"
        AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="350.0">
    <styleClass>
        <String fx:value="btn"/>
        <String fx:value="btn-default"/>
    </styleClass>
</Button>

绑定importGeneratorConfig9(在GeneratorConfigController中)方法:

java 复制代码
@FXML
public void importGeneratorConfig() {
    FileChooser fileChooser = new FileChooser();

    //设置文件上传类型
    FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("json files (*.json)", "*.json");
    fileChooser.getExtensionFilters().add(extFilter);
    File file = fileChooser.showOpenDialog(this.mainUIController.getPrimaryStage());
    StringBuilder config = new StringBuilder();
    String content = "";
    GeneratorConfig generatorConfig = null;
    try {
        BufferedReader bufferedReader = new BufferedReader(new FileReader( file));
        while ((content = bufferedReader.readLine()) != null) {
            config.append(content);
        }
        generatorConfig = JSONObject.parseObject(config.toString(), GeneratorConfig.class);

        // 设置文件名为导入配置文件的默认名
        ConfigHelper.deleteGeneratorConfig(file.getName());
        generatorConfig.setName(file.getName().split(".json")[0]);
        ConfigHelper.saveGeneratorConfig(generatorConfig);

        // 导入成功后,刷新页面
        refreshTableView();
    } catch (FileNotFoundException e) {
        _LOG.error("导入配置失败: 配置文件不存在");
    } catch (IOException e) {
        _LOG.error("导入配置失败: 读取配置文件失败", e);
    } catch (Exception e) {
       _LOG.error("导入配置失败:保存配置失败", e);
    }
}

GeneratorConfigController

新增导出配置按钮,修改对应的而初始化方法:

java 复制代码
@Override
public void initialize(URL location, ResourceBundle resources) {
    controller = this;
    nameColumn.setCellValueFactory(new PropertyValueFactory<>("name"));
    // 自定义操作列
    opsColumn.setCellValueFactory(new PropertyValueFactory<>("name"));
    opsColumn.setCellFactory(cell -> {
        return new TableCell() {
            @Override
            protected void updateItem(Object item, boolean empty) {
                super.updateItem(item, empty);
                if (item == null || empty) {
                    setText(null);
                    setGraphic(null);
                } else {
                    Button btn1 = new Button("应用");
                    Button btn2 = new Button("删除");
                    Button btn3 = new Button("导出");
                    HBox hBox = new HBox();
                    hBox.setSpacing(10);
                    hBox.getChildren().add(btn1);
                    hBox.getChildren().add(btn2);
                    hBox.getChildren().add(btn3);
                    btn1.setOnAction(event -> {
                        try {
                            // 应用配置
                            GeneratorConfig generatorConfig = ConfigHelper.loadGeneratorConfig(item.toString());
                            mainUIController.setGeneratorConfigIntoUI(generatorConfig);
                            controller.closeDialogStage();
                        } catch (Exception e) {
                            AlertUtil.showErrorAlert(e.getMessage());
                        }
                    });
                    btn2.setOnAction(event -> {
                        try {
                            // 删除配置
                            _LOG.debug("item: {}", item);
                            ConfigHelper.deleteGeneratorConfig(item.toString());
                            refreshTableView();
                        } catch (Exception e) {
                            AlertUtil.showErrorAlert(e.getMessage());
                        }
                    });
                    btn3.setOnAction(event -> {
                        try {
                            _LOG.info("导出配置: {}", item);
                            ConfigHelper.exportGeneratorConfig(item.toString(), mainUIController.getPrimaryStage());

                            // 导入成功后,刷新组件
                            refreshTableView();
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                    });
                    setGraphic(hBox);
                }
            }
        };
    });
    refreshTableView();
}

ConfigHelper

在该工具类中新增导出方法:

java 复制代码
public static void exportGeneratorConfig(String name, Stage primaryStage) {
    try {
       GeneratorConfig generatorConfig = loadGeneratorConfig(name);
       DirectoryChooser directoryChooser = new DirectoryChooser();
       directoryChooser.setTitle("请选择导出配置文件的目录");
       directoryChooser.setInitialDirectory(new File(generatorConfig.getProjectFolder()));
       File file = directoryChooser.showDialog(primaryStage);

       // 设置配置名称为默认配置导出文件名
       File exportFile = new File(file.getAbsolutePath().concat("\").concat(name).concat(".json"));
       BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(exportFile));
       bufferedWriter.write(JSONObject.toJSONString(generatorConfig));
       bufferedWriter.close();
    } catch (Exception e) {
       _LOG.error("导出配置失败: 数据库加载配置失败");
    }
}

小结

本文只是简单的通过修改源代码从而适配GUI的使用;

后续使用的话,还想新增生成自定义类文件注释,这个就需要深入了解mybatis生成器的配置和原理,后续的文章会着重分析mybatis生成代码的原理;

参考

TextField(文本框) - 《JavaFX 教程中文翻译》 - 极客文档 (geekdaxue.co) JavaFX教程 (yiibai.com)

相关推荐
ok!ko2 小时前
设计模式之原型模式(通俗易懂--代码辅助理解【Java版】)
java·设计模式·原型模式
2401_857622662 小时前
SpringBoot框架下校园资料库的构建与优化
spring boot·后端·php
2402_857589362 小时前
“衣依”服装销售平台:Spring Boot框架的设计与实现
java·spring boot·后端
吾爱星辰3 小时前
Kotlin 处理字符串和正则表达式(二十一)
java·开发语言·jvm·正则表达式·kotlin
哎呦没3 小时前
大学生就业招聘:Spring Boot系统的架构分析
java·spring boot·后端
_.Switch4 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
编程、小哥哥4 小时前
netty之Netty与SpringBoot整合
java·spring boot·spring
IT学长编程5 小时前
计算机毕业设计 玩具租赁系统的设计与实现 Java实战项目 附源码+文档+视频讲解
java·spring boot·毕业设计·课程设计·毕业论文·计算机毕业设计选题·玩具租赁系统
莹雨潇潇5 小时前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器
杨哥带你写代码5 小时前
足球青训俱乐部管理:Spring Boot技术驱动
java·spring boot·后端