代码生成器

代码生成器

1. 模板技术

  • velocity:默认模板后缀vm
  • freemarker:默认模板后缀ftl

1.1 模板技术输出数据

数据(通常从数据库来)+模板(自定义)= 输出文本(目标文件)。

1.2 velocity的模板技术功能点

  1. 动态页面静态化:xxx.html
  2. 在后台准备数据,在前台准备模板,通过IO把数据与模板合并,真正的生成一个html页面出来。
  3. 用作发送邮件、短信模板。
  4. 代码生成器(今天要完成的代码生成器)。

2. velocity技术

2.1 pom依赖

<!-- 代码生成器模版技术 -->
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity</artifactId>
    <version>1.6</version>
</dependency>

2.2 入门程序

  1. 程序代码
@Test
public void test() throws Exception {
    //创建模本应用上下文
    VelocityContext context = new VelocityContext();
    //填充数据
    context.put("msg","Hello World!");
    //加载模板
    Template template = Velocity.getTemplate("template/new.html","UTF-8");
    //封装文本(目标文件)的位置
    FileWriter writer = new FileWriter(new File("template/new_1.html"));
    //将数据写到模板中
    template.merge(context,writer);
    writer.close();
}
  1. 模板
<html>
<meta charset="UTF-8">
<title></title>
<body>
<h1 style="color: red">${msg}</h1>
</body>
</html>

3.EasyCode插件

​ 主要通过自定义模板(基于velocity)来生成各种代码。通常用于生成Entity(domain)、Dao、Service、Controller。如果你动手能力强还可以用于生成HTML、JS、PHP等代码,理论上来说只要是与数据有关的代码都是可以生成的。

5.1 直接在plugins中安装Easy Code即可。

5.2 配置数据库

  1. idea面板右边点击database配置数据库;
  2. 注意要选择与当前项目对应的数据。

5.3 配置模板

5.3.1 domain模板

##引入宏定义
$!define
##使用宏定义设置回调(保存位置与文件后缀)
#save("/main/java/com/yogie/domain", ".java")
##使用宏定义设置包后缀
#setPackageSuffix("domain")
##使用全局变量实现默认包导入
$!autoImport
import javax.persistence.*;
##使用宏定义实现类注释信息
#tableComment("实体类")
@Entity
@Table(name = "$!{tableInfo.obj.name}")
public class $!{tableInfo.name} extends BaseDomain{
#foreach($column in $tableInfo.otherColumn)
    #if(${column.comment})//${column.comment}#end
    private $!{tool.getClsNameByFullName($column.type)} $!{column.name};
#end
#foreach($column in $tableInfo.otherColumn)
##使用宏定义实现get,set方法
    #getSetMethod($column)
#end
}

5.3.2 query模板

##引入宏定义
$!define
##使用宏定义设置回调(保存位置与文件后缀)
#save("/main/java/com/yogie/query", ".java")
##使用宏定义设置包后缀
#setPackageSuffix("query")
##设置文件名
$!callback.setFileName($tool.append($!{tableInfo.name},"Query", ".java"))
##使用全局变量实现默认包导入
$!autoImport
import com.github.wenhao.jpa.Specifications;
import com.yogie.domain.$!{tableInfo.name};
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.jpa.domain.Specification;
##使用宏定义实现类注释信息
#tableComment("实体类")
public class $!{tableInfo.name}Query extends BaseQuery {
    //查询条件需要的字段
    private String name;
    /**
     * 创建query查询条件
     */
    @Override
    public Specification createSpec() {
        /**
         * Specifications:是com.github.wenhao.jpa插件的类
         */
        Specification<$!{tableInfo.name}> spec = Specifications.<$!{tableInfo.name}>and()
                .like(StringUtils.isNotBlank(name), "name", "%" + name + "%")
                .build();
        return spec;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

5.3.2 repository模板

##引入宏定义
$!define
##使用宏定义设置回调(保存位置与文件后缀)
#save("/main/java/com/yogie/repository", ".java")
##使用宏定义设置包后缀
#setPackageSuffix("repository")
##设置文件名
$!callback.setFileName($tool.append($!{tableInfo.name},"Repository", ".java"))
##使用全局变量实现默认包导入
$!autoImport
import com.yogie.domain.$!{tableInfo.name};
##使用宏定义实现类注释信息
#tableComment("实体类")
public interface $!{tableInfo.name}Repository extends BaseRepository<$!{tableInfo.name},Long>{
}

5.3.2 service模板

##引入宏定义
$!define
##使用宏定义设置回调(保存位置与文件后缀)
#save("/main/java/com/yogie/service", ".java")
##使用宏定义设置包后缀
#setPackageSuffix("service")
##设置文件名
$!callback.setFileName($tool.append("I",$!{tableInfo.name},"Service", ".java"))
##使用全局变量实现默认包导入
$!autoImport
import com.yogie.domain.$!{tableInfo.name};
##使用宏定义实现类注释信息
#tableComment("实体类")
public interface I$!{tableInfo.name}Service extends IBaseService<$!{tableInfo.name},Long> {
}

5.3.2 serviceimpl模板

##引入宏定义
$!define
##使用宏定义设置回调(保存位置与文件后缀)
#save("/main/java/com/yogie/service/impl", ".java")
##使用宏定义设置包后缀
#setPackageSuffix("service.impl")
##设置文件名
$!callback.setFileName($tool.append($!{tableInfo.name},"ServiceImpl", ".java"))
##使用全局变量实现默认包导入
$!autoImport
import com.yogie.domain.$!{tableInfo.name};
import com.yogie.repository.$!{tableInfo.name}Repository;
import com.yogie.service.I$!{tableInfo.name}Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
##使用宏定义实现类注释信息
#tableComment("实体类")
@Service
public class $!{tableInfo.name}ServiceImpl extends BaseServiceImpl<$!{tableInfo.name},Long> implements I$!{tableInfo.name}Service {
    @Autowired
    private $!{tableInfo.name}Repository $!{tableInfo.obj.name}Repository;
}

5.3.2 controller模板

##引入宏定义
$!define

##使用宏定义设置回调(保存位置与文件后缀)
#save("/main/java/com/yogie/web/controller", ".java")

##使用宏定义设置包后缀
#setPackageSuffix("web.controller")

##设置文件名
$!callback.setFileName($tool.append($!{tableInfo.name},"Controller", ".java"))

##使用全局变量实现默认包导入
$!autoImport
import com.yogie.common.JsonResult;
import com.yogie.common.UIPage;
import com.yogie.domain.$!{tableInfo.name};
import com.yogie.query.$!{tableInfo.name}Query;
import com.yogie.service.I$!{tableInfo.name}Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;

##使用宏定义实现类注释信息
#tableComment("实体类")
@Controller
@RequestMapping("/$!{tableInfo.obj.name}")
public class $!{tableInfo.name}Controller {
    @Autowired
    private I$!{tableInfo.name}Service $!{tableInfo.obj.name}Service;
    
    @RequestMapping("/index")
    public String index() {
        return "$!{tableInfo.obj.name}/$!{tableInfo.obj.name}";
    }
    
    @RequestMapping("/findAll")
    @ResponseBody
    public List<$!{tableInfo.name}> findAll() {
        return $!{tableInfo.obj.name}Service.findAll();
    }
    
    @RequestMapping("/findPage")
    @ResponseBody
    public UIPage<$!{tableInfo.name}> findPage($!{tableInfo.name}Query baseQuery) {
        Page page = $!{tableInfo.obj.name}Service.findPageByQuery(baseQuery);
        return new UIPage(page);
    }

    @RequestMapping("/delete")
    @ResponseBody
    public JsonResult delete(Long id) {
        try {
            $!{tableInfo.obj.name}Service.delete(id);
            return new JsonResult();
        } catch (Exception e) {
            e.printStackTrace();
            return new JsonResult(false, e.getMessage());
        }
    }

    /**
     * 设计这个方法是为了解决在修改实体对象的时候会产生数据丢失的问题。
     * springmvc的@ModelAttribute注解会在每个controller的每个方法前面执行
     */
    @ModelAttribute("edit$!{tableInfo.name}")
    public $!{tableInfo.name} beforeEdit(Long id, String cmd) {
        if (id != null && "_update".equals(cmd)) {
            $!{tableInfo.name} $!{tableInfo.obj.name} = $!{tableInfo.obj.name}Service.findOne(id);
            /**
             * 这个方法的设计是为了解决当前台修改部门的时候,调教修改的时候会报错:n to n
             * 这个错误的原因是:当修改部门的时候,实际上是修改的$!{tableInfo.obj.name}对应的持久化对象的department持久化对象的oid
             * 解决办法就是:直接将员工关联的department对象设置为null
             * 然后springmvc会根据前台的department.id自动创建一个非持久的department对象
             */
            //$!{tableInfo.obj.name}.setDepartment(null);
            return $!{tableInfo.obj.name};
        }
        return null;
    }

    /**
     * 当在进行修改的时候,修改实体的时候会产生数据丢失现象
     * @param $!{tableInfo.obj.name}
     * @return
     */
    @RequestMapping("/update")
    @ResponseBody
    public JsonResult update(@ModelAttribute("edit$!{tableInfo.name}") $!{tableInfo.name} $!{tableInfo.obj.name}) {
        try {
            $!{tableInfo.obj.name}Service.save($!{tableInfo.obj.name});
            return new JsonResult();
        } catch (Exception e) {
            e.printStackTrace();
            return new JsonResult(false, e.getMessage());
        }
    }
    @RequestMapping("/save")
    @ResponseBody
    public JsonResult save($!{tableInfo.name} $!{tableInfo.obj.name}) {
        try {
            $!{tableInfo.obj.name}Service.save($!{tableInfo.obj.name});
            return new JsonResult();
        } catch (Exception e) {
            e.printStackTrace();
            return new JsonResult(false, e.getMessage());
        }
    }
}

5.3.2 test模板

##引入宏定义
$!define
##使用宏定义设置回调(保存位置与文件后缀)
#save("/test/java/com/yogie/repository", ".java")
##使用宏定义设置包后缀
#setPackageSuffix("repository")
##设置文件名
$!callback.setFileName($tool.append($!{tableInfo.name},"RepositoryTest",".java"))
##使用全局变量实现默认包导入
$!autoImport
import com.github.wenhao.jpa.Specifications;
import com.yogie.domain.$!{tableInfo.name};
import com.yogie.query.$!{tableInfo.name}Query;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.persistence.criteria.*;
import java.util.List;

##使用宏定义实现类注释信息
#tableComment("测试类")
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class $!{tableInfo.name}RepositoryTest {
    @Autowired
    private $!{tableInfo.name}Repository $!{tableInfo.obj.name}Repository;

    @Test
    public void testAdd() {
        $!{tableInfo.name} $!{tableInfo.obj.name} = new $!{tableInfo.name}();
        //添加set方法
        
        $!{tableInfo.obj.name}Repository.save($!{tableInfo.obj.name});
    }
}

5.3.2 jsp模板

##引入宏定义
$!define
##使用宏定义设置回调(保存位置与文件后缀)
#save("/main/webapp/WEB-INF/views/$!{tableInfo.obj.name}", ".jsp")
##设置文件名
$!callback.setFileName($tool.append($!{tableInfo.obj.name}, ".jsp"))

<%--
  User: $!author
  Date: $!currTime
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
    <title>$!{tableInfo.obj.name}.jsp</title>
    <%@include file="../head.jsp" %>
    <%--自定义的js要在head.jsp后引入,因为head.jsp中有jquery的引入,自定义的js如果要使用jquey,就需要之后引入--%>
    <script src="/js/model/$!{tableInfo.obj.name}.js"></script>
    <%--引入easyui扩展库--%>
    <link rel="stylesheet" href="http://www.easyui-extlib.com/Content/icons/icon-standard.css" />
    <link rel="stylesheet" href="http://www.easyui-extlib.com/Scripts/jquery-easyui-extensions/datagrid/jeasyui.extensions.datagrid.css" />
    <script src="http://www.easyui-extlib.com/Scripts/jquery-easyui-extensions/menu/jeasyui.extensions.menu.js"></script>
    <script src="http://www.easyui-extlib.com//Scripts/jquery-easyui-extensions/datagrid/jeasyui.extensions.datagrid.getColumnInfo.js"></script>
    <script src="http://www.easyui-extlib.com/Scripts/jquery-easyui-extensions/datagrid/jeasyui.extensions.datagrid.columnToggle.js"></script>
    <style>
        #editForm table tr td{
            padding: 3px;
        }
        .datagrid-row-selected{
            background-color:#0092DC;
        }
    </style>
</head>
<body oncontextmenu="doNothing()">
<table id="$!{tableInfo.obj.name}Grid" class="easyui-datagrid" fit="true"
       data-options="url:'/$!{tableInfo.obj.name}/findPage',fitColumns:true,
       singleSelect:false,checkOnSelect:true,onRowContextMenu:showMenu,
       pagination:true,toolbar:'#gridTolls',enableHeaderClickMenu:'true'">
    <thead>
        <tr>
            <th data-options="field:'',checkbox:true,width:50,checkbox:true" align="center"></th>
            #foreach($column in $tableInfo.otherColumn)
                #if(${column.comment})//${column.comment}#end
                <th data-options="field:'$!{column.name}',width:100" align="center">$!{column.name}</th>
            #end
        </tr>
    </thead>
</table>
<%--datagrid顶部工具栏--%>
<div id="gridTolls" style="padding:5px;height:auto">
    <%--操作按钮--%>
    <div style="margin-bottom:5px">
        <a href="#" data-method="add" class="easyui-linkbutton" iconCls="icon-add" plain="true">添加</a>
        <a href="#" data-method="update" class="easyui-linkbutton" iconCls="icon-edit" plain="true">修改</a>
        <a href="#" data-method="del" class="easyui-linkbutton" iconCls="icon-remove" plain="true">删除</a>
    </div>
    <%--搜索部分--%>
    <form id="searchForm">
        名称: <input name="name" class="easyui-textbox" style="width:80px">
        <a data-method="search" href="#" class="easyui-linkbutton" iconCls="icon-search">查询</a>
    </form>
</div>
<%--对话框--%>
<div id="$!{tableInfo.obj.name}Dialog" class="easyui-dialog" style="width:345px;padding-left: 35px;padding-right: 35px;padding-top: 15px;"
     data-options="title:'编辑功能',buttons:'#editBtn',modal:true,closed:true">
    <form id="editForm" method="post">
        <%--当修改的时候,把id传到后台--%>
        <input id="$!{tableInfo.obj.name}Id" type="hidden" name="id">
        <table>
        #foreach($column in $tableInfo.otherColumn)
        #if(${column.comment})//${column.comment}#end
        <tr>
            <td>
                <label>$!{column.name}:</label>
            </td>
            <td>
                <input class="easyui-validatebox" type="text" name="$!{column.name}"
                      data-options="required:true"/>
            </td>
        </tr>
        #end
        </table>
    </form>
</div>
<div id="editBtn">
    <a data-method="save" href="#" class="easyui-linkbutton">保存</a>
    <a data-method="close" href="#" class="easyui-linkbutton">关闭</a>
</div>
<div id="gridMenu" class="easyui-menu" style="width:80px;">
    <div data-options="iconCls:'icon-add'">添加</div>
    <div data-options="iconCls:'icon-edit'">修改</div>
    <div data-options="iconCls:'icon-remove'">删除</div>
</div>
</body>
</html>

5.3.2 js模板

## 引入宏定义
$!define
## 使用宏定义设置回调(保存位置与文件后缀)
#save("/main/webapp/js/model", ".js")
## 设置文件名
$!callback.setFileName($tool.append($!{tableInfo.obj.name}, ".js"))
/**
 * User: $!author
 * Date: $!currTime
**/
//阻止浏览器原有的右键菜单弹出
function doNothing() {
    window.event.returnValue = false;
    return false;
}

function deptFormat(value, row, index) {
    return value ? value.name : "";
}

function imgFormat(value, row, index) {
    return `<img src='${value}' alt='无图片' width="50px" height="50px"/>`;
}

function showMenu(e, index, row) {
    //选中这个行
    $("#$!{tableInfo.obj.name}Grid").datagrid("selectRow", index);
    //第0个位置的面板不支持相应功能
    e.preventDefault();
    $('#gridMenu').menu('show', {
        left: e.pageX,
        top: e.pageY,
        onClick: function (item) {
            switch (item.text) {
                case "添加" : {
                    mymethod.add();
                }
                    ;
                    break;
                case "修改" : {
                    mymethod.update();
                }
                    ;
                    break;
                case "删除" : {
                    mymethod.del();
                }
                    ;
                    break;
            }
        }
    });
}

$(function () {

    /*为每一个带有data-method属性的a标签绑定click事件*/
    $("a[data-method]").on("click", function () {
        /*获取方法的名称*/
        let methodName = $(this).data("method");
        /*调用方法*/
        window.mymethod[methodName]();
    });

    /*拿到常用的组件*/
    let $!{tableInfo.obj.name}Grid = $("#$!{tableInfo.obj.name}Grid");
    let searchForm = $("#searchForm");
    let $!{tableInfo.obj.name}Dialog = $("#$!{tableInfo.obj.name}Dialog");
    let editForm = $("#editForm");

    /*方法的定义*/
    mymethod = {
        add() {
            $("*[data-edit]").show();
            $("*[data-edit] input").validatebox("enable");
            //先清空
            editForm.form("clear");
            $!{tableInfo.obj.name}Dialog.dialog("center").dialog("open");
        },
        del() {//多行删除
            /*let row = $!{tableInfo.obj.name}Grid.datagrid("getSelected");*/
            let rows = $!{tableInfo.obj.name}Grid.datagrid("getSelections");
            //如果用户没有选中行
            if (!rows) {
                $.messager.alert("警告", "请至少选中一行数据再删除!", "warning");
                return;
            } else {
                /*如果已经选中,提示是否确认进行删除操作*/
                $.messager.confirm('确认', `您确认想要删除这${rows.length}条记录吗?`, function (r) {
                    if (r) {
                        for (let i = 0; i < rows.length; i++) {
                            $.get('/$!{tableInfo.obj.name}/delete', {id: rows[i].id}, function (result) {
                                if (result.success) {
                                    $!{tableInfo.obj.name}Grid.datagrid("reload");
                                } else {
                                    $.messager.alert("错误", "删除失败,原因:+" + result.msg, "error")
                                }
                                //关闭对话框
                                mymethod.close();
                            });
                        }
                    }
                });
            }
        },
        save() {
            let url = "/$!{tableInfo.obj.name}/save";
            let $$!{tableInfo.obj.name}Id = $("#$!{tableInfo.obj.name}Id").val();
            if ($$!{tableInfo.obj.name}Id) {
                url = "/$!{tableInfo.obj.name}/update?cmd=_update";
            }
            editForm.form('submit', {
                url: url,
                //提交之前的操作
                onSubmit: function () {
                    return $(this).form('validate');
                },
                //提交完成后,接收后台返回的数据,并操作
                success: function (data) {
                    let result = JSON.parse(data);
                    if (result.success) {
                        //如果保存成功,就重新加载数据
                        $!{tableInfo.obj.name}Grid.datagrid("reload");
                    } else {
                        //没有保存成功的话,就提示用户
                        $.messager.alert('错误', `保存失败,原因是:${result.msg}`, 'error');
                    }
                    mymethod.close();
                }
            });
        },
        update() {
            let row = $!{tableInfo.obj.name}Grid.datagrid("getSelected");
            //如果用户没有选中行
            if (!row) {
                $.messager.alert("警告", "请选中一行数据再修改!", "warning");
                return;
            } else {
                editForm.form("clear");
                //禁用密码框,并让其失效
                $("*[data-edit]").hide();
                $("*[data-edit] input").validatebox("disable");
                //解决部门不能回显的问题
                if (row.department) {
                    //row对象增加一个department.id的属性
                    row['department.id'] = row.department.id;
                }
                //数据回显
                editForm.form("load", row);
                $!{tableInfo.obj.name}Dialog.dialog("center").dialog("open");
            }
        },
        search() {
            /**
             * serializeObject是easyui/plugin/jquery.jdirk.js下的方法,
             * 功能就是拿到一个form表单中的所有数据,(根据name属性)封装成json对象
             */
            let params = searchForm.serializeObject();
            $!{tableInfo.obj.name}Grid.datagrid("load", params);/*重新加载数据(重新发送请求)*/
        },
        close() {
            $!{tableInfo.obj.name}Dialog.dialog("close");
        }
    };

    /**
     * 双击单元格的时候,直接编辑该单元格的字段的内容
     */
    $!{tableInfo.obj.name}Grid.datagrid({
        onDblClickCell:function (index, field, value) {
            $!{tableInfo.obj.name}Grid.datagrid('beginEdit', index);
            let ed = $!{tableInfo.obj.name}Grid.datagrid('getEditor', {index:index,field:field});
            $(ed.target).focus();
        }
    });

    //绑定相应的事件 : 与右键菜单冲突
    $(window).keydown(function (event) {
        switch (event.keyCode) {
            case 46:
                mymethod.del();
                break;
            case 13:
                mymethod.update();
                break;
            case 9:
                mymethod.add();
                break;
        }
    });
    //绑定相应的事件 : 与右键菜单冲突
    /*$("body").bind('keydown', 'del', window.mymethod.del);
    $(document).bind('keydown', 'Shift+1', window.mymethod.add);
    $(document).bind('keydown', 'Shift+2', window.mymethod.update);*/
});

5.4 生成目标文件

当配置好模板后,点击面板右边的Database -> 选择对应的数据库 -> 选择表 -> 右键 -> EasyCode -> Generate Code -> 设置正确的package(包名),path(路径)然后选择Template(模板) -> 点击ok即可

5.5 导出模板

File -> Export settings ->只选择EasyCodeSetting导出即可。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章