每一个系统都是由多张表的增删改查所构成,只不过对于业务复杂的系统而言,其系统中的表之间存在着千丝万缕的关系;
对于程序员而言,做系统应该将更多精力投放到处理业务上面,也就是说精力应该放在处理表与表之间的关系上面,而并非是重复的增删改查的代码编写;
而本篇博客的目的在于,制作一个代码生成器,将项目中所用到的表,增删改查的前端后端代码都一并自动生成;让程序员解放双手,安心处理系统业务;
代码生成器的制作方式有很多种,有纯js前端版的,有后端版本的,这里介绍依赖于后端的代码生成器,需要用到的技术有springboot、mybatis、velocity模板引擎、IO流技术等等;
代码生成器制作思路
1、读取到指定数据库下的所有表,然后在界面上展示出来;
2、按照表名查询出来其对应的所有的表字段;
3、读取generator.properties获取表、表列段与类、类属性的对应关系;
4、将表名、列名通过工具类,生成对应的类名、类属性名;
5、获取velocity资源加载器,加载模板集合(dao层模板、service层模板,controler层模板、html模板、js模板等等),将模板中的变量绑定到velocity中的模板
6、最终渲染模板以zip包的形式写出

Zip压缩包中的清单列表如下

其中一个生成出来的类内容
package com.javaxl.bootdo.system.controller;
import java.util.List;
import java.util.Map;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.Model;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
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.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.javaxl.bootdo.system.domain.MenuDO;
import com.javaxl.bootdo.system.service.MenuService;
import com.javaxl.bootdo.common.utils.PageUtils;
import com.javaxl.bootdo.common.utils.Query;
import com.javaxl.bootdo.common.utils.R;
/**
* 菜单管理
*
* @author chglee
* @email 1992lcg@163.com
* @date 2019-06-20 16:13:26
*/
@Controller
@RequestMapping("/system/menu")
public class MenuController {
@Autowired
private MenuService menuService;
@GetMapping()
@RequiresPermissions("system:menu:menu")
String Menu(){
return "system/menu/menu";
}
@ResponseBody
@GetMapping("/list")
@RequiresPermissions("system:menu:menu")
public PageUtils list(@RequestParam Map<String, Object> params){
//查询列表数据
Query query = new Query(params);
List<MenuDO> menuList = menuService.list(query);
int total = menuService.count(query);
PageUtils pageUtils = new PageUtils(menuList, total);
return pageUtils;
}
@GetMapping("/add")
@RequiresPermissions("system:menu:add")
String add(){
return "system/menu/add";
}
@GetMapping("/edit/{menuId}")
@RequiresPermissions("system:menu:edit")
String edit(@PathVariable("menuId") Long menuId,Model model){
MenuDO menu = menuService.get(menuId);
model.addAttribute("menu", menu);
return "system/menu/edit";
}
/**
* 保存
*/
@ResponseBody
@PostMapping("/save")
@RequiresPermissions("system:menu:add")
public R save( MenuDO menu){
if(menuService.save(menu)>0){
return R.ok();
}
return R.error();
}
/**
* 修改
*/
@ResponseBody
@RequestMapping("/update")
@RequiresPermissions("system:menu:edit")
public R update( MenuDO menu){
menuService.update(menu);
return R.ok();
}
/**
* 删除
*/
@PostMapping( "/remove")
@ResponseBody
@RequiresPermissions("system:menu:remove")
public R remove( Long menuId){
if(menuService.remove(menuId)>0){
return R.ok();
}
return R.error();
}
/**
* 删除
*/
@PostMapping( "/batchRemove")
@ResponseBody
@RequiresPermissions("system:menu:batchRemove")
public R remove(@RequestParam("ids[]") Long[] menuIds){
menuService.batchRemove(menuIds);
return R.ok();
}
}
下面开始上代码生成器相关的核心代码了
Dao层
package com.javaxl.bootdo.common.dao;
import org.apache.ibatis.annotations.Select;
import java.util.List;
import java.util.Map;
public interface GeneratorMapper {
/**
* 查询出当前用户对应的所有表
* @return
*/
@Select("select table_name tableName, engine, table_comment tableComment, create_time createTime from information_schema.tables"
+ " where table_schema = (select database())")
List<Map<String, Object>> list();
@Select("select count(*) from information_schema.tables where table_schema = (select database())")
int count(Map<String, Object> map);
@Select("select table_name tableName, engine, table_comment tableComment, create_time createTime from information_schema.tables \r\n"
+ " where table_schema = (select database()) and table_name = #{tableName}")
Map<String, String> get(String tableName);
/**
* 查询当前表所有的列段
* @param tableName
* @return
*/
@Select("select column_name columnName, data_type dataType, column_comment columnComment, column_key columnKey, extra from information_schema.columns\r\n"
+ " where table_name = #{tableName} and table_schema = (select database()) order by ordinal_position")
List<Map<String, String>> listColumns(String tableName);
}
Service层
@Override
public List<Map<String, Object>> list() {
List<Map<String, Object>> list = generatorMapper.list();
return list;
}
Controller层
@ResponseBody
@GetMapping("/list")
List<Map<String, Object>> list() {
List<Map<String, Object>> list = generatorService.list();
return list;
}
第一张效果图中的前端对应代码
---------------------------------------------以下为代码生成核心代码-----------------------------------------
存放表、表列段与类、类属性的映射关系,换一句话说,该文件存放着代码生成规则
需要的模板引擎列表

代码生成器模板列表(重要)
实体类
package com.javaxl.bootdo.common.domain;
/**
* 列的属性
*
*
*/
public class ColumnDO {
// 列名
private String columnName;
// 列名类型
private String dataType;
// 列名备注
private String comments;
// 属性名称(第一个字母大写),如:user_name => UserName
private String attrName;
// 属性名称(第一个字母小写),如:user_name => userName
private String attrname;
// 属性类型
private String attrType;
// auto_increment
private String extra;
public String getColumnName() {
return columnName;
}
public void setColumnName(String columnName) {
this.columnName = columnName;
}
public String getDataType() {
return dataType;
}
public void setDataType(String dataType) {
this.dataType = dataType;
}
public String getComments() {
return comments;
}
public void setComments(String comments) {
this.comments = comments;
}
public String getAttrname() {
return attrname;
}
public void setAttrname(String attrname) {
this.attrname = attrname;
}
public String getAttrName() {
return attrName;
}
public void setAttrName(String attrName) {
this.attrName = attrName;
}
public String getAttrType() {
return attrType;
}
public void setAttrType(String attrType) {
this.attrType = attrType;
}
public String getExtra() {
return extra;
}
public void setExtra(String extra) {
this.extra = extra;
}
@Override
public String toString() {
return "ColumnDO{" +
"columnName='" + columnName + '\'' +
", dataType='" + dataType + '\'' +
", comments='" + comments + '\'' +
", attrName='" + attrName + '\'' +
", attrname='" + attrname + '\'' +
", attrType='" + attrType + '\'' +
", extra='" + extra + '\'' +
'}';
}
}
package com.javaxl.bootdo.common.domain;
import java.util.List;
/**
* 表数据
*
* @author chenshun
* @email sunlightcs@gmail.com
* @date 2016年12月20日 上午12:02:55
*/
public class TableDO {
//表的名称
private String tableName;
//表的备注
private String comments;
//表的主键
private ColumnDO pk;
//表的列名(不包含主键)
private List<ColumnDO> columns;
//类名(第一个字母大写),如:sys_user => SysUser
private String className;
//类名(第一个字母小写),如:sys_user => sysUser
private String classname;
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public String getComments() {
return comments;
}
public void setComments(String comments) {
this.comments = comments;
}
public ColumnDO getPk() {
return pk;
}
public void setPk(ColumnDO pk) {
this.pk = pk;
}
public List<ColumnDO> getColumns() {
return columns;
}
public void setColumns(List<ColumnDO> columns) {
this.columns = columns;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getClassname() {
return classname;
}
public void setClassname(String classname) {
this.classname = classname;
}
@Override
public String toString() {
return "TableDO{" +
"tableName='" + tableName + '\'' +
", comments='" + comments + '\'' +
", pk=" + pk +
", columns=" + columns +
", className='" + className + '\'' +
", classname='" + classname + '\'' +
'}';
}
}
Controler层代码
@RequestMapping("/code/{tableName}")
public void code(HttpServletRequest request, HttpServletResponse response,
@PathVariable("tableName") String tableName) throws IOException {
String[] tableNames = new String[] { tableName };
byte[] data = generatorService.generatorCode(tableNames);
response.reset();
response.setHeader("Content-Disposition", "attachment; filename=\"bootdo.zip\"");
response.addHeader("Content-Length", "" + data.length);
response.setContentType("application/octet-stream; charset=UTF-8");
IOUtils.write(data, response.getOutputStream());
}
Service层
@Override
public byte[] generatorCode(String[] tableNames) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ZipOutputStream zip = new ZipOutputStream(outputStream);
for(String tableName : tableNames){
//查询表信息
Map<String, String> table = generatorMapper.get(tableName);
//查询列信息
List<Map<String, String>> columns = generatorMapper.listColumns(tableName);
//生成代码
GenUtils.generatorCode(table, columns, zip);
}
IOUtils.closeQuietly(zip);
return outputStream.toByteArray();
}
代码生成工具类
package com.javaxl.bootdo.common.utils;
import com.javaxl.bootdo.common.config.Constant;
import com.javaxl.bootdo.common.domain.ColumnDO;
import com.javaxl.bootdo.common.domain.TableDO;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.WordUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* 代码生成器 工具类
*/
public class GenUtils {
public static List<String> getTemplates() {
List<String> templates = new ArrayList<String>();
templates.add("templates/common/generator/domain.java.vm");
templates.add("templates/common/generator/Dao.java.vm");
//templates.add("templates/common/generator/Mapper.java.vm");
templates.add("templates/common/generator/Mapper.xml.vm");
templates.add("templates/common/generator/Service.java.vm");
templates.add("templates/common/generator/ServiceImpl.java.vm");
templates.add("templates/common/generator/Controller.java.vm");
templates.add("templates/common/generator/list.html.vm");
templates.add("templates/common/generator/add.html.vm");
templates.add("templates/common/generator/edit.html.vm");
templates.add("templates/common/generator/list.js.vm");
templates.add("templates/common/generator/add.js.vm");
templates.add("templates/common/generator/edit.js.vm");
//templates.add("templates/common/generator/menu.sql.vm");
return templates;
}
/**
* 生成代码
*/
public static void generatorCode(Map<String, String> table,
List<Map<String, String>> columns, ZipOutputStream zip) {
//配置信息
Configuration config = getConfig();
//表信息
TableDO tableDO = new TableDO();
tableDO.setTableName(table.get("tableName"));
tableDO.setComments(table.get("tableComment"));
//表名转换成Java类名
String className = tableToJava(tableDO.getTableName(), config.getString("tablePrefix"),config.getString("autoRemovePre"));
tableDO.setClassName(className);
tableDO.setClassname(StringUtils.uncapitalize(className));
//列信息
List<ColumnDO> columsList = new ArrayList<>();
for (Map<String, String> column : columns) {
ColumnDO columnDO = new ColumnDO();
columnDO.setColumnName(column.get("columnName"));
columnDO.setDataType(column.get("dataType"));
columnDO.setComments(column.get("columnComment"));
columnDO.setExtra(column.get("extra"));
//列名转换成Java属性名
String attrName = columnToJava(columnDO.getColumnName());
columnDO.setAttrName(attrName);
columnDO.setAttrname(StringUtils.uncapitalize(attrName));
//列的数据类型,转换成Java类型
String attrType = config.getString(columnDO.getDataType(), "unknowType");
columnDO.setAttrType(attrType);
//是否主键
if ("PRI".equalsIgnoreCase(column.get("columnKey")) && tableDO.getPk() == null) {
tableDO.setPk(columnDO);
}
columsList.add(columnDO);
}
tableDO.setColumns(columsList);
//没主键,则第一个字段为主键
if (tableDO.getPk() == null) {
tableDO.setPk(tableDO.getColumns().get(0));
}
//设置velocity资源加载器
Properties prop = new Properties();
prop.put("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
Velocity.init(prop);
//封装模板数据
Map<String, Object> map = new HashMap<>(16);
map.put("tableName", tableDO.getTableName());
map.put("comments", tableDO.getComments());
map.put("pk", tableDO.getPk());
map.put("className", tableDO.getClassName());
map.put("classname", tableDO.getClassname());
map.put("pathName", config.getString("package").substring(config.getString("package").lastIndexOf(".") + 1));
map.put("columns", tableDO.getColumns());
map.put("package", config.getString("package"));
map.put("author", config.getString("author"));
map.put("email", config.getString("email"));
map.put("datetime", DateUtils.format(new Date(), DateUtils.DATE_TIME_PATTERN));
VelocityContext context = new VelocityContext(map);
//获取模板列表
List<String> templates = getTemplates();
for (String template : templates) {
//渲染模板
StringWriter sw = new StringWriter();
Template tpl = Velocity.getTemplate(template, "UTF-8");
tpl.merge(context, sw);
try {
//添加到zip
zip.putNextEntry(new ZipEntry(getFileName(template, tableDO.getClassname(), tableDO.getClassName(), config.getString("package").substring(config.getString("package").lastIndexOf(".") + 1))));
IOUtils.write(sw.toString(), zip, "UTF-8");
IOUtils.closeQuietly(sw);
zip.closeEntry();
} catch (IOException e) {
throw new BDException("渲染模板失败,表名:" + tableDO.getTableName(), e);
}
}
}
/**
* 列名转换成Java属性名
*/
public static String columnToJava(String columnName) {
return WordUtils.capitalizeFully(columnName, new char[]{'_'}).replace("_", "");
}
/**
* 表名转换成Java类名
*/
public static String tableToJava(String tableName, String tablePrefix, String autoRemovePre) {
if (Constant.AUTO_REOMVE_PRE.equals(autoRemovePre)) {
tableName = tableName.substring(tableName.indexOf("_") + 1);
}
if (StringUtils.isNotBlank(tablePrefix)) {
tableName = tableName.replace(tablePrefix, "");
}
return columnToJava(tableName);
}
/**
* 获取配置信息
*/
public static Configuration getConfig() {
try {
return new PropertiesConfiguration("generator.properties");
} catch (ConfigurationException e) {
throw new BDException("获取配置文件失败,", e);
}
}
/**
* 获取文件名
*/
public static String getFileName(String template, String classname, String className, String packageName) {
String packagePath = "main" + File.separator + "java" + File.separator;
//String modulesname=config.getString("packageName");
if (StringUtils.isNotBlank(packageName)) {
packagePath += packageName.replace(".", File.separator) + File.separator;
}
if (template.contains("domain.java.vm")) {
return packagePath + "domain" + File.separator + className + "DO.java";
}
if (template.contains("Dao.java.vm")) {
return packagePath + "dao" + File.separator + className + "Dao.java";
}
// if(template.contains("Mapper.java.vm")){
// return packagePath + "dao" + File.separator + className + "Mapper.java";
// }
if (template.contains("Service.java.vm")) {
return packagePath + "service" + File.separator + className + "Service.java";
}
if (template.contains("ServiceImpl.java.vm")) {
return packagePath + "service" + File.separator + "impl" + File.separator + className + "ServiceImpl.java";
}
if (template.contains("Controller.java.vm")) {
return packagePath + "controller" + File.separator + className + "Controller.java";
}
if (template.contains("Mapper.xml.vm")) {
return "main" + File.separator + "resources" + File.separator + "mapper" + File.separator + packageName + File.separator + className + "Mapper.xml";
}
if (template.contains("list.html.vm")) {
return "main" + File.separator + "resources" + File.separator + "templates" + File.separator
+ packageName + File.separator + classname + File.separator + classname + ".html";
// + "modules" + File.separator + "generator" + File.separator + className.toLowerCase() + ".html";
}
if (template.contains("add.html.vm")) {
return "main" + File.separator + "resources" + File.separator + "templates" + File.separator
+ packageName + File.separator + classname + File.separator + "add.html";
}
if (template.contains("edit.html.vm")) {
return "main" + File.separator + "resources" + File.separator + "templates" + File.separator
+ packageName + File.separator + classname + File.separator + "edit.html";
}
if (template.contains("list.js.vm")) {
return "main" + File.separator + "resources" + File.separator + "static" + File.separator + "js" + File.separator
+ "appjs" + File.separator + packageName + File.separator + classname + File.separator + classname + ".js";
// + "modules" + File.separator + "generator" + File.separator + className.toLowerCase() + ".js";
}
if (template.contains("add.js.vm")) {
return "main" + File.separator + "resources" + File.separator + "static" + File.separator + "js" + File.separator
+ "appjs" + File.separator + packageName + File.separator + classname + File.separator + "add.js";
}
if (template.contains("edit.js.vm")) {
return "main" + File.separator + "resources" + File.separator + "static" + File.separator + "js" + File.separator
+ "appjs" + File.separator + packageName + File.separator + classname + File.separator + "edit.js";
}
// if(template.contains("menu.sql.vm")){
// return className.toLowerCase() + "_menu.sql";
// }
return null;
}
}
String工具类所用到的方法
public static String uncapitalize(String str) {
int strLen;
if (str == null || (strLen = str.length()) == 0) {
return str;
}
return new StrBuilder(strLen)
.append(Character.toLowerCase(str.charAt(0)))
.append(str.substring(1))
.toString();
}
代码介绍完毕......
备案号:湘ICP备19000029号
Copyright © 2018-2019 javaxl晓码阁 版权所有