多说两句
开场白,很久都没有写博客了。。。 因为最近都在深入的去看elasticsearch,希望之后会有时间写一下关于elasticsearch的笔记。最近发现开发过程当中,我会不经意的去想写一些小东西提高整个开发效率,例如我写了给予AOP的Service方法级别的权限校验框架,给予AOP写的全局方法级别的参数校验框架等。当然,我也希望以后会有机会把这些框架贡献出来让大家参考和吐槽,但是这两天也写了一个比较有意思的东西,一款intellij IDE的插件。
由于我开发的大部分项目都使用了Liquibase,所以我需要经常去编写建表和添加字段的DDL语句。这个让我觉得相当麻烦,而且浪费时间,我已经定义好了JPA Entity了我就不想再做个翻译员把这些东西翻译成建表的DDL语句,特别是一些字段名还有描述,明明已经在entity里面定义写好注释了,还要ctl C + ctl V 拷贝到SQL的文件当中,进行持续化。哎 心累···
第一时间我是想马上百度去搜索获得类似这种的插件的,事实上找不到,我在百度和Google都尝试找了一下 也没有什么发现。所以我就想自己写一个吧。由于我也是刚刚开始去学习写这种插件,所以很多API可能用得不太6 可能有很多非常方便的API我也没有用上,有小伙伴看见我的代码里面有比较笨的地方,欢迎在评论里面写写你的方法,让我学习学习。边学边做的东西不出BUG我也就很开心了,没有什么要求。哈哈哈
一、插件使用展示
考虑到可能有部分人和我一样,一开始想找这个功能的插件去使用,没有打算去学习如何去开发插件的同学(本来如果找到有我也没有打算去写)。我这里就展示一下我的DDLCreator是如何使用的(插件安装请看后面)
请忽略辣鸡的表结构,事实上已经完成我初衷的想法,通过Entity对象直接生成SQL 的 DDL语句。目前已经支持大部分的JPA的annotation识别,但是是否完善我还需要在实际使用过程之中发现不足。但是基本是可用的,如果你需要这个功能,而且也不嫌弃的话可以通过以下链接下载并根据第二节内容进行安装。
下载地址:https://plugins.jetbrains.com/plugin/11271-ddlcreator
但是事实上我看到官方对我的JAR进行验证的时候,发现我使用了一些新的API导致旧版本intellij IDE不兼容的问题。这些我会后续进行修改,但是我测试的2018.3 intellij的版本是不存在问题的【测试了一下 2018.1以下都不兼容,包含2018.1 后续慢慢兼容,因为我有两三台电脑还是2017版本的,迫不得已想用就升级IDE吧】。官方的测试也是通过的,以下是关于插件的官方测试结果:
二、安装DDLCreator
下载好JAR之后,我们通过setting的plugin进行安装和其他的插件安装方式一样,不过需要通过本地jar的形式安装而已。
三、创建intellij plugin项目
先看个截图
Project SDK 就是intellij IDE的SDK 这个SDK就是相应的intellij IDE所提供的API,SDK版本越高API就越新,写android的同学应该很清楚了,我就这样入坑了选择了最高的SDK然后在2018.1版本以下的intellij 根本无法支持。所以说下次写还是搞个2016的版本比较靠谱
需要支持的framework自己要需要就选就可以了。
四、创建intellij plugin 项目结构:
其中resources目录下有一份plugin.xml 文件,这份XML文件主要配置关于这个插件的信息
<idea-plugin>
<id>org.tonyyan.plugin.ddlcreator</id>
<name>DDLCreator</name>
<version>1.2</version>
<vendor email="308001970@qq.com" url="https://me.csdn.net/tony308001970">TONYYAN</vendor>
<description><![CDATA[
通过JPA Entity 生成数据库 建表DDL语句
]]></description>
<change-notes><![CDATA[
]]>
</change-notes>
<!-- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/build_number_ranges.html for description -->
<idea-version since-build="173.0"/>
<!-- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/plugin_compatibility.html
on how to target different products -->
<!-- uncomment to enable plugin in all products
<depends>com.intellij.modules.lang</depends>
-->
<extensions defaultExtensionNs="com.intellij">
<!-- Add your extensions here -->
</extensions>
<actions>
<!-- Add your actions here -->
<action id="org.tonyyan.plugin.ddlcreator.CreateTableDLL" class="org.tonyyan.plugin.ddlcreator.CreateTableDDL" text="CreateTable DDL"
description="Create Table DDL" popup="false">
<!--<keyboard-shortcut keymap="$default" first-keystroke="shift alt C"/>-->
<add-to-group group-id="GenerateGroup" anchor="last"/>
</action>
</actions>
</idea-plugin>
- version:插件的版本号
- vendor:供应商信息
- description:插件信息描述
- change-notes:版本修改记录
- actions:这个就是一些插件的功能,每个功能用一个action,这里我写了一个action CreateTableDLL【我打错了本来想写DDL来着】这个是全局唯一的ID 是用来定位到你的action的。class属性标识action的实现类、description属性是action的描述、popup 是否需要弹出式菜单
- add-to-group:这个功能添加到GenerateGroup 菜单里面,就是我们平时打开 getter setter的菜单 anchor属性代表功能的位置为最后
其实创建一个Action也可以通过GUI的形式去创建:
一毛一样的东西 GUI都可以实现,其实我也是用GUI来创建action的~
四、AnAction实现
首先先贴出最关键的两个类第一个是继承AnAction类的Suport类,主要实现主要的核心功能,如分析字段,字段信息对象转换等等。第二个要贴出的是Support类的子类,主要实现其Create DDL语句的功能。
public abstract class CreatorSupport extends AnAction {
public abstract String createDDL(PsiClass psiClass);
/**
* 执行分析生成操作
*
* @param e
*/
@Override
public void actionPerformed(AnActionEvent e) {
// TODO: insert action logic here
// 获取当前编辑的文件, 通过PsiFile可获得PsiClass, PsiField等对象
PsiFile psiFile = e.getData(LangDataKeys.PSI_FILE);
PsiJavaFile javaFile = (PsiJavaFile) psiFile;
PsiClass[] psiClasses = javaFile.getClasses();
StringBuffer result = new StringBuffer();
for (PsiClass pClass : psiClasses) {
String ddl = createDDL(pClass);
result.append(ddl);
}
openDialog(result.toString());
}
/**
* 打开结果对话框
*
* @param result
*/
protected void openDialog(String result) {
Dimension screensize = Toolkit.getDefaultToolkit().getScreenSize();
int w = (int) (screensize.width * 0.3);
int h = (int) (screensize.height * 0.3);
ResultDialog dialog = new ResultDialog(result);
dialog.setSize(w, h);
dialog.pack();
dialog.setLocation((int) (screensize.width * 0.5) - (int) (w * 0.5), (int) (screensize.height * 0.5) - (int) (h * 0.5));
dialog.setVisible(true);
}
/**
* 分析代码中的字段内容获得TableField对象
*
* @param psiField
* @return
*/
protected TableField getTableField(PsiField psiField) {
if (psiField.hasModifierProperty(PsiModifier.FINAL) || psiField.hasModifierProperty(PsiModifier.STATIC)) {
return null;
}
String typeStr = psiField.getType().getCanonicalText();
TableField tableField =