本文介紹如何用Java編寫高度自定義的代碼生成器
MyBatis 是一款優秀的持久層框架,它支持定製化 SQL、存儲過程以及高級映射。MyBatis 避免了幾乎所有的 JDBC 代碼和手動設置參數以及獲取結果集。MyBatis 可以使用簡單的 XML 或註解來配置和映射原生信息。
上面這一段話來自Mybatis官網的介紹, 初用Mybatis時感覺這個框架相比於JDBC優雅多了, 用起來也如官網說的非常簡單。但是用了一段時間之後, 弊端就慢慢凸顯出來了
使用Mybatis時不得不為每個表創建一個Entity.java、Mapper.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>
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);
}
/<columndo>
GeneratorUtils
在GeneratorUtils 中進行類信息與模版之間的轉換
完整類代碼 GeneratorUtils.java
將表信息放入Velocity模版的上下文中
Map<string> 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);
/<string>
添加模版
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");
/<string>
編譯模版
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 {
}
...
其餘模版
使用
配置文件
在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);
}
閱讀更多 鈺兒愛編程 的文章