EZDML 数据建模

超轻量级的免费数据建模工具

0%

EZDML生成Erupt代码详解

Erupt是一个基于Spring boot注解的java框架,只需要写个实体类就能自动生成增删改查的基本功能,又可以自定义代码实现复杂逻辑,设计精巧功能强大得来又很灵活。我在生成Erupt过程中有时会想其实我也很久就想弄个类似的框架,可惜也就只是个想。

EZDML已经提供了生成Erupt工程代码的模板,按惯例在这里简单复盘一下我是如何实现这个生成过程的,仅供希望自己自定义控制Erupt生成过程的人参考。

Erupt官方文档推荐用Spring Initializer,我们就按这个方式开始,重头来一次。

创建项目

前几节基本上是照抄erupt官网的《快速开始》,仅面向不熟悉的新手,老手可绕道。

打开start.spring.io,按下图创建项目:

为方便演示,我采用了h2文件数据库,下载并解压:

运行项目下的mvnw.cmd可以直接编译安装,不过为了方便演示,我们用idea打开:

配置并编译运行

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
server:
port: 8080
spring:
datasource:
url: jdbc:h2:./ezerupt_db
driver-class-name: org.h2.Driver
username: sa
password: 1234

jpa:
database: h2
show-sql: true
generate-ddl: true
open-in-view: true

再打开DemoApplication.java,加两个注解:

编译运行:

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

添加入门示例

接下来要搞个自己的功能菜单,官网有一个Simple的《入门示例》,我们就抄它了。

新建model子目录,新建Simple.java,将官网的代码粘贴过来,包名改一下,引用加一下,基本上就可以了:

创建功能菜单

接下来稍有不同,官网是需要手工添加菜单项以便访问,这里我希望自动添加菜单,毕竟我们目标是自动生成代码,希望生成后用户能直接看到功能,不需要额外操作。

新建文件SimpleConfig.java,输入以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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<MetaMenu> initMenus() {
List<MetaMenu> 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从3.32版开始支持生成Erupt,最近又改进了一下,本文是基于EZDML for win64 v3.37版来操作的。

为了让大家对生成有点印象,接下来我们先用EZDML模拟生成一下这个Simple,打开EZDML,新建表,输入以下描述字:

1
2
3
4
5
6
7
Simple 简单的例子 
-----------------
Id PKI
input 文本 S
number 数值 I
bool 布尔 BO
date 时间 D

确定保存,结果大概是这样的:

切到生成页,选中EruptEntity:

这里已经能生成实体类了。由于EZDML认为simple这个词可能与系统保留字冲突,所以加了Ez前缀;其它的可以看出来跟上面的Simple.java类几乎是一样的。把这个类代替之前手敲的类是没有什么问题的,也就是说,生成单个的Erupt实体类已经没什么问题了,接下来可以做批量生成处理了。

等等,这个生成实体类的原理不用讲下吗?嗯,这个讲起来比较麻烦,简单讲一下,就是Templates\EruptEntity.js这个脚本文件生成的,我们打开看下:

嗯,它包含了另一个文件(不想维护两个嘛),再打开包含的EzTable.java文件看看:

这个java文件其实是一个js脚本,它的目的是根据当前指定的模型表curTable生成一个java文件。具体代码这里暂时就不展开了,有空再专门讲解;里面注释较全,程序员应该基本能看懂。

EZDML模板工程

接下来我们要把刚才那个工程当成EZDML的模板工程,我们把整个工程复制到EZDML的Templates模板目录下,并改名为erupt_demo(请假装没看到前面的Erupt目录):

接下来删除.erupt、.idea、target等文件目录,作为模板工程,我们只需要保留最原始的文件:

然后回到EZDML,执行代码生成,这时就会出现erupt_demo模板了:

直接生成是可以的,但从日志可以看出,它只是把所有文件一字不差的复制过去:

批量生成Erupt实体类

前面单独生成实体类是没什么问题的,但批量生成有特别的意义,就是能把表对象之间的关联关系和依赖文件也一块生成了。

我们打开模板文件目录,找到Simple.java,改名为EzTable.java:

顺便把旁边那个改名为EzModelAutoConfig.java:

接下来最重要的配置来了,在旁边再新建一个_dml_config.INI文本文件,内容如下:

1
2
3
4
5
6
7
8
[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,生成代码时,要把这两个文件当成js脚本运行,其中EzTable.java要对所有表循环生成,并按设定的规则输出到指定文件名。

这时直接生成是不行的,因为这两个家伙并不是真正的js脚本:

我们改一下这两文件,在头上加一小段 <% %> 内容,EZDML遇到<%开头的内容,就会自动当成模板脚本处理,也就是说,<% %>里面的才认为是脚本,外面的内容会原样输出(表达式${xxx}之类的除外):

保存后再次生成,从日志可以看出,原来的复制过程发生了变化:

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

再次生成时,会发现EzTable.java对每个表都生成了一次:

打开目标文件夹,会看到确实是生成了,只不过,这三个文件的内容目前是一样的:

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

改完再次生成,可以看到生成的java内容发生了变化:

批量生成Erupt菜单

接着我们再改一下EzModelAutoConfig.java,让它生成正确的菜单项,这个大概改成这个样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<%
//测试js模板

var md=curModel;
var mPkg=AutoCapProc(md.name,'JavaPackageName');

var parentPkgName=GetGParamValue('EZGEN_ROOT_PKGNAME'); //上级包名
if(!parentPkgName)parentPkgName='com.ezdml.erupt.demo';

%>
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<MetaMenu> initMenus() {
List<MetaMenu> menus = new ArrayList<>();
menus.add(MetaMenu.createRootMenu("$ez${mPkg}", "${md.displayText}", "fa fa-code", 40));
<%
for(var j=0; j<md.tables.count; j++)
{
var tb = md.tables.getItem(j);
if(!tb.isChecked)
continue;
%>
menus.add(MetaMenu.createEruptClassMenu(${AutoCapProc(tb.name,'ClassNameSafe')}.class, menus.get(0), 0));
<%
}
%>
return menus;
}
}

再次生成:

如无意外,你会看到这个结果:

编译运行

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

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

从日志可以看出它输出了一个demo-0.0.1-SNAPSHOT.jar到target目录:

我们继续用命令运行这个jar:

1
java -jar target\demo-0.0.1-SNAPSHOT.jar

运行没出错,我们在浏览器上访问一下,就能看到熟悉的登录页面了:

说实话,我很讨厌这两个登录和改密码的页面,因为老是重复输入了无数次,想给它个默认值但又不知改哪里:

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

自动运行生成结果

我前面编译和运行都是用的命令行,只需要JDK,并不需要安装idea、eclipse之类的高级工具。因此,我们可以加多一个配置,让EZDML在生成后,自动提示编译运行。

回到erupt_demo模板目录,再新建一个_dml_config.INI文件,输入以下内容:

1
2
[dml_settings]
auto_open_on_finished=ezstart.cmd

意思就是生成完后要执行ezstart.cmd这个批处理命令。

接着当然就要创建ezstart.cmd文件了,输入内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@echo off

cd /d %~dp0
cd

echo 即将运行mvnw install...
pause
call mvnw install

echo 即将运行java -jar target\demo-0.0.1-SNAPSHOT.jar...
pause
call java -jar target\demo-0.0.1-SNAPSHOT.jar


echo 即将退出...
pause

注意保存格式,我机上是要保存为PC回车换行的ANSI文本格式(有些编辑器会默认保存为Unix UTF8格式):

这shell命令文件大概就是干了三件事:

  1. 跳转到ezstart.cmd文件所在目录(生成后就是生成目标文件夹目录了)

  2. 执行mvnw install,下载安装依赖并编译生成jar

  3. 调用java运行编译打包的结果jar

调用编译命令时加了call,是为了避免运行完自动关闭命令窗口。

然后我们再生成一下,就会看到命令行弹出了:

按一下空格开始编译:

编译成功:

继续按任意键运行:

这样我们就基本实现了一套生成Erupt的新轮子。

关联关系的生成

批量生成时,可以同时生成关联关系。以Table3为例,我们让它关联一下Simple:

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

然后我们再看Table3.java,会发现生成了EzSimple关联外键rid的内容:

编译运行,编辑Table3对象时,会有一个“关联简单例子”的列出来:

点击弹出选择:

非常不错,简单注解包打一切。当然复杂的业务逻辑还是得自己在这基本上再添砖加瓦。

生成测试数据

最后顺便演示下如何生成测试数据。默认生成的数据表是空的,我们填点东西进去。

首先我们要把Simple表改一下名,改成EzSimple,跟目标一样(因为生成Erupt时会对敏感词自动改名加Ez前缀):

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

然后我们关闭web服务,回到EZDML,执行生成数据库,选择HTTP_JDBC,点“数据源”右边的“配置”:

在JDBC配置中输入以下内容,以便连接到Erupt的h2数据库:

1
2
3
4
5
6
7
8
9
10
11
@echo off

set 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

注意要提前把h2的驱动(可从这里下载)放到lib目录下:

运行JDBC服务,成功的话大概是这个样子:

回到EZDML连接:

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

我们打开导入表界面看看:

嗯,其实是存在的,只是大小写不一样。双击TABLE3看看,会发现不仅表名、字段名大写了,而且原先驼峰命名的字段名也变成了下划线分隔的:

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

执行完后大概是这样子的:

这时再生成数据库,基本上就没有差异了:

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

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

勾选“忽略数据库检查错误”,再次生成,先生成3条记录看看SQL:

感觉没什么问题,生成30条:

直接执行:

执行完成,运气好,没出错:

左边列表双击EZ_SIMPLE,可查看数据库中的表结构:

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

选中一条记录,右键查看关联表TABLE3的相关记录:

可以看到关联信息也有了(当然了,不是每条都有子记录,要拼人品):

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

外键也关联上了:

一切正常:

OK,生成完毕,万里长征第一步完成了。