EZDML|EZDML生成Erupt代码详解

Erupt是一个基于Spring boot注解的java框架,只需要写个实体类就能自动生成增删改查的基本功能,又可以自定义代码实现复杂逻辑,设计精巧功能强大得来又很灵活。我在生成Erupt过程中有时会想其实我也很久就想弄个类似的框架,可惜也就只是个想。
EZDML已经提供了生成Erupt工程代码的模板,按惯例在这里简单复盘一下我是如何实现这个生成过程的,仅供希望自己自定义控制Erupt生成过程的人参考。
EZDML|EZDML生成Erupt代码详解
文章图片

Erupt官方文档推荐用Spring Initializer,我们就按这个方式开始,重头来一次。
创建项目
前几节基本上是照抄erupt官网的《快速开始》,仅面向不熟悉的新手,老手可绕道。
打开start.spring.io,按下图创建项目:
EZDML|EZDML生成Erupt代码详解
文章图片

为方便演示,我采用了h2文件数据库,下载并解压:
EZDML|EZDML生成Erupt代码详解
文章图片

运行项目下的mvnw.cmd可以直接编译安装,不过为了方便演示,我们用idea打开:
EZDML|EZDML生成Erupt代码详解
文章图片

配置并编译运行
demo工程打开后,默认就能编译通过运行,但因为没有配置,运行没有任何结果。我们按官方文档的说明,在pom.xml中添加依赖包,并执行maven reload(这里一般会需要下载半天):
EZDML|EZDML生成Erupt代码详解
文章图片

然后将application.properties改名为application.yml进行配置,配置内容跟官方要求的稍有不同,因为官方示例是MYSQL,我这里要用h2数据库:

server: port: 8080 spring: datasource: url: jdbc:h2:./ezerupt_db driver-class-name: org.h2.Driver username: sa password: 1234jpa: database: h2 show-sql: true generate-ddl: true open-in-view: true

EZDML|EZDML生成Erupt代码详解
文章图片

再打开DemoApplication.java,加两个注解:
EZDML|EZDML生成Erupt代码详解
文章图片

编译运行:
EZDML|EZDML生成Erupt代码详解
文章图片

不出意外的话就可以打开浏览器访问http://localhost:8080/登录erupt了:

添加入门示例
接下来要搞个自己的功能菜单,官网有一个Simple的《入门示例》,我们就抄它了。
新建model子目录,新建Simple.java,将官网的代码粘贴过来,包名改一下,引用加一下,基本上就可以了:
EZDML|EZDML生成Erupt代码详解
文章图片

创建功能菜单
接下来稍有不同,官网是需要手工添加菜单项以便访问,这里我希望自动添加菜单,毕竟我们目标是自动生成代码,希望生成后用户能直接看到功能,不需要额外操作。
新建文件SimpleConfig.java,输入以下内容:
package com.ezdml.erupt.demo.model; import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Component; import xyz.erupt.core.annotation.EruptScan; import xyz.erupt.core.module.EruptModule; import xyz.erupt.core.module.EruptModuleInvoke; import xyz.erupt.core.module.MetaMenu; import xyz.erupt.core.module.ModuleInfo; import java.util.ArrayList; import java.util.List; @Configuration @ComponentScan @EntityScan @EruptScan @Component public class SimpleConfig implements EruptModule {static { EruptModuleInvoke.addEruptModule(SimpleConfig.class); }@Override public ModuleInfo info() { return ModuleInfo.builder().name("erupt_simple").build(); }@Override public List initMenus() { List menus = new ArrayList<>(); menus.add(MetaMenu.createRootMenu("$ezsimple", "简单测试", "fa fa-code", 40)); menus.add(MetaMenu.createEruptClassMenu(Simple.class, menus.get(0), 0)); return menus; } }

代码的意思很明显,就是添加一组菜单,点了直接进Simple功能:
EZDML|EZDML生成Erupt代码详解
文章图片

再次编译运行,这回菜单直接出来了:
EZDML|EZDML生成Erupt代码详解
文章图片

增删改查也正常:
EZDML|EZDML生成Erupt代码详解
文章图片

生成实体代码
EZDML从3.32版开始支持生成Erupt,最近又改进了一下,本文是基于EZDML for win64 v3.37版来操作的。
为了让大家对生成有点印象,接下来我们先用EZDML模拟生成一下这个Simple,打开EZDML,新建表,输入以下描述字:
Simple 简单的例子 ----------------- Id PKI input 文本 S number 数值 I bool 布尔 BO date 时间 D

EZDML|EZDML生成Erupt代码详解
文章图片

确定保存,结果大概是这样的:
EZDML|EZDML生成Erupt代码详解
文章图片

切到生成页,选中EruptEntity:
EZDML|EZDML生成Erupt代码详解
文章图片

这里已经能生成实体类了。由于EZDML认为simple这个词可能与系统保留字冲突,所以加了Ez前缀;其它的可以看出来跟上面的Simple.java类几乎是一样的。把这个类代替之前手敲的类是没有什么问题的,也就是说,生成单个的Erupt实体类已经没什么问题了,接下来可以做批量生成处理了。
等等,这个生成实体类的原理不用讲下吗?嗯,这个讲起来比较麻烦,简单讲一下,就是Templates\EruptEntity.js这个脚本文件生成的,我们打开看下:
EZDML|EZDML生成Erupt代码详解
文章图片

嗯,它包含了另一个文件(不想维护两个嘛),再打开包含的EzTable.java文件看看:
EZDML|EZDML生成Erupt代码详解
文章图片

这个java文件其实是一个js脚本,它的目的是根据当前指定的模型表curTable生成一个java文件。具体代码这里暂时就不展开了,有空再专门讲解;里面注释较全,程序员应该基本能看懂。
EZDML模板工程
接下来我们要把刚才那个工程当成EZDML的模板工程,我们把整个工程复制到EZDML的Templates模板目录下,并改名为erupt_demo(请假装没看到前面的Erupt目录):
EZDML|EZDML生成Erupt代码详解
文章图片

接下来删除.erupt、.idea、target等文件目录,作为模板工程,我们只需要保留最原始的文件:
EZDML|EZDML生成Erupt代码详解
文章图片

然后回到EZDML,执行代码生成,这时就会出现erupt_demo模板了:
EZDML|EZDML生成Erupt代码详解
文章图片

直接生成是可以的,但从日志可以看出,它只是把所有文件一字不差的复制过去:
EZDML|EZDML生成Erupt代码详解
文章图片

批量生成Erupt实体类
前面单独生成实体类是没什么问题的,但批量生成有特别的意义,就是能把表对象之间的关联关系和依赖文件也一块生成了。
我们打开模板文件目录,找到Simple.java,改名为EzTable.java:
EZDML|EZDML生成Erupt代码详解
文章图片

顺便把旁边那个改名为EzModelAutoConfig.java:
EZDML|EZDML生成Erupt代码详解
文章图片

接下来最重要的配置来了,在旁边再新建一个_dml_config.INI文本文件,内容如下:
[EzTable.java] rename=#curtable_name:ClassNameSafe#.java run_as_script=js loop_each_table=1[EzModelAutoConfig.java] rename=Erupt#curmodel_name:ClassName#AutoConfig.java run_as_script=js

EZDML|EZDML生成Erupt代码详解
文章图片

意思是告诉EZDML,生成代码时,要把这两个文件当成js脚本运行,其中EzTable.java要对所有表循环生成,并按设定的规则输出到指定文件名。
这时直接生成是不行的,因为这两个家伙并不是真正的js脚本:
EZDML|EZDML生成Erupt代码详解
文章图片

我们改一下这两文件,在头上加一小段内容,EZDML遇到里面的才认为是脚本,外面的内容会原样输出(表达式${xxx}之类的除外):
EZDML|EZDML生成Erupt代码详解
文章图片

EZDML|EZDML生成Erupt代码详解
文章图片

保存后再次生成,从日志可以看出,原来的复制过程发生了变化:
EZDML|EZDML生成Erupt代码详解
文章图片

光一个表看不出什么,我们加两个表:

再次生成时,会发现EzTable.java对每个表都生成了一次:
EZDML|EZDML生成Erupt代码详解
文章图片

打开目标文件夹,会看到确实是生成了,只不过,这三个文件的内容目前是一样的:
EZDML|EZDML生成Erupt代码详解
文章图片

接下来就是修改EzTable.java里的脚本,让它生成我们需要的正确的内容,这个我就不装了,直接把EZDML自带那个拷过来改了(基本上就是改一下包名就行了):
EZDML|EZDML生成Erupt代码详解
文章图片

改完再次生成,可以看到生成的java内容发生了变化:
EZDML|EZDML生成Erupt代码详解
文章图片

批量生成Erupt菜单
接着我们再改一下EzModelAutoConfig.java,让它生成正确的菜单项,这个大概改成这个样子:
package com.ezdml.erupt.demo.model; import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Component; import xyz.erupt.core.annotation.EruptScan; import xyz.erupt.core.module.EruptModule; import xyz.erupt.core.module.EruptModuleInvoke; import xyz.erupt.core.module.MetaMenu; import xyz.erupt.core.module.ModuleInfo; import java.util.ArrayList; import java.util.List; @Configuration @ComponentScan @EntityScan @EruptScan @Component public class Erupt${AutoCapProc(md.name,'ClassName')}AutoConfig implements EruptModule {static { EruptModuleInvoke.addEruptModule(Erupt${AutoCapProc(md.name,'ClassName')}AutoConfig.class); }@Override public ModuleInfo info() { return ModuleInfo.builder().name("erupt_${AutoCapProc(md.name,'PackageName')}").build(); }@Override public List initMenus() { List menus = new ArrayList<>(); menus.add(MetaMenu.createRootMenu("$ez${mPkg}", "${md.displayText}", "fa fa-code", 40)); menus.add(MetaMenu.createEruptClassMenu(${AutoCapProc(tb.name,'ClassNameSafe')}.class, menus.get(0), 0)); return menus; } }

EZDML|EZDML生成Erupt代码详解
文章图片

再次生成:
EZDML|EZDML生成Erupt代码详解
文章图片

如无意外,你会看到这个结果:
EZDML|EZDML生成Erupt代码详解
文章图片

编译运行
生成了以上结果后,是可以用idea或eclipse打开编译运行的。不过这里为了方便装X,我用命令行来执行,首先在生成目标目录打开命令行,运行mvnw install,下载依赖包并编译(嗯,当然JDK是要先有了):
EZDML|EZDML生成Erupt代码详解
文章图片

现在的编译工具,上来就一堆疯狂的下载,完全搞不懂它有没干坏事。反正最终如果人品好运气佳没出问题编译成功看到绿色的BUILD SUCCESS话就OK了:
EZDML|EZDML生成Erupt代码详解
文章图片

从日志可以看出它输出了一个demo-0.0.1-SNAPSHOT.jar到target目录:
EZDML|EZDML生成Erupt代码详解
文章图片

我们继续用命令运行这个jar:
java -jar target\demo-0.0.1-SNAPSHOT.jar

EZDML|EZDML生成Erupt代码详解
文章图片

运行没出错,我们在浏览器上访问一下,就能看到熟悉的登录页面了:
EZDML|EZDML生成Erupt代码详解
文章图片

说实话,我很讨厌这两个登录和改密码的页面,因为老是重复输入了无数次,想给它个默认值但又不知改哪里:
EZDML|EZDML生成Erupt代码详解
文章图片

终于,看到生成的结果页面了:

自动运行生成结果
我前面编译和运行都是用的命令行,只需要JDK,并不需要安装idea、eclipse之类的高级工具。因此,我们可以加多一个配置,让EZDML在生成后,自动提示编译运行。
回到erupt_demo模板目录,再新建一个_dml_config.INI文件,输入以下内容:
[dml_settings] auto_open_on_finished=ezstart.cmd

EZDML|EZDML生成Erupt代码详解
文章图片

意思就是生成完后要执行ezstart.cmd这个批处理命令。
接着当然就要创建ezstart.cmd文件了,输入内容如下:
@echo offcd /d %~dp0 cdecho 即将运行mvnw install... pause call mvnw installecho 即将运行java -jar target\demo-0.0.1-SNAPSHOT.jar... pause call java -jar target\demo-0.0.1-SNAPSHOT.jarecho 即将退出... pause

注意保存格式,我机上是要保存为PC回车换行的ANSI文本格式(有些编辑器会默认保存为Unix UTF8格式):
EZDML|EZDML生成Erupt代码详解
文章图片

这shell命令文件大概就是干了三件事:
  1. 跳转到ezstart.cmd文件所在目录(生成后就是生成目标文件夹目录了)
  2. 执行mvnw install,下载安装依赖并编译生成jar
  3. 调用java运行编译打包的结果jar
调用编译命令时加了call,是为了避免运行完自动关闭命令窗口。
然后我们再生成一下,就会看到命令行弹出了:
EZDML|EZDML生成Erupt代码详解
文章图片

按一下空格开始编译:
EZDML|EZDML生成Erupt代码详解
文章图片

编译成功:
EZDML|EZDML生成Erupt代码详解
文章图片

继续按任意键运行:
EZDML|EZDML生成Erupt代码详解
文章图片

这样我们就基本实现了一套生成Erupt的新轮子。
关联关系的生成
批量生成时,可以同时生成关联关系。以Table3为例,我们让它关联一下Simple:
EZDML|EZDML生成Erupt代码详解
文章图片

再次生成代码后,我们看一下生成的EzSimple.java,会发现后面有一段是关联的Table3的列表内容,只是默认注释掉了(因为脚本要求在主表中显式添加一个List对象才会正式输出子表):
EZDML|EZDML生成Erupt代码详解
文章图片

然后我们再看Table3.java,会发现生成了EzSimple关联外键rid的内容:
EZDML|EZDML生成Erupt代码详解
文章图片

编译运行,编辑Table3对象时,会有一个“关联简单例子”的列出来:
EZDML|EZDML生成Erupt代码详解
文章图片

点击弹出选择:
EZDML|EZDML生成Erupt代码详解
文章图片

非常不错,简单注解包打一切。当然复杂的业务逻辑还是得自己在这基本上再添砖加瓦。
生成测试数据
最后顺便演示下如何生成测试数据。默认生成的数据表是空的,我们填点东西进去。
首先我们要把Simple表改一下名,改成EzSimple,跟目标一样(因为生成Erupt时会对敏感词自动改名加Ez前缀):
EZDML|EZDML生成Erupt代码详解
文章图片

改名后生成的代码跟原来比应该是没有任何区别的,运行结果也没有任何变化:

然后我们关闭web服务,回到EZDML,执行生成数据库,选择HTTP_JDBC,点“数据源”右边的“配置”:
EZDML|EZDML生成Erupt代码详解
文章图片

在JDBC配置中输入以下内容,以便连接到Erupt的h2数据库:
@echo offset java_bin=java.exe set jdbc_driver=org.h2.Driver set jdbc_url=jdbc:h2:D:/temp/226/erupt_demo/ezerupt_db set jdbc_username=sa set jdbc_password=1234 set jdbc_engineType=H2 set http_password= set http_port=8083

EZDML|EZDML生成Erupt代码详解
文章图片

注意要提前把h2的驱动(可从这里下载)放到lib目录下:
EZDML|EZDML生成Erupt代码详解
文章图片

运行JDBC服务,成功的话大概是这个样子:
EZDML|EZDML生成Erupt代码详解
文章图片

回到EZDML连接:
EZDML|EZDML生成Erupt代码详解
文章图片

不过,连接成功后,我们发现生成的都是创建表SQL,貌似表不存在:

我们打开导入表界面看看:
EZDML|EZDML生成Erupt代码详解
文章图片

嗯,其实是存在的,只是大小写不一样。双击TABLE3看看,会发现不仅表名、字段名大写了,而且原先驼峰命名的字段名也变成了下划线分隔的:
EZDML|EZDML生成Erupt代码详解
文章图片

我们回到模型图,全选所有表,执行右键菜单命令“大小写转换|驼峰命名转下划线”,再执行“大小写转换|全部转成大写”:

执行完后大概是这样子的:
EZDML|EZDML生成Erupt代码详解
文章图片

这时再生成数据库,基本上就没有差异了:
EZDML|EZDML生成Erupt代码详解
文章图片

我们给Table3弄点看上去“真实点”的数据,双击Table3,切到界面页(如果默认没有显示“界面”则右上角下拉菜单呼出),打开配置面板,把NAME(名称)的数据生成类型设置为“rand_company_fullname_cn:随机公司名(全称,中文)”,把TYPE_NAME(类名)的数据生成类型设置为“industry_cn:行业(中文)”:
EZDML|EZDML生成Erupt代码详解
文章图片

回到主界面,执行菜单命令“模型|生成测试数据”。由于表结构仍有细微差别,直接生成会出错:

勾选“忽略数据库检查错误”,再次生成,先生成3条记录看看SQL:
EZDML|EZDML生成Erupt代码详解
文章图片

感觉没什么问题,生成30条:
EZDML|EZDML生成Erupt代码详解
文章图片

直接执行:
EZDML|EZDML生成Erupt代码详解
文章图片

执行完成,运气好,没出错:
EZDML|EZDML生成Erupt代码详解
文章图片

左边列表双击EZ_SIMPLE,可查看数据库中的表结构:
EZDML|EZDML生成Erupt代码详解
文章图片

切到数据页(如果默认没有显示“数据”则右上角下拉菜单呼出),可以看到确实生成了数据(前面两条是我之前测试输入的):
EZDML|EZDML生成Erupt代码详解
文章图片

选中一条记录,右键查看关联表TABLE3的相关记录:
EZDML|EZDML生成Erupt代码详解
文章图片

可以看到关联信息也有了(当然了,不是每条都有子记录,要拼人品):
EZDML|EZDML生成Erupt代码详解
文章图片

再次运行系统,可以看到确实有数据了:

外键也关联上了:
EZDML|EZDML生成Erupt代码详解
文章图片

一切正常:
EZDML|EZDML生成Erupt代码详解
文章图片

【EZDML|EZDML生成Erupt代码详解】OK,生成完毕,万里长征第一步完成了。

    推荐阅读