核心依赖
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-generator -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.3.1</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.15</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi-scratchpad -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>5.2.3</version>
</dependency>
核心代码
EnhanceImgUtil.java
@Slf4j
public class EnhanceImgUtil extends ImgUtil {
/**
* 生成纯色图片
*
* @param width 图片宽
* @param height 图片高
* @param color 颜色 空则Color.BLACK
* @return
*/
public static BufferedImage createPureColorImage(int width, int height, Color color) {
// width 生成图宽度
// height 生成图高度
// 创建一个width xheight ,RGB高彩图,类型可自定
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// 取得图形
Graphics g = img.getGraphics();
// 设置颜色
g.setColor(ObjectUtil.defaultIfNull(color, Color.white));
// 填充
g.fillRect(0, 0, img.getWidth(), img.getHeight());
// 在d盘创建个文件
return img;
}
/**
* 生成文本图片
*
* @param content 文本
* @param width 图片宽
* @param height 图片高
* @return
*/
public static BufferedImage createContentImage(String content, int width, int height) {
return createContentImage(content, width, height, Color.WHITE, Color.BLACK);
}
/**
* 生成基于content内容的图片
*
* @param content 图片内容
* @param width 图片宽
* @param height 图片高
* @param bgColor 图片背景色 空则白色
* @param fontColor 文字颜色色 空则黑色
* @return
*/
public static BufferedImage createContentImage(String content, int width, int height, Color bgColor, Color fontColor) {
return createContentImage(content, width, height, ObjectUtil.defaultIfNull(bgColor, Color.WHITE), ObjectUtil.defaultIfNull(fontColor, Color.BLACK), false);
}
/**
* 生成基于content内容的图片
*
* @param content 图片内容
* @param width 图片宽
* @param height 图片高
* @param bgColor 图片背景色 空则白色
* @param fontColor 文字颜色色 空则黑色
* @return
*/
public static BufferedImage createContentImage(Font font, String content, int width, int height, Color bgColor, Color fontColor) {
return createContentImage(font, content, width, height, ObjectUtil.defaultIfNull(bgColor, Color.WHITE), ObjectUtil.defaultIfNull(fontColor, Color.BLACK), false);
}
/**
* 生成基于content内容的图片
*
* @param content 图片内容
* @param width 图片宽
* @param height 图片高
* @param backgroundTransparentFlag 背景是否透明
* @return
*/
public static Image createContentImage(String content, int width, int height, boolean backgroundTransparentFlag) {
return createContentImage(content, width, height, Color.WHITE, Color.BLACK, backgroundTransparentFlag);
}
/**
* 生成基于content内容的图片
*
* @param content 图片内容
* @param width 图片宽
* @param height 图片高
* @param bgColor 图片背景色 空则白色
* @param fontColor 文字颜色色 空则黑色
* @param backgroundTransparentFlag 背景是否需要透明 true透明且bgColor不生效
* @return
*/
public static BufferedImage createContentImage(String content, int width, int height, Color bgColor, Color fontColor, boolean backgroundTransparentFlag) {
return createContentImage(null, content, width, height, Color.WHITE, Color.BLACK, backgroundTransparentFlag);
}
public static BufferedImage createContentImage(Font font, String content, int width, int height, Color bgColor, Color fontColor, boolean backgroundTransparentFlag) {
return createContentImage(font, content, width, height, bgColor, fontColor, backgroundTransparentFlag,true);
}
/**
* 生成基于content内容的图片
*
* @param font 图片内容字体样式
* @param content 图片内容
* @param width 图片宽
* @param height 图片高
* @param bgColor 图片背景色 空则白色
* @param fontColor 文字颜色色 空则黑色
* @param backgroundTransparentFlag 背景是否需要透明 true透明且bgColor不生效
* @param fontCenterFlag 文字是否需要水平居中 true水平居中 false左对齐
* @return
*/
public static BufferedImage createContentImage(Font font, String content, int width, int height, Color bgColor, Color fontColor, boolean backgroundTransparentFlag, boolean fontCenterFlag) {
// 图片设置:宽、高、背景透明
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);// 获取Image对象
Graphics2D g2d = image.createGraphics();
// 填充白色背景图
g2d.setColor(ObjectUtil.defaultIfNull(bgColor, Color.WHITE));
g2d.fillRect(0, 0, width, height);
// 背景透明
if (backgroundTransparentFlag) {
image = g2d.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
}
// g2d.dispose();
g2d = image.createGraphics();
// 设置字体颜色和透明度
g2d.setColor(ObjectUtil.defaultIfNull(fontColor, Color.BLACK));
// g2d.setStroke(new BasicStroke(1));
// 设置字体
font = ObjectUtil.defaultIfNull(font, new Font("微软雅黑", Font.BOLD, 25));
g2d.setFont(font);
FontRenderContext context = g2d.getFontRenderContext();
Rectangle2D bounds = font.getStringBounds(content, context);
double x = 16.0;
// 文字水平居中显示
if (fontCenterFlag) {
x = (width - bounds.getWidth()) / 2;
}
// 文字垂直居中显示
double y = (height - bounds.getHeight()) / 2;
double ascent = -bounds.getY();
double baseY = y + ascent;
g2d.drawString(content, Convert.toInt(x), Convert.toInt(baseY));
// 设置透明度
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
// 释放对象
g2d.dispose();
return image;
}
/**
* 生成多行文字的图片
*
* @param font 图片内容字体样式
* @param contents 图片内容-每个元素代表一行内容
* @param width 图片宽
* @param height 图片高
* @param bgColor 图片背景色 空则白色
* @param fontColor 文字颜色色 空则黑色
* @param backgroundTransparentFlag 背景是否需要透明 true透明且bgColor不生效
* @return
*/
public static BufferedImage createContentsImage(Font font, List<String> contents, int width, int height, Color bgColor, Color fontColor, boolean backgroundTransparentFlag, boolean fontCenterFlag) {
BufferedImage[] bufferedImages = CollUtil.emptyIfNull(contents)
.stream()
.map(content -> createContentImage(font, content, width, height, bgColor, fontColor, backgroundTransparentFlag,fontCenterFlag))
.toArray(BufferedImage[]::new);
return mergeImage(bufferedImages);
}
public static BufferedImage createContentsImage(Font font, List<String> contents, int width, int height, Color bgColor, Color fontColor, boolean fontCenterFlag) {
return createContentsImage(font, contents, width, height, bgColor, fontColor, false,fontCenterFlag);
}
public static BufferedImage createContentsImage(Font font, List<String> contents, int width, int height, Color bgColor, Color fontColor) {
return createContentsImage(font, contents, width, height, bgColor, fontColor, true);
}
public static BufferedImage createContentsImage(Font font, List<String> contents, int width, int height, Color bgColor, boolean fontCenterFlag) {
return createContentsImage(font, contents, width, height, bgColor, Color.BLACK,fontCenterFlag);
}
public static BufferedImage createContentsImage(Font font, List<String> contents, int width, int height, Color bgColor) {
return createContentsImage(font, contents, width, height, bgColor, Color.BLACK);
}
/**
* 垂直合并图片
* 代码复制粘贴自{https://www.cnblogs.com/chbyiming-bky/articles/8940105.html}
*
* @param imgs 图片
*/
public static BufferedImage mergeImage(BufferedImage... imgs) {
if (imgs == null) {
return null;
}
imgs = ArrayUtil.removeNull(imgs);
if (ArrayUtil.isNotEmpty(imgs)) {
return mergeImage(false, imgs);
}
return null;
}
/**
* 合并任数量的图片成一张图片
* 代码复制粘贴自{https://www.cnblogs.com/chbyiming-bky/articles/8940105.html}
*
* @param isHorizontal true代表水平合并,fasle代表垂直合并
* @param imgs 待合并的图片数组
*/
public static BufferedImage mergeImage(boolean isHorizontal, BufferedImage... imgs) {
List<BufferedImage> bufferedImageList = Arrays.asList(imgs).stream()
.filter(ObjectUtil::isNotNull)
.collect(Collectors.toList());
imgs = Convert.convert(BufferedImage[].class, bufferedImageList, new BufferedImage[0]);
// 生成新图片
BufferedImage destImage = null;
// 计算新图片的长和高
int allw = 0, allh = 0, allwMax = 0, allhMax = 0;
// 获取总长、总宽、最长、最宽
for (int i = 0; i < imgs.length; i++) {
BufferedImage img = imgs[i];
if (img == null) {
continue;
}
allw += img.getWidth();
allh += img.getHeight();
if (img.getWidth() > allwMax) {
allwMax = img.getWidth();
}
if (img.getHeight() > allhMax) {
allhMax = img.getHeight();
}
}
// 创建新图片
if (isHorizontal) {
destImage = new BufferedImage(allw, allhMax, BufferedImage.TYPE_INT_RGB);
} else {
destImage = new BufferedImage(allwMax, allh, BufferedImage.TYPE_INT_RGB);
}
// 合并所有子图片到新图片
int wx = 0, wy = 0;
for (int i = 0; i < imgs.length; i++) {
BufferedImage img = imgs[i];
int w1 = img.getWidth();
int h1 = img.getHeight();
// 从图片中读取RGB
int[] ImageArrayOne = new int[w1 * h1];
ImageArrayOne = img.getRGB(0, 0, w1, h1, ImageArrayOne, 0, w1); // 逐行扫描图像中各个像素的RGB到数组中
if (isHorizontal) { // 水平方向合并
destImage.setRGB(wx, 0, w1, h1, ImageArrayOne, 0, w1); // 设置上半部分或左半部分的RGB
} else { // 垂直方向合并
destImage.setRGB(0, wy, w1, h1, ImageArrayOne, 0, w1); // 设置上半部分或左半部分的RGB
}
wx += w1;
wy += h1;
}
return destImage;
}
}
DbQO.java
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "数据库查询类")
public class DbQO {
private final String urlTemplate = "jdbc:{dbType}://{ip}:{port}/{databaseName}";
@ApiModelProperty(value = "数据库类型(dbType)")
String dbType;
@ApiModelProperty(value = "数据库IP地址(ip)")
String ip;
@ApiModelProperty(value = "数据库端口号(port)")
String port;
@ApiModelProperty(value = "数据库库名(databaseName)")
String databaseName;
@ApiModelProperty(value = "其他参数(otherParams) == 比如时区、编码等配置")
Map<String, String> otherParams;
@ApiModelProperty(value = "数据库url(url) == 参考jdbc:{dbType}://{ip}:{port}/{databaseName}")
String url;
@ApiModelProperty(value = "数据库账号(userName)",required = true)
@NotBlank(message = "数据库账号缺失")
String userName;
@ApiModelProperty(value = "数据库密码(password)",required = true)
@NotBlank(message = "数据库密码缺失")
String password;
@ApiModelProperty(value = "需匹配的表名,多个则逗号分隔(includeTableName) = 某些接口支持正则匹配")
String includeTableName;
@ApiModelProperty(value = "被排除的表名,多个则逗号分隔(excludeTableName) = 某些接口支持正则匹配")
String excludeTableName;
@ApiModelProperty(value = "待执行的SQL语句(sql)")
String sql;
@ApiModelProperty(value = "解析-SQL执行计划(explainSqlFlag)")
Boolean explainSqlFlag;
@ApiModelProperty(value = "自动在查询语句上添加分页参数(autoAddPageSQLParams)")
boolean autoAddPageSQLParamsFlag = true;
@ApiModelProperty(value = "页码(pageNo) = 从1开始")
Integer pageNo;
@ApiModelProperty(value = "页记录数(pageSize)")
Integer pageSize;
/**
* 获取数据库链接
* 如果{@link DbQO#url}有值则取这个,否则通过{@link DbQO#dbType, DbQO#ip}等进行构建
*
* @return
*/
public String blankToBuildUrl() {
String result = url;
if (StrUtil.isBlank(result)) {
result = StrUtil.format(urlTemplate, BeanUtil.beanToMap(this), true);
if (CollUtil.isNotEmpty(otherParams)) {
String otherParamsStr = URLUtil.buildQuery(otherParams, CharsetUtil.CHARSET_UTF_8);
result = StrUtil.format("{}?{}", result, otherParamsStr);
}
}
return result;
}
}
private static DataSource getDataSource(DbQO dbQO) {
return new DriverManagerDataSource(dbQO.blankToBuildUrl(), dbQO.getUserName(), dbQO.getPassword());
}
/**
* 获取数据库的某表的元信息
*
* @param dbQO 数据库信息
* @return
*/
public static List<TableInfo> getTablesMeta(DbQO dbQO) {
// 1. 数据库连接
DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder(getDataSource(dbQO))
.build();
// 2. 查找策略
String inclueTableName = dbQO.getIncludeTableName();
String excludeTableName = dbQO.getExcludeTableName();
StrategyConfig.Builder strategyConfigBuilder = new StrategyConfig.Builder();
if (StrUtil.isNotBlank(inclueTableName)) {
strategyConfigBuilder.addInclude(StrUtil.split(inclueTableName, StrUtil.COMMA));
}
if (StrUtil.isNotBlank(excludeTableName)) {
strategyConfigBuilder.addExclude(StrUtil.split(excludeTableName, StrUtil.COMMA));
}
ConfigBuilder configBuilder = new ConfigBuilder(null, dataSourceConfig, strategyConfigBuilder.build(), null, null, null);
DefaultQuery defaultQuery = new DefaultQuery(configBuilder);
return defaultQuery.queryTables();
}
private static BufferedImage createTableStructureImage(TableInfo tableInfo) {
String tableComment = tableInfo.getComment();
String tableName = tableInfo.getName();
String tableDesc = StrUtil.isNotBlank(tableComment) ? StrUtil.format("{}({})", tableName, tableComment) : tableName;
List<TableField> tableFields = tableInfo.getFields();
Integer maxLengthTableFieldNameDesc = tableFields.stream()
.map(tableField -> {
String tableFieldName = tableField.getName();
String tableFieldComment = tableField.getComment();
String tableFieldNameDesc = StrUtil.isNotBlank(tableFieldComment) ? StrUtil.format("{}({})", tableFieldName, tableFieldComment) : tableFieldName;
return tableFieldNameDesc;
})
.map(StrUtil::length)
.map(length -> length + 4)
.max(Integer::compareTo)
.orElse(0);
Integer maxLengthTableFieldTypeDesc = tableFields.stream()
.map(tableField -> {
String jdbcType = Convert.toStr(tableField.getMetaInfo().getJdbcType());
int typeLength = tableField.getMetaInfo().getLength();
String tableFieldTypeDesc = typeLength > 0 ? StrUtil.format("{}({})", jdbcType, typeLength) : jdbcType;
return tableFieldTypeDesc;
})
.map(StrUtil::length)
.max(Integer::compareTo)
.orElse(0);
List<String> tableFieldDescList = tableFields.stream()
.map(tableField -> {
String tableFieldName = tableField.getName();
String tableFieldComment = tableField.getComment();
String tableFieldNameDesc = StrUtil.isNotBlank(tableFieldComment) ? StrUtil.format("{}({})", tableFieldName, tableFieldComment) : tableFieldName;
tableFieldNameDesc = StrUtil.padAfter(tableFieldNameDesc, maxLengthTableFieldNameDesc, StrUtil.SPACE);
// EnhanceImgUtil.createContentImage(font, "文章", imageWidth, rowHeight, new Color(0xD9EAFD), Color.BLACK);
String jdbcType = Convert.toStr(tableField.getMetaInfo().getJdbcType());
int typeLength = tableField.getMetaInfo().getLength();
String tableFieldTypeDesc = typeLength > 0 ? StrUtil.format("{}({})", jdbcType, typeLength) : jdbcType;
return StrUtil.format("{} {}", tableFieldNameDesc, tableFieldTypeDesc);
})
.collect(Collectors.toList());
Integer fontSize = 16;
Integer maxRowContentLength = ArrayUtil.max(maxLengthTableFieldNameDesc + maxLengthTableFieldTypeDesc, StrUtil.length(tableDesc));
Integer maxRowContentWidth = maxRowContentLength * fontSize;
Integer rowHeight = fontSize + 4;
Integer tableFieldNameDescWidth = maxLengthTableFieldNameDesc * fontSize;
Integer tableFieldTypeDescWidth = maxRowContentWidth - tableFieldNameDescWidth;
Font font = new Font(Font.MONOSPACED, Font.BOLD, fontSize);
BufferedImage[] tableFieldsImages = tableFields.stream()
.map(tableField -> {
String tableFieldName = tableField.getName();
String tableFieldComment = tableField.getComment();
String tableFieldNameDesc = StrUtil.isNotBlank(tableFieldComment) ? StrUtil.format("{}({})", tableFieldName, tableFieldComment) : tableFieldName;
tableFieldNameDesc = StrUtil.padAfter(tableFieldNameDesc, maxLengthTableFieldNameDesc, StrUtil.SPACE);
BufferedImage tableFieldNameDescImage = EnhanceImgUtil.createContentImage(font, tableFieldNameDesc, tableFieldNameDescWidth, rowHeight, new Color(0xD9EAFD), Color.BLACK, false, false);
String jdbcType = Convert.toStr(tableField.getMetaInfo().getJdbcType());
int typeLength = tableField.getMetaInfo().getLength();
String tableFieldTypeDesc = typeLength > 0 ? StrUtil.format("{}({})", jdbcType, typeLength) : jdbcType;
BufferedImage tableFieldTypeDescImage = EnhanceImgUtil.createContentImage(font, tableFieldTypeDesc, tableFieldTypeDescWidth, rowHeight, new Color(0xD9EAFD), Color.BLACK, false, false);
return EnhanceImgUtil.mergeImage(true, tableFieldNameDescImage, tableFieldTypeDescImage);
})
.toArray(BufferedImage[]::new);
Integer borderHeight = 2;
BufferedImage tableNameImage = EnhanceImgUtil.createContentImage(font, tableDesc, maxRowContentWidth, rowHeight, new Color(0xD9EAFD), Color.BLACK);
BufferedImage rowBorderbufferedImage = EnhanceImgUtil.createPureColorImage(maxRowContentLength * fontSize, borderHeight, new Color(0x0080C0));
BufferedImage tableFieldsImage = EnhanceImgUtil.mergeImage(tableFieldsImages);
// 上中下边框
BufferedImage result = EnhanceImgUtil.mergeImage(rowBorderbufferedImage, tableNameImage, rowBorderbufferedImage, tableFieldsImage, rowBorderbufferedImage);
// 左右边框框
BufferedImage columnBorderbufferedImage = EnhanceImgUtil.createPureColorImage(borderHeight, result.getHeight(), new Color(0x0080C0));
result = EnhanceImgUtil.mergeImage(true, columnBorderbufferedImage, result, columnBorderbufferedImage);
return result;
}
测试代码
@Test
@SneakyThrows
public void test58() {
DbQO dbQO = new DbQO();
dbQO.setUrl("jdbc:mysql://localhost:3306/lrc-utils-web?useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai\n");
dbQO.setUserName("root");
dbQO.setPassword("root");
List<TableInfo> tablesMetas = getTablesMeta(dbQO);
FileOutputStream outputStream = new FileOutputStream("C:\\Users\\Administrator\\Desktop\\数据库文档.docx");
Word07Writer writer = new Word07Writer();
// 标题
Font docTitleFont = new Font(Font.SANS_SERIF, Font.BOLD, 25);
writer.addText(ParagraphAlignment.CENTER, docTitleFont, "数据库设计文档");
writer.addText(ParagraphAlignment.CENTER, docTitleFont, StrUtil.EMPTY);
writer.addText(ParagraphAlignment.CENTER, docTitleFont, StrUtil.EMPTY);
// 表索引
AtomicInteger tableIndexAtomicInteger = new AtomicInteger();
tablesMetas.forEach(tableInfo -> {
String comment = tableInfo.getComment();
comment = StrUtil.isBlank(comment) ? StrUtil.EMPTY : StrUtil.format("({})", comment);
// 1. 标题
Font tableNameTitleFont = new Font(Font.SANS_SERIF, Font.BOLD, 20);
writer.addText(tableNameTitleFont, StrUtil.format("{}、{}{}", tableIndexAtomicInteger.incrementAndGet(), tableInfo.getName(), comment));
writer.addText(FontUtil.createFont(), StrUtil.EMPTY);
// 2. pw表结构图片
Integer docWidth = 450;
BufferedImage pwTableStructureImageimage = createTableStructureImage(tableInfo);
// 缩放图片在Doc中的比例
int docPwTableStructureImageimageWidth = NumberUtil.min(pwTableStructureImageimage.getWidth(), docWidth);
double docLengthProportion = NumberUtil.div(docPwTableStructureImageimageWidth, pwTableStructureImageimage.getWidth());
int docPwTableStructureImageimageHeight = Convert.toInt(NumberUtil.mul(pwTableStructureImageimage.getHeight(), docLengthProportion));
ByteArrayOutputStream imageOutputStream = new ByteArrayOutputStream();
ImgUtil.write(pwTableStructureImageimage, "png", imageOutputStream);
ByteArrayInputStream byteArrayInputStream = IoUtil.toStream(imageOutputStream);
writer.addPicture(byteArrayInputStream, PicType.JPEG, tableInfo.getName(), docPwTableStructureImageimageWidth, docPwTableStructureImageimageHeight);
writer.addText(FontUtil.createFont(), StrUtil.EMPTY);
// 3. 表结构表格
List<TableField> tableFields = tableInfo.getFields();
// 表的字段索引
AtomicInteger tableFieldInexAtomicInteger = new AtomicInteger(0);
List<Map<String, Object>> workTableFields = tableFields.stream()
.map(tableField -> {
Map<String, Object> wrokTableRow = new LinkedHashtable();
wrokTableRow.put("序号", tableFieldInexAtomicInteger.incrementAndGet());
wrokTableRow.put("字段名", tableField.getName());
wrokTableRow.put("数据类型", tableField.getMetaInfo().getJdbcType());
wrokTableRow.put("数据长度", tableField.getMetaInfo().getLength());
wrokTableRow.put("主键", tableField.isKeyFlag() ? "是" : "否");
wrokTableRow.put("非空", tableField.getMetaInfo().isNullable() ? "是" : "否");
wrokTableRow.put("默认值", tableField.getMetaInfo().getDefaultValue());
wrokTableRow.put("描述", tableField.getComment());
return wrokTableRow;
})
.collect(Collectors.toList());
;
writer.addTable(workTableFields);
writer.addText(FontUtil.createFont(), StrUtil.EMPTY);
writer.addText(FontUtil.createFont(), StrUtil.EMPTY);
});
writer.flush(outputStream, false);
}