博客信息

代码生成器制作方案

发布时间:『 2019-06-20 04:34』  博客类别:解决方案  阅读(992)

前言

每一个系统都是由多张表的增删改查所构成,只不过对于业务复杂的系统而言,其系统中的表之间存在着千丝万缕的关系;

对于程序员而言,做系统应该将更多精力投放到处理业务上面,也就是说精力应该放在处理表与表之间的关系上面,而并非是重复的增删改查的代码编写;

而本篇博客的目的在于,制作一个代码生成器,将项目中所用到的表,增删改查的前端后端代码都一并自动生成;让程序员解放双手,安心处理系统业务;

 

代码生成器的制作方式有很多种,有纯js前端版的,有后端版本的,这里介绍依赖于后端的代码生成器,需要用到的技术有springbootmybatisvelocity模板引擎、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;
}


第一张效果图中的前端对应代码

list.html 

list.js 


---------------------------------------------以下为代码生成核心代码-----------------------------------------

 

存放表、表列段与类、类属性的映射关系,换一句话说,该文件存放着代码生成规则

generator.properties 


需要的模板引擎列表


小李飞刀_解决方案


代码生成器模板列表(重要)

generator.zip 


实体类

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晓码阁 版权所有