整个小东西,在IDEA中自动生成PO、DAO、Mapper
作者:小傅哥
博客:https://bugstack.cn
源码:https://github.com/fuzhengwei/CodeGuide/wiki
沉淀、分享、成长,让自己和他人都能有所收获!
一、前言
都能用,都能凑活用!
一个东西好几套,为了晋升都来搞。拿了成绩就要跑,后面兄弟再重造!
几年前,大家并不是这样,那时候还有很多东西可以创新,乱世出英雄总能在一个方向深耕并作出一款款好用的产品功能、框架服务、技术组件等。但后来好像这样的情况开始减少了,取而代之的是重复、复刻、照搬,换个新的皮肤、换个新的样式、换个新的名字,就是取巧的新东西了。
有时候公司或者组织也像家,但家里的东西一般是破了补补、坏了修修,实在不行就换个,但没有谁的家里卫生间一个马桶、厨房一个马桶、客厅一个马桶、卧室一个马桶的,虽然你的新马桶可以自动喷水。
所以,在建设一个好的产品功能时,尽可能要学学那些已经非常的优秀的产品,IDEA、GitHub、Mysql等等,在IDEA提供了满足用户扩展功能的插件开发,而不是你说一个东西我没有,你就自己造。共建会让这个东西变得更加优秀!
二、需求目的
在上一章节中我们通过扩展创建工程向导,添加我们需要创建DDD工程脚手架的步骤,最终提供一个DDD开发框架。那么在这个DDD工程开发框架中,还缺少一部分基于数据库表信息自动生成对应PO、DAO、Mapper文件的功能。
- 那么本章节我们就来在工程中扩展这部分内容,实际操作的效果就是我们可以在工程上通过鼠标右键的方式,唤出添加ORM代码块的窗体,通过选择库表的方式,使用 freemarker 自动生成代码。
- 在生成的代码块中需要完成对所需要包的引入,同时会使用到 lombok 注解的方式替代PO对象中的get、set方法,以减少代码量逻辑的创建。
三、案例开发
1. 工程结构
guide-idea-plugin-orm
├── .gradle
└── src
├── main
│ └── java
│ └── cn.bugstack.guide.idea.plugin
│ ├── action
│ │ └── CodeGenerateAction.java
│ ├── domain
│ │ ├── model.vo
│ │ │ ├── CodeGenContextVO.java
│ │ │ └── ORMConfigVO.java
│ │ └── service
│ │ ├── impl
│ │ │ └── ProjectGeneratorImpl.java
│ │ ├── AbstractProjectGenerator.java
│ │ ├── GeneratorConfig.java
│ │ └── IProjectGenerator.java
│ ├── infrastructure
│ │ ├── data
│ │ │ ├── DataSetting.java
│ │ │ └── DataState.java
│ │ ├── po
│ │ │ ├── Base.java
│ │ │ ├── Column.java
│ │ │ ├── Dao.java
│ │ │ ├── Field.java
│ │ │ ├── Model.java
│ │ │ └── Table.java
│ │ └── utils
│ │ ├── DBHelper.java
│ │ └── JavaType.java
│ ├── module
│ │ └── FileChooserComponent.java
│ └── ui
│ ├── ORMSettingsUI.java
│ └── ORMSettingsUI.form
├── resources
│ ├── META-INF
│ │ └── plugin.xml
│ └── template
│ ├── dao.ftl
│ ├── mapper.ftl
│ └── model.ftl
├── build.gradle
└── gradle.properties
源码获取:#公众号:bugstack虫洞栈
回复:idea
即可下载全部 IDEA 插件开发源码# 第 5 章:在开发工程中鼠标右键,生成ORM代码
在此 IDEA 插件工程中,主要分为5块区域:
- action:用于提供菜单栏,这个菜单的位置在 plugin.xml 中配置,我们把它配置到工程鼠标右键出现的列表上。这样可以更加方便的让我们选取工程,以及在这个工程下添加生成的代码片段
- domain:领域服务层,其实你直接写一个Service包也是可以的,只不过最近作者小傅哥更喜欢使用DDD的思想和结构来创建代码实现功能逻辑。
- infrastructure:基础层,提供数据在工程下的存放,每一个工程右键都有自己的配置存储默认信息,方便下次打开的时候可以读取到这部分内容。同时这一层还提供了用于处理数据库操作的类,因为我们需要从数据库中读取出表的信息、字段、注释,用于创建PO、DAO、Mapper使用。
- module:模块层,这里提供了一个用于选择文件路径的组件,可以让我们在工程上鼠标右键的出来的窗体中,点击模块选择对应的要生成代码的位置路径。
- ui:提供配置面板,也就是我们在代码工程上鼠标右键弹出来的面板,这个面板配置后用于生成ORM代码。
2. 拖拽Swing面板
ORMSettingsUI:咱们先把用于创建代码配置的面板创建出来,有了画面,就好进入了。
- 面板包括生成 PO、DAO、XML 的代码路径,以及配置数据库和选择表的内容。
- 操作过程就是在你配置好了这些基本信息后,就可以选择查询表名,并选择好你要给哪几个表生成对应的ORM代码了。
3. 配置鼠标右键弹窗
首先我们需要创建一个 Action 实现类,通过 New -> Plugin DevKit -> Action
cn.bugstack.guide.idea.plugin.action.CodeGenerateAction
/**
* @author: 小傅哥,微信:fustack
* @github: https://github.com/fuzhengwei
* @Copyright: 公众号:bugstack虫洞栈 | 博客:https://bugstack.cn - 沉淀、分享、成长,让自己和他人都能有所收获!
*/
public class CodeGenerateAction extends AnAction {
private IProjectGenerator projectGenerator = new ProjectGeneratorImpl();
@Override
public void actionPerformed(AnActionEvent e) {
Project project = e.getRequiredData(CommonDataKeys.PROJECT);
ShowSettingsUtil.getInstance().editConfigurable(project, new ORMSettingsUI(project, projectGenerator));
}
}
- 这是一个右键菜单的入口,通过这个入口才能去打开我们自己的UI窗体,这个UI窗体就是我们上面拖拽出来的配置面板,ORMSettingsUI
- 接下来我们还需要把这个 Action 配置到 plugin.xml 文件中,才能被右键菜单创建出来。开发代码的时候也是这样一个流程,你总要从一个点开始,有了抓手才好抓下去
plugin.xml 配置
<actions>
<!-- Add your actions here -->
<action id="CodeGenerateAction" class="cn.bugstack.guide.idea.plugin.action.CodeGenerateAction"
text="ORMCodeGenerate - 小傅哥" description="Code Generate ORM" icon="/icons/logo.png">
<add-to-group group-id="ProjectViewPopupMenu" anchor="last"/>
</action>
</actions>
ea-plugin>
- 把我们的 Action 实现类配置到 xml 中,同时你还要配置它应该出现的位置,比如你需要把这个菜单添加到工程创建中
ProjectViewPopupMenu
以及位置信息anchor="last"
- 另外为了让插件看上去更加高大上还美观适合吹牛,那么还需要配置 icon,这个位置配置一个
16*16
的图片,图片可以从 iconfont 进行下载。
4. 给窗体添加功能
这一步其实干的就是注入灵魂的事情,让窗体活起来。给输入框添加内容、给按钮添加事件、给确认按钮增加上生成创建ORM代码块的上下文。文章的描述尽可能会偏向于核心代码的讲解,详情可以参考源码
接下来这部分内容会在 ORMSettingsUI 类中反复摩擦,直到补全所有功能。
4.1 选择框事件
// 选择PO生成目录
this.poButton.addActionListener(e -> {
FileChooserComponent component = FileChooserComponent.getInstance(project);
VirtualFile baseDir = project.getBaseDir();
VirtualFile virtualFile = component.showFolderSelectionDialog("选择PO生成目录", baseDir, baseDir);
if (null != virtualFile) {
ORMSettingsUI.this.poPath.setText(virtualFile.getPath());
}
});
- 还记得我们提到的
拖拽Swing面板
吗,那么这个添加事件的步骤就是给你的 PO 目录添加一个事件,允许我们可以自己选择出要把对应PO的代码生成到哪个目录结构下。 - 关于dao、xml都是类似操作,这里就不在演示了。
4.2 数据表事件
this.selectButton.addActionListener(e -> {
try {
DBHelper dbHelper = new DBHelper(this.host.getText(), Integer.parseInt(this.port.getText()), this.user.getText(), this.password.getText(), this.database.getText());
List<String> tableList = dbHelper.getAllTableName(this.database.getText());
String[] title = {"", "表名"};
Object[][] data = new Object[tableList.size()][2];
for (int i = 0; i < tableList.size(); i++) {
data[i][1] = tableList.get(i);
}
table1.setModel(new DefaultTableModel(data, title));
TableColumn tc = table1.getColumnModel().getColumn(0);
tc.setCellEditor(new DefaultCellEditor(new JCheckBox()));
tc.setCellEditor(table1.getDefaultEditor(Boolean.class));
tc.setCellRenderer(table1.getDefaultRenderer(Boolean.class));
tc.setMaxWidth(100);
} catch (Exception exception) {
Messages.showWarningDialog(project, "数据库连接错误,请检查配置.", "Warning");
}
});
- 这一步操作核心流程就在于把你需要生成ORM的代码的表给拉出来,只要把表选择上,才能根据表生成PO、DAO、Mapper,其实你用的其他一些自动生成代码框架也是这么干的。
- 另外你的建表最好规范,比如有表注释、有字段注释、字段的设计遵守下划线和小写字母,这样会更加容易创建出好看的代码。
4.3 组装生成代码上下文
当我们点击配置窗体的 OK 按钮时候,要干啥,对喽,我们要创建出代码片段了,那么这个时候需要在重写的 apply
中完成此项操作。
public void apply() {
// 链接DB
DBHelper dbHelper = new DBHelper(config.getHost(), Integer.parseInt(config.getPort()), config.getUser(), config.getPassword(), config.getDatabase());
// 组装代码生产上下文
CodeGenContextVO codeGenContext = new CodeGenContextVO();
codeGenContext.setModelPackage(config.getPoPath() + "/po/");
codeGenContext.setDaoPackage(config.getDaoPath() + "/dao/");
codeGenContext.setMapperDir(config.getXmlPath() + "/mapper/");
List<Table> tables = new ArrayList<>();
Set<String> tableNames = config.getTableNames();
for (String tableName : tableNames) {
tables.add(dbHelper.getTable(tableName));
}
codeGenContext.setTables(tables);
// 生成代码
projectGenerator.generation(project, codeGenContext);
}
- 在 apply 中的核心代码主要就是使用 DBHelper 数据操作工具获取到对应的库下链接信息,同时把选择的号的表创建出用于生成代码类的参数,比如;类的名称、字段名称、注释名称等。
- 最后就是调用生成代码的服务了,
projectGenerator.generation(project, codeGenContext);
这一部分就是在我们领域服务 domain 中实现的。
5. 代码生成领域服务
- 用于创建PO、DAO、Mapper的代码块的代码主要是这里实现的,核心在于提供了一个抽象类以及对应的实现类,因为处理代码生成需要使用到 freemarker 所以就在抽象类里包装了下,这样可以免去实现类中还需要关心这部分逻辑。
ProjectGeneratorImpl 生成代码
@Override
protected void generateORM(Project project, CodeGenContextVO codeGenContext) {
List<Table> tables = codeGenContext.getTables();
for (Table table : tables) {
List<Column> columns = table.getColumns();
List<Field> fields = new ArrayList<>();
for (Column column : columns) {
Field field = new Field(column.getComment(), JavaType.convertType(column.getType()), column.getName());
field.setId(column.isId());
fields.add(field);
}
// 生成PO
Model model = new Model(table.getComment(), codeGenContext.getModelPackage() + CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, table.getName()), table.getName(), fields);
writeFile(project, codeGenContext.getModelPackage(), model.getSimpleName() + ".java", "domain/orm/model.ftl", model);
// 生成DAO
Dao dao = new Dao(table.getComment(), codeGenContext.getDaoPackage() + "I" + CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, table.getName()) + "Dao", model);
writeFile(project, codeGenContext.getDaoPackage(), dao.getSimpleName() + ".java", "domain/orm/dao.ftl", dao);
// 生成Mapper
writeFile(project, codeGenContext.getMapperDir(), dao.getModel().getSimpleName() + "Mapper.xml", "domain/orm/mapper.ftl", dao);
}
}
- 创建代码的过程就比较简单了,通过循环提取出来的表信息,映射成对应的类和属性以及注释,再使用 resources 下的 ftl 文件创建出对应的类和xml配置文件就可以了。
- 如果你还需要生成起来代码片段或者创建调用一些常用的组件,也是可以通过这样的方式进行实现的。
四、测试验证
- 点击
Plugin
启动 IDEA 插件,之后在工程右键如下:
1. 鼠标右键,选择菜单
2. 配置页面,配置信息
3. 自动创建,生成代码
- 好了,选择代码块就这么嗖的创建了出来,是不是非常方便,而且可以满足你在任何时候的把新的库表代码补充进来,减少了手敲CRUD操作。
五、总结
- 本章节小傅哥带着你又在 IDEA DDD 插件生成工程的结构下,又完善了一步生成ORM代码,当然你也可以在创建工程向导中添加生成ORM代码的步骤。而在工程下创建ORM的方式可以当做是对脚手架工程的补充,满足不同场景下的需求。
- 此外在 IDEA 插件开发的系列内容中我们是不断的尝试使用新的方式完善不同的功能点,如果你需要开发一个完整的插件那么可以结合这些功能一起来开发你的需求。
- 插件开发中还是有很多的内容需要了解和学习的,同时也要注意一些代码实现细节,例如我们本章节中的数据保存是在一个什么维度,是IDEA开发工具维度,还是在IDEA中的工程维度,这些是有区别。比如你的不同工程,是不需要保存同一份配置的
六、系列推荐
- 开发一个可以在IDEA中摸鱼看书的插件
- 方案设计:基于IDEA插件开发和字节码插桩技术,实现研发交付质量自动分析
- 技术扫盲:关于低代码编程的可持续性交付设计和分析
- Lottery 抽奖系统 - 基于领域驱动设计的四层架构的互联网分布式开发实践
- 《SpringBoot 中间件设计和开发》| 对,年底教你造火箭!
整个小东西,在IDEA中自动生成PO、DAO、Mapper的更多相关文章
- MyBatis 使用Generator自动生成Model , Dao, mapper
最近 我新建了一 个maven 项目,使用的是spring + springmvc + mybatis框架. 听说Mybatis可以自动生成model和mapper以及dao层,我就从网上查了查资 ...
- 懒人小工具1:winform自动生成Model,Insert,Select,Delete以及导出Excel的方法
懒人小工具2:T4自动生成Model,Insert,Select,Delete以及导出Excel的方法 github地址:https://github.com/Jimmey-Jiang/J ...
- 在 Linux 中自动生成 Cordova/Phonegap for Android 的 APK 安装程序
在 Linux 中自动生成 Cordova/Phonegap for Android 的 APK 安装程序 本贴首发于: http://xuekaiyuan.com/forum.php?mod=vie ...
- IntelliJ IDEA 中自动生成 serialVersionUID 的方法
as, idea plugin中搜如下关键字,并安装该插件: GenerateSerialVersionUID 如上图所示,创建一个类并实现Serializable接口,然后按alt+Enter键,即 ...
- 在PowerDesigner中自动生成sqlserver字段备注
在PowerDesigner中自动生成sqlserver字段备注 PowerDesigner是数据库设计人员常用的设计工具,但其自生默认生成的代码并不会生成sqlserver数据库的字段备注说明.在生 ...
- 二十四、详述 IntelliJ IDEA 中自动生成 serialVersionUID 的方法
当我们用 IntelliJ IDEA 编写类并实现 Serializable(序列化)接口的时候,可能会遇到这样一个问题,那就是: 无法自动生成serialVersionUID. 而serialVer ...
- eclipse中自动生成注释
eclipse中自动生成注释 包前缀设置的地方 注释模板设置的地方 Eclipse自动生成方法注释 快捷键 自动生成方法的注释格式,例如 /*** @param str* @return* @thro ...
- Eclipse中自动生成get/set时携带注释给get/set
Eclipse中自动生成get/set时携带注释给get/set 编码的时候通常要用到 JavaBean ,而在我们经常把注释写在字段上面,但生成的Get/Set方法不会生成,通过修改Eclips ...
- 从JSON中自动生成对应的对象模型
编程的乐趣和挑战之一,就是将体力活自动化,使效率成十倍百倍的增长. 需求 做一个项目,需要返回一个很大的 JSON 串,有很多很多很多字段,有好几层嵌套.前端同学给了一个 JSON 串,需要从这个 J ...
- 使用MyBatis Generator自动生成实体、mapper和dao层
原文链接 通过MyBatis Generator可以自动生成实体.mapper和dao层,记录一下怎么用的. 主要步骤: 关于mybatis从数据库反向生成实体.DAO.mapper: 参考文章:ht ...
随机推荐
- HDU--1166--单点更新
敌兵布阵 HDU - 1166 多组输入,注意清除tr数组 维护一个前缀数组,耗时有点大 #include <cstdio> #include <cstring> using ...
- ZOJ 3537 Cake (凸包 + 区间DP && 最优三角形剖分)
题目链接:Here 题意: 给定 \(n\) 个点的坐标,先问这些点能否组成一个凸包,如是凸包,问用不相交的线来切这个凸包使得凸包只由三角形组成,根据 \(cost_{i, j} = |x_i + ...
- proxy代理
- Vue插件—vant当中van-list的使用
https://www.cnblogs.com/xbxxf/p/12889843.html 注意:父级元素不能加overflow:auto: 1 getPendingWorkList() { 2 co ...
- GoLang 指针初探
1. 内置类型和引用类型 Go 中内置类型包括数值类型,字符串类型和布尔类型.引用类型包括切片,映射,通道,接口和函数类型.其中,引用类型表示创建的变量包含一个指向底层数据结构的指针和一组管理底层数据 ...
- 【TouchGFX】使用CubeMX创建touchgfx项目 -- 初始篇
1.系统构成,黑色块表示组件非必须 2.环境准备 CubeMX:6.0.1 touchgfx:4.15.0 rt-thread:2020-8-14 commit Keil:5.30 board:stm ...
- 百度网盘(百度云)SVIP超级会员共享账号每日更新(2023.11.22)
一.百度网盘SVIP超级会员共享账号 可能很多人不懂这个共享账号是什么意思,小编在这里给大家做一下解答. 我们多知道百度网盘很大的用处就是类似U盘,不同的人把文件上传到百度网盘,别人可以直接下载,避免 ...
- [转帖]nginx 日志打印响应时间 request_time 和 upstream_response_time
https://www.cnblogs.com/chooperman/p/14722450.html 设置log_format,添加request_time,$upstream_response_ti ...
- [转帖]设置kafka 数据保留时间
https://www.cnblogs.com/gao88/p/12539112.html kafka 单独设置某个topic的数据过期时间kafka 默认存放7天的临时数据,如果遇到磁盘空间小,存放 ...
- [转帖]Jmeter之JDBC Request使用方法(oracle)
https://zhuanlan.zhihu.com/p/121747788 JDBC Request: 这个sampler可以向数据库发送一个jdbc请求(sql语句),它经常需要和JDBC Con ...