---
url: >-
  https://www.dreamlu.net/blog/2018-01-25-JFinal-generates-the-markdown-format-data-dictionary.md
description: >-
  借用 JFinal 内置的 DataDictionaryGenerator，重写表头与单元格生成逻辑，把 MySQL 命令行风格的数据字典直接输出成
  markdown 表格。
---

## 设计初衷

利用JFinal的代码生成可以为我们生成漂亮的数据字典。其格式类似Mysql的命令行，可见波总的用心。

```html
Table: t_ad (广告表)
----------------+---------------------+------+-----+-------------------+---------
 Field          | Type                | Null | Key | Default           | Remarks 
----------------+---------------------+------+-----+-------------------+---------
 id             | INT UNSIGNED(10)    | NO   | PRI |                   | 主键id    
 ad_position_id | INT UNSIGNED(10)    | NO   |     | 0                 | 广告位id   
 title          | VARCHAR(64)         | NO   |     |                   | 广告标题    
 content        | VARCHAR(255)        | NO   |     |                   | 广告内容    
 media_type     | TINYINT UNSIGNED(2) | NO   |     | 0                 | 媒体类型    
 link           | VARCHAR(255)        | NO   |     |                   | 链接地址    
 image_url      | VARCHAR(255)        | NO   |     |                   | 图片地址    
 end_time       | DATETIME(19)        | NO   |     | CURRENT_TIMESTAMP | 结束时间    
 seq            | TINYINT(3)          | NO   |     | 0                 | 排序      
 enabled        | TINYINT UNSIGNED(2) | NO   |     | 1                 | 是否启用     
 creator        | VARCHAR(32)         | NO   |     |                   | 格式:创建者[id]
 updator        | VARCHAR(32)         | NO   |     |                   | 格式:创建者[id]
 create_time    | DATETIME(19)        | NO   |     | CURRENT_TIMESTAMP | 创建时间    
 update_time    | DATETIME(19)        | NO   |     | CURRENT_TIMESTAMP | 更新时间    
----------------+---------------------+------+-----+-------------------+---------
```

在实际工作中现在我们更多的是采用`markdown`格式的文档，研究一番可以发现JFinal生成的数据字典和`markdown`的Table比较类型。下面来看看我的改造过程。

## 改造数据字典格式

### 添加Druid链接参数

```java
DruidPlugin druidPlugin = new DruidPlugin(jdbcUrl, user, password);
// remarks=true设置可以获取column remarks信息
// useInformationSchema=true设置可以获取tables remarks信息
druidPlugin.setConnectionProperties("useInformationSchema=true;remarks=true");
druidPlugin.start();
return druidPlugin.getDataSource();
```

### 重写DataDictionaryGenerator

```java
import javax.sql.DataSource;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.generator.ColumnMeta;
import com.jfinal.plugin.activerecord.generator.TableMeta;

public class MyDataDictionaryGenerator extends com.jfinal.plugin.activerecord.generator.DataDictionaryGenerator {

	public DataDictionaryGenerator(DataSource dataSource, String dataDictionaryOutputDir) {
		super(dataSource, dataDictionaryOutputDir);
	}

	/**
	 * 重写掉生成换行
	 */
	@Override
	protected String genSeparateLine(TableMeta tm) {
		return "";
	}

	protected void generateTable(TableMeta tableMeta, StringBuilder ret) {
		ret.append("Table: ").append(tableMeta.name);
		if (StrKit.notBlank(tableMeta.remarks)) {
			ret.append("（").append(clearBlank(tableMeta.remarks)).append("）");
		}
		ret.append("\n");
		
		String sparateLine = genSeparateLine(tableMeta);
		ret.append(sparateLine);
		genTableHead(tableMeta, ret);
		ret.append(sparateLine);
		for (ColumnMeta columnMeta : tableMeta.columnMetas) {
			genColumn(tableMeta, columnMeta, ret);
		}
		ret.append(sparateLine);
		ret.append("\n");
	}

	/**
	 * 表头
	 */
	@Override
	protected void genTableHead(TableMeta tm, StringBuilder ret) {
		ret.append('\n').append('|');
		genCell(tm.colNameMaxLen, " ", "Field", " ", "|",  ret);
		genCell(tm.colTypeMaxLen, " ", "Type", " ", "|", ret);
		genCell("Null".length(), " ", "Null",  " ","|", ret);
		genCell("Key".length(), " ", "Key",  " ","|", ret);
		genCell(tm.colDefaultValueMaxLen, " ", "Default", " ", "|", ret);
		genCell("Remarks".length(), " ", "Remarks", " ", "|", ret);
		ret.append('\n').append('|');
		genCell(tm.colNameMaxLen, " ", "-", "-", "|",  ret);
		ret.replace(ret.length() - 2, ret.length() - 1, " ");
		genCell(tm.colTypeMaxLen, " ", "-", "-", "|", ret);
		ret.replace(ret.length() - 2, ret.length() - 1, " ");
		genCell("Null".length(), " ", "-",  "-","|", ret);
		ret.replace(ret.length() - 2, ret.length() - 1, " ");
		genCell("Key".length(), " ", "-",  "-","|", ret);
		ret.replace(ret.length() - 2, ret.length() - 1, " ");
		genCell(tm.colDefaultValueMaxLen, " ", "-", "-", "|", ret);
		ret.replace(ret.length() - 2, ret.length() - 1, " ");
		genCell("Remarks".length(), " ", "-", "-", "|", ret);
		ret.replace(ret.length() - 2, ret.length() - 1, " ");
		ret.append('\n');
	}

	/**
	 * 列
	 */
	@Override
	protected void genColumn(TableMeta tableMeta, ColumnMeta columnMeta, StringBuilder ret) {
		ret.append('|');
		genCell(tableMeta.colNameMaxLen, " ", columnMeta.name, " ", "|",  ret);
		genCell(tableMeta.colTypeMaxLen, " ", columnMeta.type, " ", "|", ret);
		genCell("Null".length(), " ", columnMeta.isNullable, " ", "|", ret);
		genCell("Key".length(), " ", columnMeta.isPrimaryKey, " ", "|", ret);
		genCell(tableMeta.colDefaultValueMaxLen, " ", columnMeta.defaultValue, " ", "|", ret);
		genCell("Remarks".length(), " ", clearBlank(columnMeta.remarks), " ", "|", ret);
		ret.append("\n");
	}
	
	/**
	 * 清除注释中的空白符
	 */
	private String clearBlank(String str) {
		return str.replaceAll("[ |\t|\n]+", "");
	}
}
```

### 配置新数据字典生成类

```java
// 创建生成器
Generator generator = new Generator(dataSource, baseModelPackageName, baseModelOutputDir, modelPackageName, modelOutputDir);
DataDictionaryGenerator dg = new MyDataDictionaryGenerator(dataSource, modelOutputDir);
dg.setDataDictionaryFileName("数据字典.md");
generator.setDataDictionaryGenerator(dg);
```

到此大工搞成，迫不及待的看看效果展示了~

### 效果展示

Table: t_ad (广告表)

| Field          | Type                | Null | Key | Default           | Remarks |
| -------------- | ------------------- | ---- | --- | ----------------- | ------- |
| id             | INT UNSIGNED(10)    | NO   | PRI |                   | 主键id    |
| ad_position_id | INT UNSIGNED(10)    | NO   |     | 0                 | 广告位id   |
| title          | VARCHAR(64)         | NO   |     |                   | 广告标题    |
| content        | VARCHAR(255)        | NO   |     |                   | 广告内容    |
| media_type     | TINYINT UNSIGNED(3) | NO   |     | 0                 | 媒体类型    |
| link           | VARCHAR(255)        | NO   |     |                   | 链接地址    |
| image_url      | VARCHAR(255)        | NO   |     |                   | 图片地址    |
| end_time       | DATETIME(19)        | NO   |     | CURRENT_TIMESTAMP | 结束时间    |
| seq            | TINYINT(3)          | NO   |     | 0                 | 排序      |
| enabled        | TINYINT UNSIGNED(3) | NO   |     | 1                 |         |
| creator        | VARCHAR(32)         | NO   |     |                   | 格式:创建者\[id]|
| updator        | VARCHAR(32)         | NO   |     |                   | 格式:创建者\[id]|
| create_time    | DATETIME(19)        | NO   |     | CURRENT_TIMESTAMP | 创建时间    |
| update_time    | DATETIME(19)        | NO   |     | CURRENT_TIMESTAMP | 更新时间    |

## 开源推荐

* Spring boot 微服务高效开发 `mica` 工具集：<https://gitee.com/596392912/mica>
* `Avue` 一款基于vue可配置化的神奇框架：<https://gitee.com/smallweigit/avue>
* `pig` 宇宙最强微服务（架构师必备）：<https://gitee.com/log4j/pig>
* `SpringBlade` 完整的线上解决方案（企业开发必备）：<https://gitee.com/smallc/SpringBlade>
* `IJPay` 支付SDK让支付触手可及：<https://gitee.com/javen205/IJPay>
* 加入【如梦技术】Spring QQ群：479710041，了解更多。

## 微信公众号

![如梦技术](/img/dreamlu-weixin.jpg)

精彩内容每日推荐！！！
