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>