代码: idea-plugin-demo
1.背景
excel导入时都会使用批量插入或者批量更新到数据库,这在mysql下没有问题。
但因为公司国产化需求,换成达梦数据库就不行了,报sql超长。
一开始想写mybatis拦截器处理,又怕出现bug,这个问题是正式上线以后暴露的,测试没有用大数据测过。
后来想到一个将下划线和驼峰式转换的idea插件:CamelCasePlugin插件,如果可以实现类似的代码转换-将批量插入方法改写成分批批量插入,就比较保险了。
2.前期准备
- 需要写一个通用的分批批量插入的方法
精简版
-
BatchUtil:通用分批操作工具类
import org.springframework.util.CollectionUtils;import java.util.ArrayList; import java.util.List; import java.util.function.Function;/*** 通用分批操作 处理批量sql超长*/ public class BatchUtil {private final static int BATCH_NUM = 10; // 每一批处理多少个public static void main(String[] args) {List<String> list = new ArrayList<>();for (int i = 0; i < 25; i++) {list.add("2");}System.out.println("----一个参数---");batchHandle(list, t1 -> t1.size());System.out.println("----多个参数---");// 只有list是可变的,其他参数一般都不会改变,所以归根结底,本质上也是一个参数batchHandle(list, t1 -> t1.size() + 1 + 2 + "1111".length());}/*** 分批处理 可能超过sql长度限制** @param list 要求要分批的集合必须放第一个* @param function 将方法当成参数传递* @param <T>*/public static <T> void batchHandle(List<T> list, Function<List<T>, Integer> function) {if (CollectionUtils.isEmpty(list) || function == null) {return;}int sum = list.size();// 每批多少条int count = BATCH_NUM;if (sum > count) {int limit = getSize(sum, count);for (int i = 0; i < limit; i++) {List subList = list.subList(i * count, i == (limit - 1) ? sum : (i + 1) * count);Integer apply = function.apply(subList);System.out.println("apply: " + apply);}} else {Integer apply = function.apply(list);System.out.println("apply: " + apply);}}/*** 方法描述:根据数据总数获取需要循环次数** @param count 数据总数* @return loop 每次数量*/public static int getSize(long count, int loop) {if (loop < 1) {return 0;}int size = 1;if (count > loop) {if (count % loop == 0) {size = (int) count / loop;} else {size = (int) count / loop + 1;}}return size;}}
-
Mybatis测试
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;import java.util.ArrayList; import java.util.Date; import java.util.List;import static com.ycxh.module.mybatis.batch.BatchUtil.batchHandle;@RestController @RequestMapping("/mybatis") public class MybatisController {@Autowiredprivate TestTable01Dao dao;@GetMapping("batchTest")public void batchTest() throws Exception {List<TestTable01> list = new ArrayList<>();for (int i = 0; i < 100; i++) {TestTable01 testTable01 = new TestTable01();testTable01.setId(i + 300);testTable01.setName(i + 300 + "name");testTable01.setAge(i + 300);testTable01.setSex(i % 2 + 1);testTable01.setCreatedTime(new Date());list.add(testTable01);}/*** select * from test_table_01 where id >= 300*/dao.batchDelete(list);// dao.batchInsert(list);// 分批处理 单个参数batchHandle(list, t1 -> dao.batchInsert(t1));// dao.batchUpdateSex(list, 2);// 分批处理 多个参数// BatchUtil_bak.batchHandle((t1, t2) -> dao.batchUpdateSex(t1, (Integer)t2[0]), list, 2);// 简化 一开始想复杂了 只有t1是变化的,所以用形参 其他都是不用变化的 所以它本质其实也是一个参数batchHandle(list, t1 -> dao.batchUpdateSex(t1, 2));// dao.batchUpdate(list);// dao.batchDeleteByNameAndSex(list);System.out.println();}}
想太多,复杂版,仅记录
import org.apache.commons.collections.CollectionUtils;import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;@FunctionalInterface
public interface MoreParamFunction<T1, T2, R> {// 可变参 第2个参数是一个数组R apply(T1 t1, T2... t2);}class Test {public static void main(String[] args) {List<String> list = new ArrayList<>();for (int i = 0; i < 25; i++) {list.add("2");}System.out.println("----一个参数---");batchHandle(list, t1 -> t1.size());System.out.println("----多个参数---");MoreParamFunction<List<String>, Object, Integer> function = (t1, t2) -> {// 可变参 第2个形参是一个数组return t1.size() + ((Integer) t2[0] + (Integer) t2[1]) + ((String) t2[2]).length();};batchHandle(function, list, 1, 2, "1111");}/*** 分批处理 可能超过sql长度限制* 一个参数 简洁一点,不需要写强转这些* @param list* @param function 将方法当成参数传递* @param <T>*/public static <T> void batchHandle(List<T> list, Function<List<T>, Integer> function) {batchHandle((t1, t2) -> function.apply(t1), list);}/*** 分批处理 可能超过sql长度限制* 可变参*/public static <T> void batchHandle(MoreParamFunction<List<T>, Object, Integer> function, List<T> list, Object... params) {if (CollectionUtils.isEmpty(list)) {return;}int sum = list.size();// 每批多少条int count = 10;if (sum > count) {int limit = getSize(sum, count);for (int i = 0; i < limit; i++) {List subList = list.subList(i * count, i == (limit - 1) ? sum : (i + 1) * count);Integer apply = function.apply(subList, params);System.out.println("apply: " + apply);}} else {Integer apply = function.apply(list, params);System.out.println("apply: " + apply);}}/*** 方法描述:根据数据总数获取需要循环次数** @param count 数据总数* @return loop 每次数量*/public static int getSize(long count, int loop) {if (loop < 1) {return 0;}int size = 1;if (count > loop) {if (count % loop == 0) {size = (int) count / loop;} else {size = (int) count / loop + 1;}}return size;}}
3.目标:插件要实现的功能
// 将批量插入方法改写成分批批量插入
dao.batchUpdateSex(list, 2);
转换为
batchHandle(list, t1 -> dao.batchUpdateSex(t1, 2));
4.搭建一个idea插件项目
插件的创建、配置、运行、打包流程,以及 action
直接从网上找一篇博客照着操作即可
-
官网
-
Intellij IDEA 插件开发 | 京东云技术团队【推荐】
-
IDEA自定义插件开发总结
-
IDEA 插件开发入门
-
idea插件开发教程
-
IDEA插件开发实战
4.1.主要的插件类型
- UI主题插件
- 自定义语言支持插件
- 框架集成插件
- 工具集成插件
4.2.开发插件的三种方式
-
Using Gradle
-
Using GitHub Template
- 在github上选择一个模板,会自动根据模板生成一个配置好的插件项目,可以直接clone到本地开发
- 打开模板的Github地址:https://github.com/JetBrains/intellij-platform-plugin-template。
-
Using DevKit
- 前2种是idea官方推荐的方式,但是都要使用Gradle
- 如果不需要引入额外jar包,我觉得使用最后这种简单的方式即可
4.3.使用DevKit开发插件
4.3.1.设置开发环境
1.确认启用 Plugin DevKit插件
该插件是IDEA自带的插件
导航到 Settings | Plugins
2.配置IntelliJ Platform Plugin SDK
IntelliJ Platform Plugin SDK 就是开发 IntelliJ 平台插件的 SDK, 是基于 JDK 之上运行的,类似于开发 Android 应用需要 Android SDK。
导航到 File | Project Structure,选择对话框左侧栏 Platform Settings 下的 SDKs
点击 + 按钮,如果还没有添加 JDK,先添加jdk,指定 JDK 的路径
再创建 IntelliJ Platform Plugin SDK,指定 home path 为 IDEA 的安装路径,然后还需要指定对应的jdk
- 选择idea安装目录
- 选择jdk
- 添加成功
配置sdk源码路径,此步可以跳过,方便debug
- 查看 build 号:打开 IDEA,Help | About,查看版本号及 build 号
- IDEA Community 源码(https://github.com/JetBrains/intellij-community):切换到与build 号相同的分支,点击 Clone or download 按钮,选择 Download ZIP
- 下载
网页:https://github.com/JetBrains/intellij-community/tree/183.4886
下载链接:https://codeload.github.com/JetBrains/intellij-community/zip/refs/heads/183.4886
- github下载到最后,速度变为0,换镜像网站下载
- 如果https://hub.nuaa.cf不能使用,就找找新的镜像站
网页:https://hub.nuaa.cf/JetBrains/intellij-community/tree/183.4886
下载链接:https://archive.nuaa.cf/JetBrains/intellij-community/archive/refs/heads/183.4886.zip
- 选择 SDKs-> 选中之前在第 3 步添加的 sdk 点击 SourcePath 后,点击右侧+添加一个 sourcePath,选择上面下载额源码后点击 OK、点击 Apply
- 要不要解压啊???不解压似乎也可以
Sandbox 沙箱
IntelliJ IDEA 插件以 Debug/Run 模式运行时是在 SandBox 中进行的,不会影响当前的 IntelliJ IDEA;但是同一台机器同时开发多个插件时默认使用的同一个 sandbox,即在创建 IntelliJ Platform SDK 时默认指定的 Sandbox Home
如果需要每个插件的开发环境是相互独立的,可以创建多个 IntelliJ Platform SDK,为 Sandbox Home 指定不同的目录 。
设置沙箱目录, 插件项目的setting信息会保存在这个目录, 可以指定任意目录
4.3.2.创建一个模块工程
选择 File | New | Project 或者 Modele,左侧栏中选择 IntelliJ Platform Plugin 类型
点击 Next,设置名称及位置,点击 Finish 完成创建。可以到 File | Project Structure 来自定义设置。
4.3.3.插件工程结构
- src 实现插件功能的 classes
- resources/META-INF/plugin.xml 插件的配置文件,指定插件名称、描述、版本号、支持的 IntelliJ IDEA 版本、插件的 components 和 actions 以及软件商等信息。
4.3.4.plugin.xml
- 下面示例描述了可在 plugin.xml 文件配置的主要元素:
<idea-plugin><!-- 插件名称,别人在官方插件库搜索你的插件时使用的名称 --><name>MyPlugin</name><!-- 插件唯一id,不能和其他插件项目重复,所以推荐使用com.xxx.xxx的格式插件不同版本之间不能更改,若没有指定,则与插件名称相同 --><id>com.example.plugin.myplugin</id><!-- 插件的描述 --><description>my plugin description</description><!-- 插件版本变更信息,支持HTML标签;将展示在 settings | Plugins 对话框和插件仓库的Web页面 --><change-notes>Initial release of the plugin.</change-notes><!-- 插件版本 --><version>1.0</version><!-- 供应商主页和email--><vendor url="http://www.jetbrains.com" email="support@jetbrains.com" /><!-- 插件所依赖的其他插件的id --><depends>MyFirstPlugin</depends><!-- 插件兼容IDEA的最大和最小 build 号,两个属性可以任选一个或者同时使用官网详细介绍:http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/build_number_ranges.html--><idea-version since-build="3000" until-build="3999"/><!-- application components --><application-components><component><!-- 组件接口 --><interface-class>com.plugin.demo.Component1Interface</interface-class><!-- 组件的实现类 --><implementation-class>com.plugin.demo.impl.Component1Impl</implementation-class></component></application-components><!-- project components --><project-components><component><!-- 接口和实现类相同 --><interface-class>com.plugin.demo.impl.Component2</interface-class></component></project-components><!-- module components --><module-components><component><interface-class>com.plugin.demo.impl.Component3</interface-class></component></module-components><!-- Actions --><actions>...</actions><!-- 插件定义的扩展点,以供其他插件扩展该插件 --><extensionPoints>...</extensionPoints><!-- 声明该插件对IDEA core或其他插件的扩展 --><extensions xmlns="com.intellij">...</extensions>
</idea-plugin>
4.3.5.创建 Action
Action 是实现插件功能的类, 一个 Action 类需要继承 AnAction 并且实现 actionPerformed 方法。当用户点击菜单或者工具栏按钮, 按快捷键,或者通过 Help | Find Action 点击时, IntelliJ Platform 系统会回调对应 Action 的 actionPerformed 方法。
一个 Action 表示 IDEA 菜单里的一个 menu item 或工具栏上的一个按钮,通过继承 AnAction class 实现,当选择一个 menu item 或点击工具栏上的按钮时,就会调用 AnAction 类的 actionPerformed 方法。
实现自定义 Action 分两步:
- 定义一个或多个 action
- 注册 action,将 item 添加到菜单或工具栏上
4.3.5.1.定义 Action
定义一个 Java class,继承 AnAction 类,并重写 actionPerformed 方法, 如
public class ActionDemo extends AnAction {public void actionPerformed(AnActionEvent event) {Project project = event.getData(PlatformDataKeys.PROJECT);Messages.showInputDialog(project,"What is your name?","Input your name",Messages.getQuestionIcon());}
}
4.3.5.3.注册 Action
在 plugin.xml 文件的 <actions>
元素内注册
<actions><group id="MyPlugin.SampleMenu" text="Sample Menu" description="Sample menu"><add-to-group group-id="MainMenu" anchor="last" /><action id="Myplugin.ActionDemo" class="Mypackage.ActionDemo" text="Text Boxes" description="A test menu item" /></group>
</actions>
- 元素会定义一个 action,指定 action 的 id、实现类、显示文本、描述
- 元素会定义一个 action group(多个 action),设置 action group 的 id、文本、描述
- 元素指定其外部 action 或 action group 被添加到的位置
上面示例会定义一个被添加到 IDEA 主菜单的最后面的 “SampleMenu” 的菜单,点击该菜单将弹出一个 “Text Boxes” item,如图
-
示例 多级菜单
<!-- 插件动作声明,在这里可以声明自定义动作 --><actions><!--自己创建菜单组--><!--https://blog.csdn.net/a__int__/article/details/127762837--><!--https://zhuanlan.zhihu.com/p/408739679?utm_id=0--><!--https://gitee.com/pillowtree/scream-tool/blob/master/resources/META-INF/plugin.xml--><!--http://www.taodudu.cc/news/show-4799909.html?action=onClick--><!--多个action可以组成一个group,形成层级菜单--><!--popup="true"如果不设置,那么group的text不会在EditorPopupMenu上展示,无法形成层级菜单--><group id="ScreamTool.Tool" text="Scream Tool" popup="true" description="ScreamTool"><!--anchor 位置 first last --><add-to-group group-id="EditorPopupMenu" anchor="first"/><action id="ScreamTool.BeanToJsonString" class="helloBoy"text="BeanToJsonString"description="BeanToJsonString" /><action id="ScreamTool.BeanToJsonString" class="helloBoy2" text="changeCode"><!-- 快捷键冲突会使用不了 --><keyboard-shortcut keymap="$default" first-keystroke="ctrl F2"/></action></group><action id="ScreamTool.BeanToJsonString" class="helloBoy2" text="sayHello2"><add-to-group group-id="ScreamTool.Tool" anchor="first"/><!-- 快捷键冲突会使用不了 --><!--<keyboard-shortcut keymap="$default" first-keystroke="ctrl F2"/>--></action><!-- Add your actions here --><!-- 需要等一会才能从灰色变为可用 --><action id="com.idea.hello" class="helloBoy" text="sayHello"><!--系统默认有一些组--><!--ToolsMenu Tools菜单栏--><!--EditorPopupMenu代码编辑页面右击出现--><add-to-group group-id="ToolsMenu" anchor="first"/><!-- 快捷键冲突会使用不了 --><keyboard-shortcut keymap="$default" first-keystroke="ctrl F2"/></action></actions>
4.3.5.3.快速创建 Action
https://idea.javaguide.cn/tips/plug-in-development-intro.html#_03-手动创建-action
Idea插件添加EditorPopupMenu显示JAVA对象的JSON字符串
IntelliJ Platform 提供了 New Action 向导,它会帮助我们创建 action class 并配置 plugin.xml 文件:
在目标 package 上右键,选择 New | Plugin DevKit | Action:
- Action ID: action 唯一 id,推荐 format: PluginName.ID
- Class Name: 要被创建的 action class 名称
- Name: menu item 的文本
- Description: action 描述,toolbar 上按钮的提示文本,可选
- Add to Group:选择新 action 要被添加到的 action group(Groups, Actions)以及相对其他 actions 的位置(Anchor)
- Keyboard Shortcuts:指定 action 的第一和第二快捷键
创建完成之后,我们的 plugin.xml
的 <actions>
节点下会自动生成我们刚刚创建的 Action 信息:
<actions><!-- Add your actions here --><action id="BatchToSplitBatchTool" class="BatchToSplitBatchAction" text="BatchToSplitBatch" description="批量转分批批量"><add-to-group group-id="EditorPopupMenu" anchor="first"/><keyboard-shortcut keymap="$default" first-keystroke="ctrl F2"/></action></actions>
并且 java
目录下会生成一个叫做 BatchToSplitBatchAction
的类。这个类继承了 AnAction
,并覆盖了 actionPerformed()
方法。这个 actionPerformed
方法就好比 JS 中的 onClick
方法,会在你点击的时候触发对应的动作。
另外,每个动作都会归属到一个 Group 中,这个 Group 可以简单看作 IDEA 中已经存在的菜单。
注意:该向导只能向主菜单中已存在的 action group 或工具栏上添加 action,若要创建新的 action group,需要另想办法。
4.3.6.运行调试插件
运行 / 调试插件可直接在 IntelliJ IDEA 进行,选择 Run | Edit Configurations…,若左侧栏没有 Plugin 类型的 Configuration, 点击右上角 + 按钮,选择 Plugin 类型,如图
- 如果报错,则设置一下输出目录
点击run|debug之后,Intel IDEA 会另启一个装有该插件的 IDEA窗口,该idea中已经安装了你开发的插件。
在该idea中可以直接使用插件,使用过程中,可以在项目里面断点跟进。
启动的idea和正常idea操作是一样的,可以打开一个测试demo,来测试你的插件是否正常运行。
上面设置创建的group-id为EditorPopupMenu,所以可以在编辑窗口右键菜单中发现该插件。
-
存在2个快捷键一样的插件,是因为之前调试的另一个插件还在沙盒里面,可以删掉
-
假如创建的 Action 的所属 Group 是 ToolsMenu(Tools) 。这样的话,创建的 Action 所在的位置就在 Tools 这个菜单下。
- 假如创建的 Action 所属的 Group 是MainMenu (IDEA 最上方的主菜单栏)下的 FileMenu(File) 的话。
<actions><!-- Add your actions here --><action id="test.hello" class="HelloAction" text="Hello" description="IDEA 插件入门"><add-to-group group-id="FileMenu" anchor="first"/></action>
</actions>
我们创建的 Action 所在的位置就在 File 这个菜单下。
4.3.6.1.可能需要等一会,菜单项才能从灰色变为可用
4.3.6.2.调试 会打开一个新的idea窗口,可能需要重新激活
idea 2018.3永久简单激活。激活码
https://blog.csdn.net/weixin_44991324/article/details/1361533941.打开hosts文件将 0.0.0.0 account.jetbrains.com 添加到文件末尾
C:\Windows\System32\drivers\etc\hosts2.注册码:
4.3.4.打包安装插件
4.3.4.1.打包插件
选择 Build | Prepare Plugin Module ‘module name’ for Deployment 来打包插件:
插件包位置:一般在工程根目录下
如果插件没有依赖任何 library,插件会被打包成一个 .jar ,否则会被打包成一个 .zip ,zip 中包含了所有的插
件依赖
4.3.4.2.安装插件
导航到 File | Settings | Plugins 页面,点击 Install plugin from disk…
5.导入一个idea插件项目
idea plugin 工程导入
idea中一个普通工程变为插件工程
-
将.iml文件中的type属性值JAVA_MODULE其修改为PLUGIN_MODULE。
- idea_plugin_demo/.idea/idea_plugin_demo.iml
-
项目打开默认是java项目
<module type="JAVA_MODULE" version="4">
- 修改为插件项目
<module type="PLUGIN_MODULE" version="4">
还要加上
<component name="DevKit.ModuleBuildProperties" url="file://$MODULE_DIR$/resources/META-INF/plugin.xml" />
-
也可以直接创建一个idea插件项目,对比一下文件差异
-
最终效果
<module type="PLUGIN_MODULE" version="4"><component name="DevKit.ModuleBuildProperties" url="file://$MODULE_DIR$/resources/META-INF/plugin.xml" /><component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="true"><exclude-output /><content url="file://$MODULE_DIR$"><sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" /><sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /></content><orderEntry type="jdk" jdkName="IntelliJ IDEA IU-183.4886.37" jdkType="IDEA JDK" /><orderEntry type="sourceFolder" forTests="false" /></component>
</module>