Posted in

IntelliJ IDE 插件开发 | (十一)解析 Mybatis 的控制台 SQL 日志_AI阅读总结 — 包阅AI

包阅导读总结

1.

关键词:MyBatis、SQL 日志、控制台、解析、插件

2.

总结:本文介绍了开发解析 MyBatis 控制台 SQL 日志的插件,通过继承相关类实现对控制台文本处理、绘制图标、添加点击事件,以获取完整 SQL 日志并复制到粘贴板,还提到了对异常处理及自定义渲染类的实现,总结了实现思路。

3.

主要内容:

– 前言

– 介绍系列文章背景,将通过实战样例展示插件开发知识运用。

– 实现思路

– 继承`Filter`和`ConsoleFilterProvider`处理控制台中的 SQL 文本。

– 继承`EditorCustomElementRenderer`绘制图标。

– 继承`InputHandler`为图标添加点击事件实现 SQL 解析。

– 增加控制台过滤器处理文本

– 展示`MyBatisSQLFilter`类的代码,过滤包含特定格式的行。

– 介绍将类注册到`plugin.xml`中。

– 过滤器使用扩展

– 展示修改`applyFilter`方法给文本添加超链接样式。

– 实现通义千问在异常后添加图标

– 展示`ExceptionFilter`类的代码及在`plugin.xml`中的注册。

– 实现自定义渲染类并添加点击事件

– 展示`MybatisSQLRenderer`类的代码,包括鼠标操作和图标绘制等方法。

– 总结

– 介绍实现对 MyBatis SQL 日志的解析,提供思路,未涉及 JPA 解析。

思维导图:

文章地址:https://juejin.cn/post/7385215732818722850

文章来源:juejin.cn

作者:庄周de蝴蝶

发布时间:2024/6/28 9:00

语言:中文

总字数:1640字

预计阅读时间:7分钟

评分:87分

标签:IntelliJ IDEA 插件开发,MyBatis,SQL 日志解析,控制台过滤器,Kotlin


以下为原文内容

本内容来源于用户推荐转载,旨在分享知识与观点,如有侵权请联系删除 联系邮箱 media@ilingban.com

系列文章

本系列文章已收录到专栏,交流群号:689220994,也可点击链接加入。

前言

在前面的十个章节中主要介绍了关于插件开发的基础知识,从本节开始则会通过一个一个实战的 demo 样例来展示这些内容的综合运用,同时会偶尔夹杂一些进阶的理论内容。本文则是开发一个解析控制台中 MyBatis 的 SQL 日志,通过点击图标即可实现获取到完整 SQL 日志并复制到粘贴板中,效果如下图所示,另外本文所涉及到的完整代码也已上传到GitHub。

动画

实现思路

  1. 继承FilterConsoleFilterProvider来实现对控制台中的 SQL 文本进行处理。
  2. 继承EditorCustomElementRenderer实现在控制台中绘制图标。
  3. 继承InputHandler实现给 2 中绘制的图标添加点击事件,然后在点击事件中实现对 SQL 的解析。

增加控制台过滤器处理文本

这里先直接展示代码:

class MyBatisSQLFilter: Filter, ConsoleFilterProvider {    override fun applyFilter(line: String, offset: Int): Filter.Result? {        if (line.contains(Constants.SQL_PREFIX)) {            return Filter.Result(listOf(                MyBatisSQLResult(offset - line.length),                Filter.ResultItem(0, 0, null)            ))        }        return null    }        class MyBatisSQLResult(private val offset: Int) : Filter.Result(offset, offset, null), InlayProvider {        override fun createInlayRenderer(editor: Editor): EditorCustomElementRenderer {            return MybatisSQLRenderer(editor, offset)        }    }    override fun getDefaultFilters(p0: Project): Array<Filter> {        return arrayOf(this)    }}

其中applyFilter方法就是用来过滤需要处理文本,第一行的判断就是用来过滤行中包含==> Preparing:(MyBatis SQL 日志的固定格式)的内容,然后对文本行应用MybatisSQLRenderer这个自定义的渲染类,同时通过传递的 offset 参数就实现了在满足条件的文本行前添加图标。

在实现了该类后,还需要将其注册到plugin.xml中:

<extensions defaultExtensionNs="com.intellij">	<consoleFilterProvider implementation="cn.butterfly.sqllog.filter.MyBatisSQLFilter"                            order="first"/></extensions>

过滤器使用扩展

控制台文本增加超链接样式

除了上述使用过滤器在控制台添加图标外,还可以通过使用过滤器给文本添加超链接样式,只需要将上述的applyFilter方法按如下修改:

override fun applyFilter(line: String, offset: Int): Filter.Result? {    if (line.contains(Constants.SQL_PREFIX)) {        return Filter.Result(listOf(MyBatisSQLResult(offset - line.length),            Filter.ResultItem(offset - line.length, offset, HyperlinkInfo {  })        ))    }    return null}

其中前两个参数用于控制超链接的起始范围,第三个参数则用于添加超链接样式,最终效果如下:

image-20240629135225924

实现通义千问在异常后添加图标

这里先直接展示代码:

class ExceptionFilter: JvmExceptionOccurrenceFilter {        override fun applyFilter(exceptionClassName: String, classes: MutableList<PsiClass>, offset: Int): Filter.ResultItem {        return object : Filter.Result(offset, offset + exceptionClassName.length, null), InlayProvider {             override fun createInlayRenderer(editor: Editor): EditorCustomElementRenderer {                return MybatisSQLRenderer(editor, offset)            }}    }}

可以看到上述代码实现的的是JvmExceptionOccurrenceFilter接口,这个接口是 IDEA 专门提供用于处理控制台中产生的异常信息,除了代码实现外,还需要在plugin.xml中进行注册:

<extensions defaultExtensionNs="com.intellij">	<jvm.exceptionFilter implementation="cn.butterfly.sqllog.filter.ExceptionFilter"                         order="last"/></extensions>

最终效果如下图所示:

image-20240629141253162

实现自定义渲染类并添加点击事件

这里先展示代码:

class MybatisSQLRenderer(private val editor: Editor, private val offset: Int): EditorCustomElementRenderer, InputHandler {        private var hovering = false        override fun mouseMoved(event: MouseEvent, translated: Point) {        if (hovering) return        (editor as EditorImpl).setCustomCursor(this, Cursor.getPredefinedCursor(Cursor.HAND_CURSOR))        hovering = true    }    override fun mouseExited() {        hovering = false        (editor as EditorImpl).setCustomCursor(this, null)    }    override fun mouseClicked(event: MouseEvent, translated: Point) {                val doc = editor.document        val sqlLineNumber = doc.getLineNumber(offset)        val paramsLineNumber = sqlLineNumber + 1        var sqlStr = getTextByLineNumber(sqlLineNumber)        val paramsStr = getTextByLineNumber(paramsLineNumber)        paramsStr.split(",").map {            val param = it.trim()            val value = param.substring(0, param.indexOf('('))            val type = param.substring(param.indexOf('(') + 1, param.indexOf(')'))            if (type == "String") "'$value'" else value        }.forEach {            sqlStr = sqlStr.replaceFirst("?", it)        }        val stringSelection = StringSelection(sqlStr)        Toolkit.getDefaultToolkit().systemClipboard.setContents(stringSelection, null)        Utils.info("复制成功")    }        private fun getTextByLineNumber(lineNumber: Int): String {        val doc = editor.document        val start = doc.getLineStartOffset(lineNumber)        val end = doc.getLineEndOffset(lineNumber)        return doc.getText(TextRange(start, end)).substring(Constants.PARAMS_PREFIX.length)    }    override fun calcWidthInPixels(p0: Inlay<*>) = PluginIcons.MAPPER_ICON.iconWidth    override fun calcHeightInPixels(inlay: Inlay<*>) = PluginIcons.MAPPER_ICON.iconHeight        override fun paint(inlay: Inlay<*>, g: Graphics, r: Rectangle, textAttributes: TextAttributes) {        val consoleIcon = PluginIcons.MAPPER_ICON        val curX = r.x + r.width / 2 - consoleIcon.iconWidth / 2        val curY = r.y + r.height / 2 - consoleIcon.iconHeight / 2        if (curX >= 0 && curY >= 0) {            consoleIcon.paintIcon(inlay.editor.component, g, curX, curY)        }    }}

其中mouseMoved方法用于控制鼠标移入图标时将鼠标切换为手形图标,mouseExited则是在鼠标离开图标时恢复原始的样式,mouseClicked则是在鼠标点击图标时实现对 SQL 文本进行解析并复制到粘贴板中,这里要注意的是该方法内部是对如下格式的 SQL 日志进行解析的:

==> Preparing: SELECT id,name,age,email FROM user_demo WHERE (age >= ? AND name <> ?)==> Parameters: 18(Integer), demo(String)

calcWidthInPixelscalcHeightInPixelspaint则是设置图标的宽高然后进行绘制。

以上代码就可以实现开篇所提到的功能,如果想要实现对 JPA 的 SQL 日志解析,也只需要修改上述mouseClicked中的逻辑即可。

总结

本文介绍了如何通过继承控制台过滤器类来实现自定义控制台的内容,然后以此来实现对 MyBatis SQL 日志的解析:根据打印的 SQL 和参数拼接得到完整的 SQL 然后保存到粘贴板中供用户使用。不过本文只是做简单处理,提供一个实现的思路,像 JPA 的 SQL 日志解析也是可以参考这种方式去实现,这里就不再介绍,如果有问题也欢迎在评论区或者讨论群进行交流讨论。