代码: 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 { @Autowired private 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
直接从网上找一篇博客照着操作即可
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/136153394
1.打开hosts文件将 0.0.0.0 account.jetbrains.com 添加到文件末尾
C:\Windows\System32\drivers\etc\hosts
2.注册码:
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插件项目
-
将.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>