Mybatis自動程式碼生成器的實現

智慧辣子雞發表於2018-11-27

原博地址laboo.top/2018/11/26/…

本文介紹如何用Java編寫高度自定義的程式碼生成器

MyBatis 是一款優秀的持久層框架,它支援定製化 SQL、儲存過程以及高階對映。MyBatis 避免了幾乎所有的 JDBC 程式碼和手動設定引數以及獲取結果集。MyBatis 可以使用簡單的 XML 或註解來配置和對映原生資訊。

上面這一段話來自Mybatis官網的介紹, 初用Mybatis時感覺這個框架相比於JDBC優雅多了, 用起來也如官網說的非常簡單。但是用了一段時間之後, 弊端就慢慢凸顯出來了

使用Mybatis時不得不為每個表建立一個Entity.javaMapper.xml(Mapper可以融合入Dao中)Dao.java,Service.java 層次很清晰, 但是太多重複性的工作了, 費時間且易於出錯

並且當資料庫發生一點改動的時候... 苦不堪言

後來出現了自動生成程式碼的外掛, 但是總是不盡人意, 不能隨心所欲地控制, 畢竟每個人的需求都不一樣

本文就來介紹如何簡單的編寫一個自己的程式碼生成器

專案原始碼

mybatis-generator

程式碼實現

實現的思路很簡單, 首先查詢資料庫的表結構, 得到列名, 列型別...等資訊

建立檔案模版, 將這些資訊插入模版中, 最後打包模版進壓縮包匯出

程式碼實現 一共五個Java類

  • TableDO
  • ColumnDO
  • GeneratorMapper
  • GeneratorUtils
  • GeneratorService

首先來看兩個實體類

TableDO 和 ColumnDO

TableDO 存放表名, 對於的類名, 以及列資訊

完整類程式碼 TableDO.java

public class TableDO {

    private String tableName;
    private List<ColumnDO> columns;
    private String className;
    private String suffix;

    // get()... set()...
}
複製程式碼

ColumnDO 存放列名, 資料庫欄位型別, 以及對應Java中的屬性名和型別

完整類程式碼 ColumnDO.java

public class ColumnDO {

    private String columnName;
    private String dataType;
    private String attrName;
    private String attrLowerName;
    private String attrType;

    // get()... set()...
}
複製程式碼

GeneratorMapper

在GeneratorMapper 中, 我們通過表名查詢表自動的資訊

完整類程式碼 GeneratorMapper.java

@Mapper
public interface GeneratorMapper {

    @Select("select column_name columnName, data_type dataType from information_schema.columns where table_name = #{tableName} and table_schema = (select database()) order by ordinal_position")
    List<ColumnDO> listColumns(String tableName);
}
複製程式碼

GeneratorUtils

在GeneratorUtils 中進行類資訊與模版之間的轉換

完整類程式碼 GeneratorUtils.java

將表資訊放入Velocity模版的上下文中

Map<String, Object> map = new HashMap<>();
map.put("tableName", table.getTableName());
map.put("className", table.getClassName());
map.put("pathName", getPackageName().substring(getPackageName().lastIndexOf(".") + 1));
map.put("columns", table.getColumns());
map.put("package", getPackageName());
map.put("suffix", table.getSuffix());

Properties prop = new Properties();
prop.put("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
Velocity.init(prop);
VelocityContext context = new VelocityContext(map);
複製程式碼

新增模版

List<String> templates = new ArrayList<>();
templates.add("mybatis/Model.java.vm");
templates.add("mybatis/Query.java.vm");
templates.add("mybatis/Dao.java.vm");
templates.add("mybatis/Mapper.xml.vm");
templates.add("mybatis/Service.java.vm");
複製程式碼

編譯模版

StringWriter sw = new StringWriter();
Template tpl = Velocity.getTemplate(template, "UTF-8");
tpl.merge(context, sw);
複製程式碼

Utils類完成了生成程式碼的主要工作, 但是程式碼也是比較簡單的

GeneratorService

在Service 中注入Mapper 查詢列資訊, 並用Utils生成程式碼, 然後匯出壓縮包

完整類程式碼 GeneratorService.java

@Service
public class GeneratorService {

    @Resource
    private GeneratorMapper generatorMapper;

    @Resource
    private Environment environment;

    public void generateZip(String[] tableNames, String zipPath) throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zip = new ZipOutputStream(outputStream);
        for (String tableName : tableNames) {
            TableDO table = new TableDO();
            table.setTableName(tableName);
            table.setColumns(generatorMapper.listColumns(tableName));
            GeneratorUtils.generatorCode(table, zip,getConfig());
        }
        IOUtils.closeQuietly(zip);
        FileOutputStream file = new FileOutputStream(zipPath);
        file.write(outputStream.toByteArray());
        file.close();
    }

    // getConfig ...
}
複製程式碼

VM模版

自己寫程式碼生成器的好處就是, 可以根據需求定製自己的模版, 下面是我的幾個模版可以供參考

  • Mapper.xml.vm
  • Dao.java.vm
  • Service.java.vm
  • Model.java.vm
  • Query.java.vm

生成的程式碼是在commons-mybatis架構下使用的

Dao.java.vm

package ${package}.database.dao;

import ${package}.database.model.${className}${suffix};

import org.apache.ibatis.annotations.Mapper;
import org.laziji.commons.mybatis.dao.${suffix}Dao;

@Mapper
public interface ${className}Dao extends ${suffix}Dao<${className}${suffix}> {

}
複製程式碼

...

其餘模版

使用

配置檔案

resources下建立application-${name}.yml檔案, ${name}隨意, 例如: application-example.yml, 可建立多個

配置檔案內容如下, 填入資料庫配置, 以及生成程式碼的包名, 原始檔路徑

spring:
  datasource:
    url: jdbc:mysql://xxx.xxx.xxx.xxx:3306/xxxx?characterEncoding=utf-8
    username: xxxxxx
    password: xxxxxx

generator:
  package: com.xxx.xxx
  resources: mapper
複製程式碼

Test

在test檔案下建立測試類

  • @ActiveProfiles("example")中填入剛才配置檔名的name
  • tableNames需要生成的表, 可以多個
  • zipPath 程式碼匯出路徑 執行測試方法即可
package pg.laziji.generator;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import pg.laziji.generator.mybatis.GeneratorService;

import javax.annotation.Resource;
import java.io.IOException;

@ActiveProfiles("example")
@RunWith(SpringRunner.class)
@SpringBootTest
public class ExampleTest {

    @Resource
    private GeneratorService generatorService;

    @Test
    public void test() throws IOException {
        String[] tableNames = new String[]{"example_table1", "example_table2"};
        String zipPath = "/home/code.zip";
        generatorService.generateZip(tableNames,zipPath);
    }
}

複製程式碼

歡迎關注我的部落格公眾號

2018_11_16_0048241709.png

相關文章