java自动生成代码
为了简化日常编写代码,根据公司的自身情况,编写了一个自动生成代码的简易工具。
主要需要熟悉freemarker模板引擎,JDBC等相关知识;
<dependency>
<groupId>freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.8</version>
</dependency>
<!-- 这个是生成驼峰命名的工具 -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>21.0</version>
</dependency>
- 创建实体类ColumnClass
ColumnClass 用来封装 数据库表元数据的信息,如字段名称,字段类型,字段注释等等。
public class ColumnClass {
/** 数据库字段名称 **/
private String columnName;
/** 数据库字段类型 **/
private String columnType;
/** 数据库字段首字母小写且去掉下划线字符串 **/
private String changeColumnName;
/** 数据库字段注释 **/
private String columnComment;
/**
* 列的长度
*/
private int columnSize;
/**
* 是否允许为空
*/
private String nullable;
/**
* 主键名
*/
private String pkName;
...get/set方法
}
- 主要代码
package cn.sh.cjvision.CodeGenerator.jdbc;
import cn.sh.cjvision.CodeGenerator.ColumnClass;
import cn.sh.cjvision.CodeGenerator.FreeMarkerTemplateUtils;
import com.google.common.base.CaseFormat;
import org.apache.commons.jexl2.UnifiedJEXL;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
//import com.evada.inno.pm.code.generate.model.ColumnClass;
import freemarker.template.Template;
import org.apache.commons.lang3.StringUtils;
import java.io.*;
import java.sql.*;
import java.time.LocalDate;
import java.util.*;
import static cn.sh.cjvision.CodeGenerator.FreeMarkerTemplateUtils.createDir;
public class CodeGenerateUtils
{
/**
* 链接地址
*/
@Value("${spring.datasource.url}")
private String URL;
/**
* 用户名
*/
@Value("@{spring.datasource.username}")
private String USER;
/**
* 密码
*/
@Value("@{spring.datasource.password}")
private String PASSWORD;
/**
* 连接驱动
*/
@Value("@{spring.datasource.driver-class-name}")
private String DRIVER;
/**
* 作者
*/
private final String AUTHOR = "wowoToffon";
/**
* 时间
*/
private final String CURRENT_DATE = LocalDate.now().toString();
/**
* 表名
*/
private final String tableName = "BOOK";
/**
* 包名
*/
private final String packageName = "cn.sh.cjvision";
/**
* 表中文注释
*/
private final String tableAnnotation = "书";
/**
* 生成位置
*/
private final String diskPath = "E:\\MyProject\\Quartz\\Quartz\\app-quartz\\src\\main\\java\\cn\\sh\\cjvision";
/**
* 将类似为 A_TABLE 转换为驼峰命名 aTable
*/
private final String changeTableName = CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, tableName);
/**
* 获取jdbc链接
* @return
* @throws Exception
*/
public Connection getConnection() throws Exception
{
Class.forName(DRIVER);
Properties props =new Properties();
props.put("user", USER);
props.put("password", PASSWORD);
// 允许获取注解(这个是重点,在oracle中不加这个,获得注释就是 null)
props.put("remarksReporting","true");
Connection connection = DriverManager.getConnection(URL, props);
return connection;
}
public static void main(String[] args) throws Exception
{
CodeGenerateUtils codeGenerateUtils = new CodeGenerateUtils();
codeGenerateUtils.generate();
}
public void generate() throws Exception
{
Connection connection = getConnection();
//生成Model文件
generateModelFile(connection);
// 生成Controller文件
generateControllerFile();
// 生成service文件
generateServiceFile();
}
public List<ColumnClass> setColumnClass(Connection connection) throws SQLException
{
DatabaseMetaData databaseMetaData = connection.getMetaData();
// 获取表中列值信息
ResultSet resultSet = databaseMetaData.getColumns(null, "%", tableName, "%");
// 获取表主键信息
ResultSet primaryKeys = databaseMetaData.getPrimaryKeys(null, null, "BOOK");
List<ColumnClass> columnClassList = new LinkedList<>();
ColumnClass columnClass = null;
while (resultSet.next())
{
columnClass = new ColumnClass();
//获取字段名称
columnClass.setColumnName(resultSet.getString("COLUMN_NAME"));
//获取字段类型
columnClass.setColumnType(resultSet.getString("TYPE_NAME").split("\\(")[0]);
//转换字段名称,如 sys_name 变成 SysName
columnClass.setChangeColumnName(CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL,(resultSet.getString("COLUMN_NAME"))));
//字段在数据库的注释
columnClass.setColumnComment(resultSet.getString("REMARKS"));
// 字段长度
columnClass.setColumnSize(resultSet.getInt("COLUMN_SIZE"));
// 是否为必填
columnClass.setNullable(resultSet.getString("NULLABLE"));
columnClassList.add(columnClass);
}
while (primaryKeys.next())
{
int i = 0;
columnClass = columnClassList.get(i);
columnClass.setPkName(primaryKeys.getString("PK_NAME"));
i++;
}
return columnClassList;
}
private void generateModelFile(Connection connection) throws Exception
{
DatabaseMetaData databaseMetaData = connection.getMetaData();
ResultSet resultSet = databaseMetaData.getColumns(null, "%", tableName, "%");
String newPath = createDir(diskPath, "bean");
final String suffix = ".java";
final String path = newPath + changeTableName + suffix;
final String templateName = "Model.ftl";
File mapperFile = new File(path);
Map<String, Object> dataMap = new HashMap<>();
dataMap.put("model_column", setColumnClass(connection));
generateFileByTemplate(templateName, mapperFile, dataMap);
}
private void generateServiceFile() throws Exception
{
String newPath = createDir(diskPath, "service");
final String suffix = "Service.java";
final String path = newPath + changeTableName + suffix;
final String templateName = "Service.ftl";
File mapperFile = new File(path);
Map<String, Object> dataMap = new HashMap<>();
generateFileByTemplate(templateName, mapperFile, dataMap);
}
private void generateControllerFile() throws Exception
{
String newPath = createDir(diskPath, "controller");
final String suffix = "Controller.java";
final String path = newPath + changeTableName + suffix;
final String templateName = "Controller.ftl";
File mapperFile = new File(path);
Map<String, Object> dataMap = new HashMap<>();
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("table_name", changeTableName);
dataMap.put("author", AUTHOR);
dataMap.put("date", CURRENT_DATE);
dataMap.put("package_name", packageName);
dataMap.put("table_annotation", tableAnnotation);
Writer out = new BufferedWriter(new OutputStreamWriter(fos, "utf-8"), 10240);
template.process(dataMap, out);
}
}
-
FreeMarkerTemplateUtils工具类
FreeMarkerTemplateUtils工具类用来配置模板所在的路径
package cn.sh.cjvision.CodeGenerator; // import com.evada.inno.core.exception.BusinessException; import freemarker.cache.ClassTemplateLoader; import freemarker.cache.NullCacheStorage; import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.TemplateExceptionHandler; import java.io.File; import java.io.IOException; public class FreeMarkerTemplateUtils { private FreeMarkerTemplateUtils(){} private static final Configuration CONFIGURATION = new Configuration(); 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(); } /** * 创建 文件夹 * @param FileAddress * @param fileName * @return */ public static String createDir (String FileAddress, String fileName){ FileAddress = FileAddress + "/" +fileName + "/"; File file = new File(FileAddress); if(file.exists()){ System.out.println("存在该目录"); return FileAddress; } /*if (!FileAddress.endsWith(File.separator)) { FileAddress = FileAddress + File.separator; }*/ if(file.mkdir()){ System.out.println("创建目录" + FileAddress + "创建成功"); }else { System.out.println("创建失败"); } return FileAddress; } }
-
freemarker 模板文件
Freemarker的模板文件,后缀都是以ftl结尾的。
创建Model.ftl
package ${package_name}.bean; import cn.sh.cjvision.framework.bean.bean.BaseBean; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import org.hibernate.annotations.GenericGenerator; import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.NotBlank; import cn.sh.cjvision.framework.utils.constant.AppConstants; import javax.persistence.*; import java.util.Date; /** * 描述:${table_annotation}模型 * @author ${author} * @date ${date} */ @Entity @Table(name = "${table_name_small}") @ApiModel public class ${table_name} extends BaseBean { <#if model_column?exists> <#list model_column as model> /** *${model.columnComment!} */ <#if (model.columnType = 'VARCHAR2' || model.columnType = 'VARCHAR' || model.columnType = 'NUMBER' || model.columnType = 'TEXT')> <#if model.pkName?exists> @Id @GeneratedValue(generator = "${model.columnName}") @GenericGenerator(name = "${model.columnName}", strategy = AppConstants.SEQ_GENERATOR_STR) <#else > @Column(name = "${model.columnName}") <#if (model.nullable = '0')> @NotBlank(message = "${model.columnComment!}不能为空") @ApiModelProperty(value = "${model.columnComment!}不能为空", required = true) </#if> </#if> @Length(max = ${model.columnSize},message = "最长为${model.columnSize}") private String ${model.changeColumnName?uncap_first}; </#if> <#if model.columnType = 'TIMESTAMP' > <#if (model.nullable = '0')> @NotNull(message = "${model.columnComment!}不能为空") @ApiModelProperty(value = "${model.columnComment!}不能为空", required = true) </#if> @Column(name = "${model.columnName}",columnDefinition = "TIMESTAMP") private Date ${model.changeColumnName?uncap_first}; </#if> </#list> </#if> <#if model_column?exists> <#list model_column as model> <#if (model.columnType = 'VARCHAR2' || model.columnType = 'TEXT')> 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 = 'TIMESTAMP' > 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> }
Service.flt文件
package ${package_name}.service; import cn.sh.cjvision.framework.repository.CustomRepository; import ${package_name}.bean.${table_name}Bean; public interface ${table_name}Service extends CustomRepository<${table_name}Bean, String> { }
Controller.flt文件
package ${package_name}.controller; import ${package_name}.bean.${table_name}Bean; import cn.sh.cjvision.framework.annotation.log.LogWrite; import cn.sh.cjvision.framework.controller.BaseController; import cn.sh.cjvision.framework.utils.out.OutPutJson; import ${package_name}.service.${table_name}Service; import com.alibaba.fastjson.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.swagger.annotations.Api; import org.hibernate.validator.constraints.NotBlank; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.beans.factory.annotation.Autowired; import java.util.HashMap; import java.util.List; import java.util.Map; @RestController @RequestMapping("/app/${table_name?uncap_first}") @Validated @Api(tags = "${table_annotation}") public class ${table_name}Controller { private final static Logger logger = LoggerFactory.getLogger(${table_name}Controller.class); @Autowired private ${table_name}Service ${table_name?uncap_first}Service; @Autowired private NamedParameterJdbcTemplate namedParameterJdbcTemplate; /** * 描述:根据Id 查询 * @param id ${table_annotation}id */ @PostMapping(value = "get${table_name}ById") @LogWrite(moduleName = "获取${table_annotation}", op = LogWrite.OP.QUERY) public JSONObject findById(@NotBlank(message = "ID不能为空") String id)throws Exception { JSONObject obj = new JSONObject(); ${table_name}Bean ${table_name?uncap_first}Bean = ${table_name?uncap_first}Service.findOne(id); obj.put("${table_name?uncap_first}Bean", ${table_name?uncap_first}Bean); return OutPutJson.success("获取成功", obj); } /** * 描述:创建${table_annotation} * @param */ @PostMapping(value = "add${table_name}") @LogWrite(moduleName = "新增${table_annotation}", op = LogWrite.OP.ADD) public JSONObject add${table_name}(@Validated ${table_name}Bean ${table_name?uncap_first}Bean) throws Exception { JSONObject obj = new JSONObject(); try{ ${table_name}Bean ${table_name?uncap_first}Save = ${table_name?uncap_first}Service.save(${table_name?uncap_first}Bean); obj.put("${table_name?uncap_first}Bean", ${table_name?uncap_first}Save); return OutPutJson.success("新增成功", obj); } catch (Exception e){ logger.error("URL:/app/${table_name?uncap_first}/add${table_name} 异常 " + e.getMessage(), e); return OutPutJson.failure("新增失败:" + e.getMessage()); } } /** * 描述:删除${table_annotation} * @param id ${table_annotation}id */ @PostMapping(value = "del${table_name}ByIds") @LogWrite(moduleName = "删除${table_annotation}", op = LogWrite.OP.DELETE) public JSONObject del${table_name}ByIds(@RequestBody List<String> ids) throws Exception { Map<String, Object> map = new HashMap<>(1); if(ids.size() > 0){ map.put("ids", ids); namedParameterJdbcTemplate.update("DELETE FROM ${date} WHERE RWRY_ID IN (:ids)", map); } return OutPutJson.success("移除成功"); } /** * 描述:编辑${table_annotation} * @param */ @PostMapping(value = "update${table_name}") @LogWrite(moduleName = "编辑${table_annotation}", op = LogWrite.OP.UPDATE) public JSONObject update${table_name}(@Validated ${table_name}Bean ${table_name?uncap_first}Bean) throws Exception { JSONObject obj = new JSONObject(); try{ ${table_name}Bean ${table_name?uncap_first}Update = ${table_name?uncap_first}Service.saveAndFlush(${table_name?uncap_first}Bean); obj.put("${table_name?uncap_first}Bean", ${table_name?uncap_first}Update); return OutPutJson.success("编辑成功", obj); } catch (Exception e){ logger.error("URL:/app/${table_name?uncap_first}/update${table_name} 异常 " + e.getMessage(), e); return OutPutJson.failure("编辑失败:" + e.getMessage()); } } }
注意:
- 在连接数据库的时候一定要 remarksReporting 设置为true,不然获取不到注解
- 在JDBC元数据的操作是很消耗性能的,有点慢
- JDBC获取信息有7中方法,每个方法获取的信息不一样,就是这个东西,databaseMetaData.getColumns是获取不到主键信息的,获主键的时候一直说的是“列名不存在” 有点蛋疼,需要单独的获取主键信息。详情
- freemarker 开上面写的应该能看懂。
参考:
JDBC元数据操作(一)-- DatabaseMetaData接口详解
Java之利用Freemarker模板引擎实现代码生成器,提高效率(我主要看的这篇,在他的基础上加了生成文件夹, 没有用他的驼峰名)