SpringBoot代码生成器,从此不用手撸代码

前言
通常在开始开发项目的时候,首先会建立好数据库相关表,然后根据表结构生成 Controller、Service、DAO、Model以及一些前端页面。
如果开发前没有强制的约束,而每个程序员都有自己的编码习惯,最终会导致一个项目呈现出多种编码风格。再有就是一些CRUD的列表功能,基本是没啥挑战性的,纯粹苦力活,浪费时间。
所以,根据公司现有框架,开发一款统一风格的代码生成器还是很有必要的。
技术选型
开发框架:SpringBoot+JPA,考虑到会生成各种前后端代码文件,这里我们选用freemarker模板引擎来制作相应的模板。
实现思路
获取表结构信息
首先我们定义一个实体类,为了使用方便,把表和字段信息放到了一个类中:
/**
* 表以及相关字段信息
*/
@Data
public class AppGen extends PageBean implements Serializable {
/**
* 表名
*/
private String tableName;
/**
* 实体类名
*/
private String entityName;
/**
* 实体类名 首字母小写
*/
private String lowerEntityName;
/**
* 表备注
*/
private String tableComment;
/**
* 表前缀
*/
private String prefix;
/**
* 功能描述
*/
private String function;
/**
* 列名
*/
private String columnName;
/**
* 实体列名
*/
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模板为例,贴一下实现代码,更多模板见源码:
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);
}
}
说白了其实就是传递参数,把一些可变的代码片段使用${name}形式编写。
代码生成
有点长,慢慢看,其实就是渲染各种前后端模板:
/**
* 生成代码
* @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");
/**
* 生成静态页面
*/
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 实现
*/
template = configuration.getTemplate("java/serviceImpl.ftl");
text = FreeMarkerTemplateUtils.processTemplateIntoString(
template, gen);
FileUtil.writeString(text,serviceImplFile,"UTF-8");
/**
* 生成后端代码 controller 实现
*/
template = configuration.getTemplate("java/controller.ftl");
text = FreeMarkerTemplateUtils.processTemplateIntoString(
template, gen);
FileUtil.writeString(text,controllerFile,"UTF-8");
return Result.ok();
}
生成逻辑还是很傻瓜的,后期会慢慢优化,比如根据字段类型生成不同的表单形式,可以自定义字段是否显示等的。
小结
总的来说,还是比较容易上手的,相对于一些简单的列表功能分分钟撸出效果,开发一分钟,喝茶一整天。当然对于一些复杂的效果,还是自己一一去实现。
源码
https://gitee.com/52itstyle/SPTools
SpringBoot代码生成器,从此不用手撸代码的更多相关文章
- 你试过不用if撸代码吗?
译者按: 试着不用if撸代码,是件很有趣的事,而且,万一你领会了什么是“数据即代码,代码即数据”呢? 原文: Coding Tip: Try to Code Without If-statements ...
- 手撸代码实现equals方法
重点都在注释里面写了,这里就不再重复叙述,贴上代码到博客主要是备用. package equals; class Book extends Object { private String title; ...
- 手撸一个SpringBoot的Starter,简单易上手
前言:今天介绍一SpringBoot的Starter,并手写一个自己的Starter,在SpringBoot项目中,有各种的Starter提供给开发者使用,Starter则提供各种API,这样使开发S ...
- 【分布式锁的演化】终章!手撸ZK分布式锁!
前言 这应该是分布式锁演化的最后一个章节了,相信很多小伙伴们看完这个章节之后在应对高并发的情况下,如何保证线程安全心里肯定也会有谱了.在实际的项目中也可以参考一下老猫的github上的例子,当然代码没 ...
- 《Spring 手撸专栏》第 3 章:初显身手,运用设计模式,实现 Bean 的定义、注册、获取
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 你是否能预见复杂内容的设计问题? 讲道理,无论产品功能是否复杂,都有很大一部分程序员 ...
- 手撸一个SpringBoot-Starter
1. 简介 通过了解SpringBoot的原理后,我们可以手撸一个spring-boot-starter来加深理解. 1.1 什么是starter spring官网解释 starters是一组方便的依 ...
- ClownFish:比手写代码还快的通用数据访问层
http://www.cnblogs.com/fish-li/archive/2012/07/17/ClownFish.html 阅读目录 开始 ClownFish是什么? 比手写代码还快的执行速度 ...
- .NET手撸绘制TypeScript类图——下篇
.NET手撸绘制TypeScript类图--下篇 在上篇的文章中,我们介绍了如何使用.NET解析TypeScript,这篇将介绍如何使用代码将类图渲染出来. 注:以防有人错过了,上篇链接如下:http ...
- 99%的程序员都在用Lombok,原理竟然这么简单?我也手撸了一个!|建议收藏!!!
罗曼罗兰说过:世界上只有一种英雄主义,就是看清生活的真相之后依然热爱生活. 对于 Lombok 我相信大部分人都不陌生,但对于它的实现原理以及缺点却鲜为人知,而本文将会从 Lombok 的原理出发,手 ...
随机推荐
- 【集群实战】fatab开机挂载失败案例
1. nfs挂载加入fstab案例 NFS客户端实现fstab开机自启动挂载 现象:nfs开机挂载卸载了/etc/fstab中,结果无法开机自动挂载nfs 解答:1. nfs客户端命令放在/etc/r ...
- IT服务,共享经济的下一个风口?
前两天,在上千名CIO参加.释放10亿采购需求的2017华南CIO大会暨信息技术交易会上,一款"一站式IT工程师共享平台"成为大会关注焦点--这就是神州数码旗下的神州邦邦. 其实最 ...
- Vim Install-Could not get lock......
在Ubuntu下安装Vim编辑器时,出现错误: 可能原因: 上次安装或更新没有正常完成,导致资源被锁. 解决方案: 删除异常资源,重新下载: 注意 删除异常资源时,一定要切换到root用户,否则操作无 ...
- MySQL Windows 环境安装
1.下载 MySQL Windows 安装包 下载地址:https://downloads.mysql.com/archives/installer/ 我这个是 MySQL 5.7 版本 2.直接双击 ...
- 学习笔记之pip的基本使用
粗略学习了pip的基础知识,便将此作为学习笔记记录下来同样希望分享的能帮到大家! 如果自己电脑没有pip,小澈在此分享如何安装,解决办法很多呢 1.使用easy_install安装: 各种进入到eas ...
- request中跟路径有关的api的分析
最近重在研究struts的源码,其中涉及到了request中的几个api,看了文档后还是觉得不清楚,所以在自己原来的工程中 测试了一下各个api的具体效果.在这里跟大家分享一下. 这是我具体测试的代码 ...
- Asp.Net Core MVC在View中,根据用户权限动态生成菜单
1. 用户登录时,将用户的权限写入Cookie: //将需要的信息写入claims后 var identity = new ClaimsIdentity(claims, IdentityConstan ...
- 【Hadoop离线基础总结】Sqoop常用命令及参数
目录 常用命令 常用公用参数 公用参数:数据库连接 公用参数:import 公用参数:export 公用参数:hive 常用命令&参数 从关系表导入--import 导出到关系表--expor ...
- fork...join的用法
如果希望在仿真的某一时刻同时启动多个任务,可以使用fork....join语句.例如,在仿真开始的 100 ns 后,希望同时启动发送和接收任务,而不是发送完毕后再进行接收,如下所示: initial ...
- [coj 1353 Guessing the Number]kmp,字符串最小表示法
题意:给一个字符串,求它的最小子串,使得原串是通过它重复得到的字符串的一个子串. 思路:先求最小长度,最小循环长度可以利用kmp的next数组快速得到,求出长度后然后利用字符串最小表示法求循环节的最小 ...