前言
通常在開始開發(fā)項目的時候对嚼,首先會建立好數(shù)據(jù)庫相關(guān)表夹抗,然后根據(jù)表結(jié)構(gòu)生成 Controller、Service纵竖、DAO漠烧、Model
以及一些前端頁面。
如果開發(fā)前沒有強(qiáng)制的約束靡砌,而每個程序員都有自己的編碼習(xí)慣已脓,最終會導(dǎo)致一個項目呈現(xiàn)出多種編碼風(fēng)格。再有就是一些CRUD
的列表功能通殃,基本是沒啥挑戰(zhàn)性的度液,純粹苦力活,浪費(fèi)時間邓了。
所以恨诱,根據(jù)公司現(xiàn)有框架媳瞪,開發(fā)一款統(tǒng)一風(fēng)格的代碼生成器還是很有必要的骗炉。
技術(shù)選型
開發(fā)框架:SpringBoot+JPA
,考慮到會生成各種前后端代碼文件蛇受,這里我們選用freemarker
模板引擎來制作相應(yīng)的模板句葵。
實(shí)現(xiàn)思路
獲取表結(jié)構(gòu)信息
首先我們定義一個實(shí)體類,為了使用方便兢仰,把表和字段信息放到了一個類中:
/**
* 表以及相關(guān)字段信息
*/
@Data
public class AppGen extends PageBean implements Serializable {
/**
* 表名
*/
private String tableName;
/**
* 實(shí)體類名
*/
private String entityName;
/**
* 實(shí)體類名 首字母小寫
*/
private String lowerEntityName;
/**
* 表備注
*/
private String tableComment;
/**
* 表前綴
*/
private String prefix;
/**
* 功能描述
*/
private String function;
/**
* 列名
*/
private String columnName;
/**
* 實(shí)體列名
*/
private String entityColumnName;
/**
* 列描述
*/
private String columnComment;
/**
* 類型
*/
private String dataType;
/**
* 自增
*/
private Object columnExtra;
/**
* 長度
*/
private Object columnLength;
private List<AppGen> list;
}
獲取表列表:
@Override
@Transactional(readOnly = true)
public Result list(AppGen gen){
String countSql = "SELECT COUNT(*) FROM information_schema.tables ";
countSql +="WHERE table_schema='tools'";
Long totalCount = dynamicQuery.nativeQueryCount(countSql);
PageBean<AppGen> data = new PageBean<>();
if(totalCount>0){
String nativeSql = "SELECT table_name as tableName,table_comment as tableComment ";
nativeSql+="FROM information_schema.tables WHERE table_schema='tools'";
Pageable pageable = PageRequest.of(gen.getPageNo(),gen.getPageSize());
List<AppGen> list = dynamicQuery.nativeQueryPagingListModel(AppGen.class,pageable, nativeSql);
data = new PageBean<>(list, totalCount);
}
return Result.ok(data);
}
制作模板
模板太多了乍丈,這里只以Controller
模板為例,貼一下實(shí)現(xiàn)代碼把将,更多模板見源碼:
package com.tools.module.${prefix}.web;
import com.tools.common.config.AbstractController;
import com.tools.common.model.Result;
import com.tools.module.${prefix}.entity.${entityName};
import com.tools.module.${prefix}.service.${entityName}Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/${prefix}/${function}")
public class ${entityName}Controller extends AbstractController {
@Autowired
private ${entityName}Service ${function}Service;
/**
* 列表
*/
@PostMapping("/list")
public Result list(${entityName} ${function}){
return ${function}Service.list(${function});
}
/**
* 查詢
*/
@PostMapping("/get")
public Result get(Long id){
return ${function}Service.get(id);
}
/**
* 保存
*/
@PostMapping("/save")
public Result save(@RequestBody ${entityName} ${function}){
return ${function}Service.save(${function});
}
/**
* 刪除
*/
@PostMapping("/delete")
public Result delete(Long id){
return ${function}Service.delete(id);
}
}
說白了其實(shí)就是傳遞參數(shù)轻专,把一些可變的代碼片段使用${name}
形式編寫。
代碼生成
有點(diǎn)長察蹲,慢慢看请垛,其實(shí)就是渲染各種前后端模板:
/**
* 生成代碼
* @param gen
* @return
* @throws IOException
* @throws TemplateException
*/
@PostMapping("/create")
public Result create(@RequestBody AppGen gen) throws IOException, TemplateException {
/**
* 獲取表字段以及注釋
*/
List<AppGen> list = genService.getByTable(gen);
String name = gen.getTableName();
String[] table = StringUtils.split(name,"_");
gen.setPrefix(table[0]);
gen.setFunction(table[1]);
gen.setEntityName(GenUtils.allInitialCapital(gen.getTableName()));
list.stream().forEach(column-> {
column.setEntityColumnName(GenUtils.secInitialCapital(column.getColumnName()));
});
gen.setList(list);
String baseFile = filePath+ SystemConstant.SF_FILE_SEPARATOR+"com"+
SystemConstant.SF_FILE_SEPARATOR+ "tools"+
SystemConstant.SF_FILE_SEPARATOR+ "module"+
SystemConstant.SF_FILE_SEPARATOR+ gen.getPrefix()+SystemConstant.SF_FILE_SEPARATOR;
/**
* 后端代碼
*/
File entityFile = FileUtil.touch(baseFile+"entity"+
SystemConstant.SF_FILE_SEPARATOR+gen.getEntityName()+".java");
File repositoryFile = FileUtil.touch(baseFile+"repository"+
SystemConstant.SF_FILE_SEPARATOR+gen.getEntityName()+"Repository.java");
File serviceFile = FileUtil.touch(baseFile+"service"+
SystemConstant.SF_FILE_SEPARATOR+gen.getEntityName()+"Service.java");
File serviceImplFile = FileUtil.touch(baseFile+"service"+
SystemConstant.SF_FILE_SEPARATOR+"impl"+SystemConstant.SF_FILE_SEPARATOR+
gen.getEntityName()+"ServiceImpl.java");
File controllerFile = FileUtil.touch(baseFile+"web"+
SystemConstant.SF_FILE_SEPARATOR + gen.getEntityName() + "Controller.java");
/**
* 前端代碼
*/
String htmlPath = filePath+
SystemConstant.SF_FILE_SEPARATOR + "templates"+
SystemConstant.SF_FILE_SEPARATOR + gen.getPrefix()+
SystemConstant.SF_FILE_SEPARATOR + gen.getFunction()+SystemConstant.SF_FILE_SEPARATOR;
File listFile = FileUtil.touch(htmlPath + "list.html");
File formFile = FileUtil.touch(htmlPath + "form.html");
/**
* 生成靜態(tài)頁面
*/
Template template = configuration.getTemplate("html/list.ftl");
String text = FreeMarkerTemplateUtils.processTemplateIntoString(
template, gen);
FileUtil.writeString(text,listFile,"UTF-8");
template = configuration.getTemplate("html/form.ftl");
text = FreeMarkerTemplateUtils.processTemplateIntoString(
template, gen);
FileUtil.writeString(text,formFile,"UTF-8");
/**
* 生成后端代碼 repository
*/
template = configuration.getTemplate("java/repository.ftl");
text = FreeMarkerTemplateUtils.processTemplateIntoString(
template, gen);
FileUtil.writeString(text,repositoryFile,"UTF-8");
/**
* 生成后端代碼 entity
*/
template = configuration.getTemplate("java/entity.ftl");
text = FreeMarkerTemplateUtils.processTemplateIntoString(
template, gen);
FileUtil.writeString(text,entityFile,"UTF-8");
/**
* 生成后端代碼 service
*/
template = configuration.getTemplate("java/service.ftl");
text = FreeMarkerTemplateUtils.processTemplateIntoString(
template, gen);
FileUtil.writeString(text,serviceFile,"UTF-8");
/**
* 生成后端代碼 service 實(shí)現(xiàn)
*/
template = configuration.getTemplate("java/serviceImpl.ftl");
text = FreeMarkerTemplateUtils.processTemplateIntoString(
template, gen);
FileUtil.writeString(text,serviceImplFile,"UTF-8");
/**
* 生成后端代碼 controller 實(shí)現(xiàn)
*/
template = configuration.getTemplate("java/controller.ftl");
text = FreeMarkerTemplateUtils.processTemplateIntoString(
template, gen);
FileUtil.writeString(text,controllerFile,"UTF-8");
return Result.ok();
}
生成邏輯還是很傻瓜的,后期會慢慢優(yōu)化洽议,比如根據(jù)字段類型生成不同的表單形式宗收,可以自定義字段是否顯示等的。
小結(jié)
總的來說亚兄,還是比較容易上手的混稽,相對于一些簡單的列表功能分分鐘擼出效果,開發(fā)一分鐘,喝茶一整天匈勋。當(dāng)然對于一些復(fù)雜的效果礼旅,還是自己一一去實(shí)現(xiàn)。