public class IconUtils { private static ImageIcon methodIcon = new ImageIcon(IconUtils.class.getClassLoader().getResource("icon/mybatis.png")); private static ImageIcon xmlIcon = new ImageIcon(IconUtils.class.getClassLoader().getResource("icon/mybatis-ns.png")); public static Icon useMyBatisIcon() { return methodIcon; } public static Icon useXmlIcon() { return xmlIcon; } }
package com.ccnode.codegenerator.view; import com.ccnode.codegenerator.util.IconUtils; import com.intellij.codeInsight.daemon.RelatedItemLineMarkerInfo; import com.intellij.codeInsight.daemon.RelatedItemLineMarkerProvider; import com.intellij.codeInsight.navigation.NavigationGutterIconBuilder; import com.intellij.openapi.editor.markup.GutterIconRenderer; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiMethod; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.PsiShortNamesCache; import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlFile; import com.intellij.psi.xml.XmlTag; import org.jetbrains.annotations.NotNull; import java.util.Collection; import java.util.HashSet; import java.util.Set; /** * Created by bruce.ge on 2016/12/8. */ public class MybatisXmlLineMarkerProvider extends RelatedItemLineMarkerProvider { private static Set<String> tagNameSet = new HashSet<String>() {{ add("select"); add("insert"); add("update"); add("delete"); }}; @Override protected void collectNavigationMarkers(@NotNull PsiElement element, Collection<? super RelatedItemLineMarkerInfo> result) { if (!(element instanceof XmlTag)) return; PsiFile psiFile = element.getContainingFile(); if (!(psiFile instanceof XmlFile)) return; XmlFile xmlFile = (XmlFile) psiFile; if (!xmlFile.getRootTag().getName().equals("mapper")) { return; } XmlTag tag = (XmlTag) element; if (!tagNameSet.contains(tag.getName())) { return; } XmlAttribute namespaceAttribute = xmlFile.getRootTag().getAttribute("namespace"); if (namespaceAttribute == null || namespaceAttribute.getValue() == null) { return; } String namespace = namespaceAttribute.getValue(); XmlAttribute id = tag.getAttribute("id"); if (id == null || id.getValue() == null) { return; } String[] split = namespace.split("\."); String className = split[split.length - 1]; Module moduleForPsiElement = ModuleUtilCore.findModuleForPsiElement(element); if (moduleForPsiElement == null) { return; } PsiClass[] classesByName = PsiShortNamesCache.getInstance(element.getProject()).getClassesByName(className, GlobalSearchScope.moduleScope(moduleForPsiElement)); PsiClass realClass = null; for (PsiClass psiClass : classesByName) { if (psiClass.isInterface() && psiClass.getQualifiedName().equals(namespace)) { // realClass = psiClass; break; } } if (realClass == null) { return; } String xmlMethodName = id.getValue(); PsiElement findedMethod = null; PsiMethod[] allMethods = realClass.getAllMethods(); for (PsiMethod classMethod : allMethods) { if (xmlMethodName.equals(classMethod.getName())) { findedMethod = classMethod; break; } } if (findedMethod == null) { return; } result.add(NavigationGutterIconBuilder.create(IconUtils.useXmlIcon()).setAlignment(GutterIconRenderer.Alignment.CENTER) .setTarget(findedMethod).setTooltipTitle("navigation to mapper class").createLineMarkerInfo(element)); //means we find the class. then get it all method and get it. } }
package com.ccnode.codegenerator.view; import com.ccnode.codegenerator.util.IconUtils; import com.ccnode.codegenerator.util.MyPsiXmlUtils; import com.intellij.codeInsight.daemon.RelatedItemLineMarkerInfo; import com.intellij.codeInsight.daemon.RelatedItemLineMarkerProvider; import com.intellij.codeInsight.navigation.NavigationGutterIconBuilder; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.editor.markup.GutterIconRenderer; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiMethod; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.PsiNonJavaFileReferenceProcessor; import com.intellij.psi.search.PsiSearchHelper; import com.intellij.psi.search.PsiShortNamesCache; import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlFile; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * Created by bruce.ge on 2016/12/8. */ public class MybatisJavaLineMarkerProvider extends RelatedItemLineMarkerProvider { @Override protected void collectNavigationMarkers(@NotNull PsiElement element, Collection<? super RelatedItemLineMarkerInfo> result) { if (element instanceof PsiMethod) { PsiMethod method = (PsiMethod) element; PsiClass containingClass = method.getContainingClass(); if (!containingClass.isInterface()) { return; } // if it's interface, then find it in the xml file to check if it contain the name. //shall be mapper then go to find to corresponding xml file. Project project = element.getProject(); String qualifiedName = containingClass.getQualifiedName(); PsiElement methodElement = null; PsiFile[] filesByName = PsiShortNamesCache.getInstance(project).getFilesByName(containingClass.getName() + ".xml"); //first search with samename xml. if (filesByName.length == 0) { methodElement = handleWithFileNotFound(method, project, qualifiedName, result); } else { for (PsiFile file : filesByName) { if (file instanceof XmlFile) { XmlFile xmlFile = (XmlFile) file; XmlAttribute namespace = xmlFile.getRootTag().getAttribute("namespace"); if (namespace == null || !namespace.getValue().equals(qualifiedName)) { continue; } //say we find the xml file. PsiElement psiElement = MyPsiXmlUtils.findTagForMethodName(xmlFile, method.getName()); if (psiElement != null) { methodElement = psiElement; break; } } } if (methodElement == null) { methodElement = handleWithFileNotFound(method, project, qualifiedName, result); } } if (methodElement != null) { result.add(NavigationGutterIconBuilder.create(IconUtils.useMyBatisIcon()).setAlignment(GutterIconRenderer.Alignment.CENTER) .setTarget(methodElement).setTooltipTitle("navigation to mapper xml").createLineMarkerInfo(method.getNameIdentifier())); } //只进行method的判断 进行控制 其他的不管 } } private PsiElement handleWithFileNotFound(@NotNull PsiMethod method, Project project, final String qualifiedName, Collection<? super RelatedItemLineMarkerInfo> result) { PsiSearchHelper searchService = ServiceManager.getService(project, PsiSearchHelper.class); List<XmlFile> xmlFiles = new ArrayList<XmlFile>(); Module moduleForPsiElement = ModuleUtilCore.findModuleForPsiElement(method); if (moduleForPsiElement == null) { return null; } searchService.processUsagesInNonJavaFiles("mapper", new PsiNonJavaFileReferenceProcessor() { @Override public boolean process(PsiFile file, int startOffset, int endOffset) { if (file instanceof XmlFile) { XmlFile xmlFile = (XmlFile) file; if (xmlFile.getRootTag() != null) { XmlAttribute namespace = xmlFile.getRootTag().getAttribute("namespace"); if (namespace != null && namespace.getValue().equals(qualifiedName)) { xmlFiles.add(xmlFile); return false; } } } return true; } }, GlobalSearchScope.moduleScope(moduleForPsiElement)); if (xmlFiles.size() == 0) { return null; } return MyPsiXmlUtils.findTagForMethodName(xmlFiles.get(0), method.getName()); } }
<idea-plugin version="2"> <id>com.ccnode.codegenerator.MyBatisCodeHelper</id> <name>MyBatisCodeHelper</name> <version>1.5.0-SNAPSHOT</version> <vendor email="gejun123456@gmail.com">MyBatisCodeHelper</vendor> <description><![CDATA[ <div> <p><a href="https://github.com/gejun123456/MyBatisCodeHelper">GitHub</a> | <a href="https://github.com/gejun123456/MyBatisCodeHelper/issues">Issues</a></p> <p>特性</p> <ul> <li>支持mysql和oracle</li> <li><p>根据java对象生成mybatis crud代码和建表sql</p></li> <li><p><b>根据mybatis接口中的方法名生成mybatis的sql 支持find,update,delete,count方法 只需定义一个方法名就可以得到完整mybatis xml代码 可生成大部分单表操作sql 极大提升效率</b></p></li> <li><p>mybatis接口文件的mapper xml之间的相互跳转</p></li> <li><p>数据库对象更新后一键更新对应的Sql和mybatis xml文件</p></li> <li><p>默认提供insert,insertSelective,insertList,update。因为delete可以由方法名生成 默认不提供</p></li> <li><p>提供mybatis接口方法名的重构</p></li> <li>refid, resultMap跳转到到定义,支持重命名</li> <li>refid,resultMap,keyProperty,property的自动补全</li> <li><p>mybatis mapper xml文件sql的自动补全</p></li> </ul> <p>使用方法</p> <ul> <li><p>在数据库对象上使用alt+insert (generate mybatis files)来生成crud代码和建表sql(mac上使用ctrl+N)</p></li> <li><p>数据库对象添加字段后使用alt+insert (generate mybatis files) 来生成更新sql,mapper xml中的字段</p></li> <li><p>在mybatis接口的方法名上使用alt+enter来生成对应的mapper sql</p></li> <li><p>详细配置: <a href="https://github.com/gejun123456/MyBatisCodeHelper">https://github.com/gejun123456/MyBatisCodeHelper</a></p></li> <li>qq群:542735959</li> </ul> <p>Features</p> <ul> <li>Generate mybatis crud and create table sql according to domain class</li> <li><p><b>Generate mybatis sql based on mybatis interface method name, with this, you don't have to write most sql for non join query</b>,support with method name start with find,update,delete,count</p></li> <li><p>Jump from mybatis dao interface to mapper xml each other</p></li> <li><p>generate files provide insert, insertSelective,insertList,update. others can be generated by methodName</p></li> <li><p>refactor for mybatis interface method name</p></li> <li><p>jump from refid resultMap to their definition, refactor their name as well</li> <li><p>mybatis mapper sql auto completion</p></li> <li>refid,resultMap,keyProperty,property auto complete</li> <li><p>Support java + MySQL and Oracle and later will support more DB.</p></li> <li><a href="https://github.com/gejun123456/MyBatisCodeHelper">https://github.com/gejun123456/MyBatisCodeHelper</a> to learn more.</li> </ul> <p>How to use</p> <ul> <li>alt+insert (generate mybatis files) on domain class to generate mybatis files (ctrl+N on mac)</li> <li>alt+insert (generate mybatis files) on domain class to update mybatis files when domain class add field (ctrl+N on mac)</li> <li>alt+enter on dao interface method to generate mybatis mapper sql</li> <li>view more on <a href="https://github.com/gejun123456/MyBatisCodeHelper">https://github.com/gejun123456/MyBatisCodeHelper</a></li> </ul> </div> ]]></description> <change-notes><![CDATA[ <p><h4>1.4.5</h4> <ul> <li>support with enum type</li> </ul> </p> <p><h4>1.4.4</h4> <ul> <li>fix exception when start up</li> </ul> </p> <p><h4>1.4.3</h4> <ul> <li> support multiple method xml generate</li> <li> support for mybatis plus</li> <li> support domain class with protected field and static field</li> <li> support for small resolution</li> <li>bugfix - fix can't generate to path</li> </ul> </p> <p><h4>1.4.2</h4> <ul> <li>bugfix - fix Could not initialize class com.ccnode.codegenerator.freemarker.TemplateUtil</li> </ul> </p> <p><h4>1.4.1</h4> <ul> <li>bugfix - oralce insertList</li> <li>bugfix - import issue</li> </ul> </p> <p><h4>1.4</h4> <ul> <li>add support for oracle</li> <li>config use generate key</li> <li>auto complete for resultMap,refid,keyProperty,property</li> <li>support with java.sql.Timestamp java.sql.Date java.sql.Time Enum LocalDateTime LocalDate</li> <li>support update field with insertSelective</li> <li>add new icon of mybatis jump to xml ect</li> </ul> </p> <p><h4>1.3</h4> <ul> <li>add index column and hasDefault column when generate mybatis files</li> <li>jump from refid resultMap to their definition in mybatis xml. could refactor as well</li> <li>could refactor method name in xml</li> <li>add insertSelective when generate mybatis files</li> <li>generate for greaterThanOrEqualTo and lessThanOrEqualTo and betweenOrEqualTo</li> <li>add configuration to use with @Mapper</li> <li>fix not null issue for find module - bug fix</li> </ul> </p> <p><h4>1.2</h4> <ul> <li>add support for unsigned type, small int.</li> <li>check for using object type instead of primitive type</li> <li>support more auto completion for sql</li> <li>double type support - bug fix</li> <li>update field exception -bug fix</li> </ul> </p> <p> 1.1 fix doc</p> <p>1.0 use gradle, fix bugs.</p> ]]> </change-notes> <actions> <group id="com.ccnode.codegenerator.MybatisCodeHelper" text="MybatisCodeHelper" description="MybatisCodeHelper" popup="true"> <separator/> <group id="MybatisCodeHelperSubGroup"/> <add-to-group group-id="ToolsMenu" relative-to-action="GenerateJavadoc" anchor="before"/> <action id="MybatisCodeHelper.LearnMore" class="com.ccnode.codegenerator.view.ShowLearnMoreAction" text="Learn More" description="Learn_More"/> <!--<action id="mybatis.enterLicense" class="com.ccnode.codegenerator.view.EnterLicenseAction" text="Register" description="License"/>--> <!--<action id="mybatis.generator" class="com.ccnode.codegenerator.view.GenCodeAction" text="genCode" description="A test menu item"/>--> </group> <group id="MybatisCodeHelpergeneratesqlgroup"> <add-to-group group-id="GenerateGroup" relative-to-action="GenerateGetter" anchor="after"/> <action class="com.ccnode.codegenerator.view.GenCodeActionUsingAlt" id="MybatisCodeHelpergencodeusingalt" text="generate mybatis files" description="generate sql"/> </group> <group id="MyBatisCodeHelperEditorGroup"> <add-to-group group-id="EditorPopupMenu"></add-to-group> <action class="com.ccnode.codegenerator.view.MultipleMethodGenerateAction" id="MybatisMutlipleMethodGenerate" text="generate mybatis xml" description="generate mybatis xml"/> </group> <!--<group id="com.ccnode.codegenerator.mybatis.generator" text="Gen" description="CodeGenerator">--> <!--<add-to-group group-id="TestActionSubGroup" />--> <!--<action id="mybatis.generator_1" class="com.ccnode.codegenerator.view.GenCodeAction" text="genCode1" description="A test menu item1"/>--> <!--<action id="mybatis.generator_2" class="com.ccnode.codegenerator.view.GenCodeAction" text="genCode2" description="A test menu item2"/>--> <!--</group>--> </actions> <!-- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/build_number_ranges.html for description --> <idea-version since-build="141.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> --> <application-components> <component> <implementation-class>com.ccnode.codegenerator.myconfigurable.MyBatisCodeHelperApplicationComponent </implementation-class> </component> </application-components> <project-components> <component> <implementation-class>com.ccnode.codegenerator.view.datasource.MyBatisDatasourceComponent</implementation-class> </component> </project-components> <extensions defaultExtensionNs="com.intellij"> <!-- Add your extensions here --> <codeInsight.lineMarkerProvider language="JAVA" implementationClass="com.ccnode.codegenerator.view.MybatisJavaLineMarkerProvider"/> <codeInsight.lineMarkerProvider language="XML" implementationClass="com.ccnode.codegenerator.view.MybatisXmlLineMarkerProvider"/> <completion.contributor language="JAVA" implementationClass="com.ccnode.codegenerator.view.completion.MethodNameCompletionContributor"/> <completion.contributor language="XML" implementationClass="com.ccnode.codegenerator.view.completion.MapperSqlCompletionContributor"/> <completion.contributor language="XML" implementationClass="com.ccnode.codegenerator.view.completion.MapperXmlTagElementCompletionContributor"/> <inspectionToolProvider implementation="com.ccnode.codegenerator.view.inspection.MyBatisMethodNotFoundInXmlProvider"/> <psi.referenceContributor implementation="com.ccnode.codegenerator.view.MyBatisJavaToXmlPsiReferenceContributor"/> <psi.referenceContributor implementation="com.ccnode.codegenerator.view.MyBatisXmlAttributeReferenceContributor"/> <applicationConfigurable instance="com.ccnode.codegenerator.myconfigurable.MyConfigurable" id="MyBatisCodeHelper" displayName="MyBatisCodeHelper"/> <toolWindow id="Mybatis datasource" secondary="true" anchor="right" factoryClass="com.ccnode.codegenerator.view.datasource.MyBatisDatasourceToolWindow"/> <!--<renamePsiElementProcessor implementation="com.ccnode.codegenerator.view.rename.RenameResultMapSqlProcessor" order="first"/>--> <intentionAction> <className>com.ccnode.codegenerator.view.GenerateMethodXmlAction</className> <!--<category>Conditional Operator</category>--> <!--<descriptionDirectoryName>ConditionalOperatorConvertor</descriptionDirectoryName>--> <!--<bundleName>Generate daoxml</bundleName>--> </intentionAction> <projectService serviceInterface="com.ccnode.codegenerator.view.completion.MysqlCompleteCacheInteface" serviceImplementation="com.ccnode.codegenerator.view.completion.MyBatisSqlCompleteCache"/> <!--<intentionAction>--> <!--<className>com.ccnode.codegenerator.view.GenerateDaoXmlFixAction</className>--> <!--<category>Conditional Operator</category>--> <!--<descriptionDirectoryName>ConditionalOperatorConvertor</descriptionDirectoryName>--> <!--</intentionAction>--> </extensions> <!--<project-components>--> <!--<component>--> <!--<!–<implementation-class>com.intellij.codeInsight.intention.Condition</implementation-class>–>--> <!--</component>--> <!--</project-components>--> <actions> <!-- Add your actions here --> </actions> </idea-plugin>