Java通过FreeMarker实现代码生成(以SSM的Mapper和实体类为例)
代码生成的模板使用的时ftl编写的,需要先了解下基本语法
官方文档:http://freemarker.foofun.cn/ref_directive_ftl.html
一丶实现思路
1.首先创建相关字段,赋予信息(数据库连接、存放目录、作者等)
2.获取数据库连接,创建生成代码存目录
3.获取数据库元信息,当对应的信息存放到所写的Column类中
4.根据模板生成对应的代码
二、代码
所需要的依赖pom.xml
这里我是放到了SpringBoot项目中,SpringBoot的并没有删,主要是用到freemarker和MySQL的依赖包
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.freemarker/freemarker -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.23</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.36</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
实体类:
在这里是用于
public class Column {
/** 数据库字段名称 **/
private String columnName;
/** 数据库字段类型 **/
private String columnType;
/** 数据库字段首字母小写且去掉下划线字符串 **/
private String changeColumnName;
/** 数据库字段注释 **/
private String columnComment;
public String getColumnComment() {
return columnComment;
}
public void setColumnComment(String columnComment) {
this.columnComment = columnComment;
}
public String getColumnName() {
return columnName;
}
public void setColumnName(String columnName) {
this.columnName = columnName;
}
public String getColumnType() {
return columnType;
}
public void setColumnType(String columnType) {
this.columnType = columnType;
}
public String getChangeColumnName() {
return changeColumnName;
}
public void setChangeColumnName(String changeColumnName) {
this.changeColumnName = changeColumnName;
}
}
FreeMarker工具类:
package com.code.generate;
import freemarker.cache.ClassTemplateLoader;
import freemarker.cache.NullCacheStorage;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateExceptionHandler;
import java.io.IOException;
public class FreeMarkerTemplateUtils {
private FreeMarkerTemplateUtils(){}
private static final Configuration CONFIGURATION = new Configuration(Configuration.VERSION_2_3_22);
static{
//这里比较重要,用来指定加载模板所在的路径
CONFIGURATION.setTemplateLoader(new ClassTemplateLoader(FreeMarkerTemplateUtils.class, "/templates"));
CONFIGURATION.setDefaultEncoding("UTF-8");
CONFIGURATION.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
CONFIGURATION.setCacheStorage(NullCacheStorage.INSTANCE);
}
public static Template getTemplate(String templateName) throws IOException {
try {
return CONFIGURATION.getTemplate(templateName);
} catch (IOException e) {
throw e;
}
}
public static void clearCache() {
CONFIGURATION.clearTemplateCache();
}
}
代码生成的工具类:
因为是通过main方法执行,好多个东西都是自己定义的,包名等各种信息均是个人习惯,可根据个人需求进行修改
package com.code.generate;
import freemarker.template.Template;
import org.apache.commons.lang3.StringUtils;
import java.io.*;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.text.SimpleDateFormat;
import java.util.*;
public class CodeGenerateUtils {
//类上的注释(作者 日期)
private final String AUTHOR = "ST";
private static String CURRENT_DATE="";
private final String tableName = "user";
//包名
private final String packageName = "com.st.task";
//数据库信息
private final String URL = "jdbc:mysql://localhost:3306/stapp";
private final String USER = "root";
private final String PASSWORD = "root";
private final String DRIVER = "com.mysql.jdbc.Driver";
//生成的位置
private final static String diskPath = "D:\\code";
private final String changeTableName = replaceUnderLineAndUpperCase(tableName);
//文件生成目录
private final static String[] dirs={
diskPath+"\\entity",diskPath+"\\controller",diskPath+"\\service",
diskPath+"\\service\\impl",diskPath+"\\mapper",diskPath+"\\mappers"
};
public static void main(String[] args) throws Exception{
CodeGenerateUtils codeGenerateUtils = new CodeGenerateUtils();
setDF();
createDirs();
codeGenerateUtils.generate();
}
/**
* 设置日期
* @throws Exception
*/
public static void setDF() throws Exception{
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
CURRENT_DATE=sdf.format(date);
}
/**
* 获取数据库连接
* @return
* @throws Exception
*/
public Connection getConnection() throws Exception{
Class.forName(DRIVER);
Connection connection= DriverManager.getConnection(URL, USER, PASSWORD);
return connection;
}
/**
* 用于创建存放的java类的路径
* @throws Exception
*/
public static void createDirs() throws Exception{
for(int i= 0; i < dirs.length; i++) {
File file = new File(dirs[i]);
if (!file.exists()){
file.mkdir();
}
}
}
/**
* 生成代码
* @throws Exception
*/
public void generate() throws Exception{
try {
List<String> tables = new ArrayList();//存储表名
//获取数据库元信息
Connection connection = getConnection();
DatabaseMetaData databaseMetaData = connection.getMetaData();
//存储表名
String[] types = {"TABLE"};
ResultSet tabs = databaseMetaData.getTables(null, null, null, types);
while(tabs.next()){
//只要表名这一列
tables.add(tabs.getString("TABLE_NAME"));
}
for(int i=0;i<tables.size();i++){//生成每个表的文件
String table = tables.get(i);
ResultSet resultSet = databaseMetaData.getColumns(null,"%",table,"%");
//生成Model文件
generateModelFile(resultSet,table);
//生成Mapper文件
generateMapperFile(resultSet,table);
}
} catch (Exception e) {
throw new RuntimeException(e);
}finally{
}
}
/**
* 生成实体类
* @param resultSet
* @throws Exception
*/
private void generateModelFile(ResultSet resultSet,String tableName) throws Exception{
final String suffix = ".java";//后缀名
final String package_name = "entity";//包名
String name = replaceUnderLineAndUpperCase(tableName);
final String path = diskPath +"\\"+package_name+"\\"+ name+ suffix;
final String templateName = "Model.ftl";
File mapperFile = new File(path);
List<Column> columnClassList = new ArrayList<>();
Column columnClass = null;
while(resultSet.next()){
//id字段略过
//if(resultSet.getString("COLUMN_NAME").equals("id")) continue;
columnClass = new Column();
//获取字段名称
columnClass.setColumnName(resultSet.getString("COLUMN_NAME"));
//获取字段类型
columnClass.setColumnType(resultSet.getString("TYPE_NAME"));
//转换字段名称,如 sys_name 变成 SysName
columnClass.setChangeColumnName(replaceUnderLineAndUpperCase(resultSet.getString("COLUMN_NAME")));
//字段在数据库的注释
columnClass.setColumnComment(resultSet.getString("REMARKS"));
columnClassList.add(columnClass);
}
Map<String,Object> dataMap = new HashMap<>();
dataMap.put("model_column",columnClassList);
dataMap.put("table_name",name);
generateFileByTemplate(templateName,mapperFile,dataMap);
}
/**
* 生成mapper文件
* @param resultSet
* @param tableName
* @throws Exception
*/
private void generateMapperFile(ResultSet resultSet,String tableName) throws Exception{
final String suffix = "Mapper.xml";
final String package_name = "mappers";//包名
String name = replaceUnderLineAndUpperCase(tableName);
final String path = diskPath +"\\"+package_name+"\\"+ name+ suffix;
final String templateName = "Mapper.ftl";
File mapperFile = new File(path);
List<Column> columnClassList = new ArrayList<>();
Column columnClass = null;
while(resultSet.next()){
//id字段略过
//if(resultSet.getString("COLUMN_NAME").equals("id")) continue;
columnClass = new Column();
//获取字段名称
columnClass.setColumnName(resultSet.getString("COLUMN_NAME"));
//获取字段类型
columnClass.setColumnType(resultSet.getString("TYPE_NAME"));
columnClassList.add(columnClass);
}
Map<String,Object> dataMap = new HashMap<>();
System.out.println(name);
dataMap.put("model_column",columnClassList);
dataMap.put("table_name",name);
dataMap.put("jing","#");
dataMap.put("left","{");
dataMap.put("right","}");
generateFileByTemplate(templateName,mapperFile,dataMap);
}
private void generateFileByTemplate(final String templateName,File file,Map<String,Object> dataMap) throws Exception{
Template template = FreeMarkerTemplateUtils.getTemplate(templateName);
FileOutputStream fos = new FileOutputStream(file);
dataMap.put("table_name_small",tableName);
dataMap.put("author",AUTHOR);
dataMap.put("date",CURRENT_DATE);
dataMap.put("package_name",packageName);
Writer out = new BufferedWriter(new OutputStreamWriter(fos, "utf-8"),10240);
template.process(dataMap,out);
}
/**
* 带下划线的名字转换
* @param str
* @return
*/
public static String replaceUnderLineAndUpperCase(String str){
StringBuffer sb = new StringBuffer();
sb.append(str);
if(str.contains("_")){
int count = sb.indexOf("_");
while(count!=0){
int num = sb.indexOf("_",count);
count = num + 1;
if(num != -1){
char ss = sb.charAt(count);
char ia = (char) (ss - 32);
sb.replace(count , count + 1,ia + "");
}
}
String result = sb.toString().replaceAll("_","");
return StringUtils.capitalize(result);
}else {
String first = str.substring(0, 1);
String after = str.substring(1, str.length());
String result = first.toUpperCase() + after;
return result;
}
}
在这里提供一下Mapper和实体类的ftl模板,可根据需求进行修改,这里通过${} 获取的值都是在java中往Map中存储的值,可根据需求自行添加
Mapper.ftl
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="${package_name}.mapper.${table_name}Mapper">
<!--表名 -->
<sql id="tableName">
${table_name}
</sql>
<#if model_column ? exists>
<!-- 字段 -->
<sql id="Field">
<#list model_column as model>
${model.columnName},
</#list>
</sql>
<!-- 字段值 -->
<sql id="FieldValue">
<#list model_column as model>
${jing}${left}${model.columnName}${right},
</#list>
</sql>
</#if>
<!-- 新增-->
<insert id="save" parameterType="${table_name}" keyProperty="id" useGeneratedKeys="true">
insert into
<include refid="tableName"></include>
(
<include refid="Field"></include>
) values (
<include refid="FieldValue"></include>
)
</insert>
<!-- 删除-->
<delete id="deleteById" parameterType="${table_name}">
delete from
<include refid="tableName"></include>
where
id in ( ${jing}${left}id${right} )
</delete>
<!-- 修改 -->
<update id="updateById" parameterType="${table_name}">
update
<include refid="tableName"></include>
set
id = ${jing}${left}id${right}
<#if model_column ? exists>
<#list model_column as model>
<if test=" ${model.columnName} != null and ${model.columnName} != ''">,name = ${jing}${left}${model.columnName}${right}</if>
</#list>
</#if>
where
id = ${jing}${left}id${right}
</update>
<!-- 列表(全部) -->
<!--resultType:表示把查询的结果集中的每一条记录封装成什么类型的对象 -->
<select id="listAll" parameterType="${table_name}" resultType="${table_name}">
select
t.*
from
<include refid="tableName"></include>
t
where 1 =1
<#if model_column ? exists>
<#list model_column as model>
<#if model.columnType = 'VARCHAR' >
<if test=" ${model.columnName} != null and ${model.columnName} != ''">and t.${model.columnName} like "%"${jing}${left}${model.columnName}${right}"%"</if>
<#else>
<if test=" ${model.columnName} != null and ${model.columnName} != ''">and t.${model.columnName} = ${jing}${left}${model.columnName}${right}</if>
</#if>
</#list>
</#if>
</select>
</mapper>
实体类ftl
这里要注意的是,需要根据数据库的字段类型判断实体类的成员变量的类型,需要判断所有的可能(也许有更简单的方法)
package ${package_name}.entity;
import java.io.Serializable;
import java.util.Date;
/**
* @author ${author}
* @date ${date}
*/
public class ${table_name} implements Serializable {
<#if model_column ? exists>
<#list model_column as model>
//${model.columnComment!}
<#if model.columnType = 'VARCHAR' >
private String ${model.changeColumnName? uncap_first};
</#if>
<#if model.columnType = 'BIGINT' >
private Long ${model.changeColumnName?uncap_first};
</#if>
<#if model.columnType = 'INT' >
private Integer ${model.changeColumnName?uncap_first};
</#if>
<#if model.columnType = 'DATETIME' >
private Date ${model.changeColumnName?uncap_first};
</#if>
</#list>
</#if>
<#if model_column?exists>
<#list model_column as model>
<#if model.columnType = 'VARCHAR' >
public String get${model.changeColumnName}() {
return this.${model.changeColumnName?uncap_first};
}
public void set${model.changeColumnName}(String ${model.changeColumnName? uncap_first}) {
this.${model.changeColumnName?uncap_first} = ${model.changeColumnName?uncap_first};
}
</#if>
<#if model.columnType = 'BIGINT' >
public Long get${model.changeColumnName}() {
return this.${model.changeColumnName?uncap_first};
}
public void set${model.changeColumnName}(Long ${model.changeColumnName? uncap_first}) {
this.${model.changeColumnName?uncap_first} = ${model.changeColumnName?uncap_first};
}
</#if>
<#if model.columnType = 'INT' >
public int get${model.changeColumnName}() {
return this.${model.changeColumnName?uncap_first};
}
public void set${model.changeColumnName}(int ${model.changeColumnName? uncap_first}) {
this.${model.changeColumnName?uncap_first} = ${model.changeColumnName?uncap_first};
}
</#if>
<#if model.columnType = 'DATETIME' >
public Date get${model.changeColumnName}() {
return this.${model.changeColumnName?uncap_first};
}
public void set${model.changeColumnName}(Date ${model.changeColumnName?uncap_first}) {
this.${model.changeColumnName?uncap_first} = ${model.changeColumnName?uncap_first};
}
</#if>
</#list>
</#if>
}
到此即可实现代码生成,生成效果如下