idea 插件开发 扫描sqlserver

大家好,我是烤鸭:

    最近在搞sqlserver 升级 mysql/tidb,发现代码里的sql有很多地方需要改,想着能不能开发一个省点力。

官方的迁移指南:
https://www.mysql.com/why-mysql/white-papers/sql-server-to-mysql-zh/

方案选择

最开始想的是在sql拦截器做个拦截,判断标识是否开启转换,把对应的sqlserver 的sql转换后mysql的。技术上可以实现,开发成本高,需要对sqlserver和mysql的语法都熟悉,更主要是业务的sql错综复杂,很难保证100%。

比如说下面这个sql

SELECT [Id] FROM [dbo].[Test] with(nolock) WHERE [UId]=#{uid} ORDER BY BrowseTime DESC OFFSET #{page}*#{size}-#{size} ROW FETCH NEXT #{size} ROW ONLY;

转成mysql

SELECT Id FROM dbo.Test WHERE UId = #{uid} ORDER BY BrowseTime DESC limit #{page}*#{size},#{size}

这个是报错的,根本没法转

在这里插入图片描述

所以想业务方无感知转换sql,是比较难的。

所以就退一步,不转换,帮忙指出哪些sql 无法适用mysql,必须改变。

idea 插件

使用idea的还是比较多的,就想着能不能开发一款插件完成扫描的工作,类似alibaba的代码扫描。

效果如图

在这里插入图片描述

在这里插入图片描述

简单分享下代码:

plugin.xml 中配置窗口和按钮

    <extensions defaultExtensionNs="com.intellij"><!-- Add your extensions here --><!-- 自定义控制台输入 --><!--canCloseContents 允许用户关闭--><toolWindow canCloseContents="true" anchor="bottom"id="Ms2Msl"factoryClass="ycplugin.toolwindow.MslToolWindow"></toolWindow></extensions><actions><action id="ycpluginSqlserverScan" class="ycplugin.ScanMain" text="ss2ms" description="ss2ms item" >
<!--            <add-to-group group-id="MainToolBar" anchor="last"/>--><add-to-group group-id="ProjectViewPopupMenu" anchor="last"/>
<!--            <add-to-group group-id="ChangesViewPopupMenu" anchor="last"/>-->
<!--            <add-to-group group-id="EditorPopupMenu" anchor="last"/>--></action></actions>

MslToolWindow

package ycplugin.toolwindow;import com.intellij.execution.filters.TextConsoleBuilder;
import com.intellij.execution.filters.TextConsoleBuilderFactory;
import com.intellij.execution.ui.ConsoleView;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowFactory;
import com.intellij.ui.content.Content;
import org.jetbrains.annotations.NotNull;import javax.swing.*;
import java.awt.*;public class MslToolWindow implements ToolWindowFactory {public static JComponent createConsolePanel(ConsoleView view) {JPanel panel = new JPanel();panel.setLayout(new BorderLayout());panel.add(view.getComponent(), BorderLayout.CENTER);return panel;}@Overridepublic boolean shouldBeAvailable(@NotNull Project project) {return true;}@Overridepublic void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) {TextConsoleBuilder consoleBuilder = TextConsoleBuilderFactory.getInstance().createBuilder(project);ConsoleView console = consoleBuilder.getConsole();JComponent consolePanel = createConsolePanel(console);Content content = toolWindow.getContentManager().getFactory().createContent(consolePanel, "ss2msl plugin", false);toolWindow.getContentManager().addContent(content);//console.print("------------after add consoleView to tool------------" + "\n", ConsoleViewContentType.LOG_INFO_OUTPUT);new ToolWindowConsole(toolWindow, console, project);//PropertiesCenter.init(project);//console.setOutputPaused(true);}}

ToolWindowConsole

package ycplugin.toolwindow;import com.intellij.execution.filters.TextConsoleBuilderFactory;
import com.intellij.execution.ui.ConsoleView;
import com.intellij.execution.ui.ConsoleViewContentType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.wm.ToolWindow;public class ToolWindowConsole {private static Project project;private static ConsoleView console;public static boolean isShow;public ToolWindowConsole(ToolWindow toolWindow, ConsoleView console, Project project) {this.console = console;this.project = project;}public static void show() {isShow = true;}public static void clear() {if (console != null) {console.clear();}}public static void log(String s) {if(project == null && console == null){return;}if (console == null) {console = TextConsoleBuilderFactory.getInstance().createBuilder(project).getConsole();}if (console.isOutputPaused()) {console.setOutputPaused(false);}console.print(s + "\n", ConsoleViewContentType.NORMAL_OUTPUT);return;}
}

ScanMain

package ycplugin;import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.vfs.VirtualFile;
import ycplugin.analysis.ScanAnalyzer;
import ycplugin.toolwindow.ToolWindowConsole;public class ScanMain extends AnAction {// 如果通过Java代码来注册,这个构造函数会被调用,传给父类的字符串会被作为菜单项的名称// 如果你通过plugin.xml来注册,可以忽略这个构造函数public ScanMain() {// 设置菜单项名称// 还可以设置菜单项名称,描述,图标super("Ss2msl Scan", "sqlserver-to-mysql plugin", IconLoader.getIcon("/icon/open.png"));}@Overridepublic void actionPerformed(AnActionEvent event) {ToolWindowConsole.show();// 每次触发清空 consoleToolWindowConsole.clear();Project project = event.getData(PlatformDataKeys.PROJECT);VirtualFile[] virtualFiles = event.getData(CommonDataKeys.VIRTUAL_FILE_ARRAY);// 扫描开始new ScanAnalyzer(virtualFiles[0], project).start();}
}

ScanAnalyzer

package ycplugin.analysis;import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import ycplugin.analysis.filter.ScanFilter;
import ycplugin.analysis.filter.ScanXmlFilter;
import ycplugin.check.Ss2mslChecker;
import ycplugin.check.Ss2mslCheckerKeyword;
import ycplugin.convert.Ss2mslConverter;
import ycplugin.convert.Ss2mslConverterMysql;
import ycplugin.toolwindow.ToolWindowConsole;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;public class ScanAnalyzer {private VirtualFile virtualFile;private Project project;private ScanFilter scanFilter;private Ss2mslChecker ss2mslChecker;private Ss2mslConverter ss2mslConverter;private int total;public ScanAnalyzer() {}public ScanAnalyzer(VirtualFile virtualFile, Project project) {this.virtualFile = virtualFile;this.project = project;scanFilter = new ScanXmlFilter();ss2mslChecker = new Ss2mslCheckerKeyword();ss2mslConverter = new Ss2mslConverterMysql();}public void start() {// 目录下if (virtualFile.isDirectory()) {findChildrenFile();} else {// 单个文件 String byLine = getSqlByLine(virtualFile);if (byLine != null) {// 说明有异常sql,先打印出文件夹ToolWindowConsole.log(byLine);}total = 1;}ToolWindowConsole.log("共扫描文件:【" + total + "】");}private void findChildrenFile() {@NotNull VirtualFile[] elements = virtualFile.getCanonicalFile().getChildren();for (int i = 0; i < elements.length; i++) {VirtualFile element = elements[i];if (!element.isDirectory()) {total++;if (scanFilter.filter(element)) {String byLine = getSqlByLine(element);if (byLine != null) {// 说明有异常sql,先打印出文件夹ToolWindowConsole.log(byLine);}}} else if (element.isDirectory()) {virtualFile = element;findChildrenFile();}}}private String getSqlByLine(VirtualFile virtualFileElement) {StringBuilder builder = new StringBuilder();int lineNum = 1;try {BufferedReader br = new BufferedReader(new InputStreamReader(virtualFileElement.getInputStream(), Charset.forName("utf8")));String line;while ((line = br.readLine()) != null) {// 返回false,校验失败,String check = ss2mslChecker.check(line);if (check != null) {if (builder.length() == 0) {builder.append("当前扫描文件为:【" + virtualFileElement.getName() + "】" + "\r\n");}builder.append("第【" + lineNum + "】行 " + "\t" + ss2mslConverter.convert(line).replaceAll(" ", "") + "\t" + "异常字符:【" + check + "】" + "\r\n");lineNum++;}}return builder.toString();} catch (IOException e) {e.printStackTrace();}return null;}
}

至于里边的filter和checker 自己写下就行,逻辑也没复杂的,目前就是关键字识别匹配,也没什么功能。

打包遇到乱码问题的话,build.gradle 中添加

tasks.withType(JavaCompile) {options.encoding = "UTF-8"
}

总结

轮子都这么多了,总有一款适合你的,如果没有,就自己开发。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/412555.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VUE之文字跑马灯效果

1.效果演示 2.相关代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><script src"js/vue-2.4.0.js"></script> </head> <body> <div id&…

[css] 用CSS绘制一个三角形

[css] 用CSS绘制一个三角形 .triangle{width: 0;border-bottom: 35px solid lightgreen;border-left: 35px solid transparent;}wrong.triangle{width: 0;border: 35px solid transparent;border-bottom: 35px solid lightgreen; }个人简介 我是歌谣&#xff0c;欢迎和大家一起…

计算机从加电到系统(Linux)启动完成

0x0 背景 在我参加的面试和我面试别人、或者参加别人对别人的面试的事后经常遇到的一个问题就是&#xff1a;请从计算机加电开始描述一下计算机启动到操作系统正式启动起来的全过程。这是一个考验对计算机体系结构和基本知识了解程度的问题。今天也就特别针对这个问题做一个回答…

《少有人走的路——心智成熟的旅程》读书笔记

大家好&#xff0c;我是烤鸭&#xff1a; 《少有人走的路——心智成熟的旅程》&#xff0c;读书笔记。 第一部分 自律 规避问题和逃避痛苦的趋向&#xff0c;是人类心理疾病的根源。(正视自己是最重要的) 自律可以消除人的痛苦&#xff1a;延迟满足、承担责任、尊重事实…

[css] 说下line-height三种赋值方式有何区别?

[css] 说下line-height三种赋值方式有何区别&#xff1f; line-height 可以有带单位及不带单位的写法&#xff08;感觉其实是两种&#xff09;。div{line-height: 24px;line-height: 1.5;line-height: 1.5em;line-height: 150%; }对于应用在单个元素上&#xff0c;这几种写法的…

MySQL数据库select语句的使用方法

select语句可 以用回车分隔$sql"select * from article where id1"和 $sql"select * from article where id1" 都可以得到正确的结果&#xff0c;但有时分开写或许能 更明了一点&#xff0c;特别是当sql语句比较长时。批量查询数据可以用in 来实现 $sql&…

PMP 错题记录

PMP 错题记录 大家好&#xff0c;我是烤鸭&#xff1a; 这次的PMP错题集本来想考前发&#xff0c;临时能看看&#xff0c;还是耽搁了&#xff0c;补发一下吧&#xff0c;不知道以后用不用的上&#xff0c;据说改版了&#xff0c;可能也用不上了。 变更题错题记录 9、一项…

[css] 让网页的字体变得清晰,变细用CSS怎么做?

[css] 让网页的字体变得清晰&#xff0c;变细用CSS怎么做&#xff1f; 全家桶&#xff1a;-webkit-font-smoothing: antialiased -moz-osx-font-smoothing: grayscale text-shadow: 1px 1px 1px 1px rgba(0,0,0,0.005) text-rendering: optimizeLegibility个人简介 我是歌谣&…

skywalking oap-server 域名配置

大家好&#xff0c;我是烤鸭&#xff1a; ​ ​ ​ ​ 由于skywalking 的 -Dskywalking.collector.backend_service 的后端服务过多&#xff0c;想通过配置域名的方式简化上报端agent配置&#xff0c;也更灵活。 报错了&#xff0c;先看代码 报错信息&#xff1a; org.apac…

[css] 描述下你所了解的图片格式及使用场景

[css] 描述下你所了解的图片格式及使用场景 通常网页在显示的图片&#xff08;图形&#xff09;的时候&#xff0c;有以下几种格式&#xff1a;GIF、PNG、JPG、SVG&#xff0c;还有个比较新的WebP格式。▍GIF优点&#xff1a;GIF是动态的&#xff1b;支持无损耗压缩和透明度。…

【P1063】 能量项链

之前一直在luogu博客上 2018年12月25日17:15:52 copy到博客园 P1063 能量项链 简单的区间dp 通过解决小区间来影响大区间 环形问题 存储的时候存两边 变成 2*N 个元素 code: for(int i1;i<n;i) {cin>>e[i];e[in]e[i]; }s[i][j] ------- i到j的最大能量 k --------…

nginx 配置 http/2(h2) 和 http 在同一端口的问题

nginx 配置 http/2(h2) 和 http 在同一端口的问题 大家好&#xff0c;我是烤鸭&#xff1a; ​ 这个完全是个采坑记录了。 场景说明 由于这边有个需求想加个支持 grpc 方式转发的域名。 正常的二级域名都是映射到80端口&#xff0c;所以也没想太多&#xff0c;按照这个…

[css] 请描述css的权重计算规则

[css] 请描述css的权重计算规则 权重值计算 选择器 案例 权重值 !important !important Infinity 内联样式 style"…" 1000 ID #id 100 class .class 10 属性 [type‘text’] 10 伪类 :hover 10 标签 p 1 伪元素 ::first-line 1 相邻选择器、子代选择器、通配符 * &…

不同操作系统打开文件浏览器(资源管理器)的方式

windows      start . 或 explorer .Mac        open .Linux(ubuntu) nautilus .Linux KDE4 dolphin . 详细说明 转载于:https://www.cnblogs.com/mengff/p/10175767.html

FutureTask isDone 返回 false

大家好&#xff0c;我是烤鸭&#xff1a; ​ 今天看一下 FutureTask源码。好吧&#xff0c;其实遇到问题了&#xff0c;哪里不会点哪里。 伪代码 package src.executor;import org.springframework.scheduling.annotation.AsyncResult; import org.springframework.sche…

为什么MySQL数据库要用B+树存储索引

A&#xff1a;为什么MySQL数据库要用B树存储索引&#xff1f; Hash的查找速度为O(1)&#xff0c;而树的查找速度为O(log2n)&#xff0c;为什么不用Hash作为数据库的存储索引呢&#xff1f; 树的话&#xff0c;无非就是前中后序遍历、二叉树、二叉搜索树、平衡二叉树&#xff0c…

[css] rgba()和opacity这两个的透明效果有什么区别呢?

[css] rgba()和opacity这两个的透明效果有什么区别呢&#xff1f; 1.opacity 是属性&#xff0c;rgba()是函数&#xff0c;计算之后是个属性值&#xff1b; 2.opacity 作用于元素和元素的内容&#xff0c;内容会继承元素的透明度&#xff0c;取值0-1&#xff1b; 3.rgba() 一般…

lettuce 配置域名 dns 切换

大家好&#xff0c;我是烤鸭&#xff1a; 如果你也有类似的困扰&#xff0c;运维告诉你&#xff0c;redis连接配置域名&#xff0c;这样出问题了&#xff0c;直接改dns地址就行&#xff0c;不需要重启服务。。。梦想是美好的&#xff0c;现实是残酷的。如果你使用的是 let…

[css] 怎样修改chrome记住密码后自动填充表单的黄色背景?

[css] 怎样修改chrome记住密码后自动填充表单的黄色背景&#xff1f; input:-webkit-autofill { -webkit-box-shadow: 0 0 3px 100px #eee inset; //改变填充背景色 }个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后端知识。放弃很容易&#xff0c; 但坚持一定很酷。欢…

[css] 说说你对z-index的理解

[css] 说说你对z-index的理解 层叠 就是Z轴的方向的位置&#xff0c;值越大离屏幕前的你越近&#xff0c;反之亦然。可以为负数个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后端知识。放弃很容易&#xff0c; 但坚持一定很酷。欢迎大家一起讨论 主目录 与歌谣一起通…