com.viiyue.plugins.mybatis.MyBatisMapperLanguageDriver Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mybatis-mapper Show documentation
Show all versions of mybatis-mapper Show documentation
Mybatis generic mapper plugin for solving most basic operations,
simplifying sql syntax and improving dynamic execution efficiency
/**
* Copyright (C) 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.viiyue.plugins.mybatis;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Locale;
import org.apache.commons.lang3.time.StopWatch;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.scripting.xmltags.MixedSqlNode;
import org.apache.ibatis.scripting.xmltags.SqlNode;
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
import org.apache.ibatis.scripting.xmltags.XMLScriptBuilder;
import org.apache.ibatis.session.Configuration;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.viiyue.plugins.mybatis.exceptions.VersionConflictException;
import com.viiyue.plugins.mybatis.mapper.Marker;
import com.viiyue.plugins.mybatis.scripting.MyBatisMapperBuilder;
import com.viiyue.plugins.mybatis.scripting.MyBatisMapperSqlSource;
import com.viiyue.plugins.mybatis.template.TemplateHandler;
import com.viiyue.plugins.mybatis.utils.ClassUtil;
import com.viiyue.plugins.mybatis.utils.FieldUtil;
import com.viiyue.plugins.mybatis.utils.GenericTypeUtil;
import com.viiyue.plugins.mybatis.utils.LoggerUtil;
import com.viiyue.plugins.mybatis.utils.MethodUtil;
import com.viiyue.plugins.mybatis.utils.ObjectUtil;
import com.viiyue.plugins.mybatis.utils.StringUtil;
/**
*
* The default mybatis-mapper will only refactor the provider annotation mapper.
* If you want to work in the xml block to achieve the same functionality, then
* you must change the default language driver for mybatis to this class.
*
* The current language driver not only supports the use of template syntax, but
* also supports the various syntax of native xml, meaning that the two can be
* mixed development.
*
*
* In a pure mybatis environment, you can configure it like this:
*
* # mybatis.xml #
* <configuration>
*
* <!-- Mybatis-mapper preference configuration -->
* <properties resource="jdbc.properties">
* <property name="enableLogger" value="true"/>
* <property name="enableRuntimeLog" value="true"/>
* <property name="enableCompilationLog" value="false"/>
* <property name="enableKeywordsToUppercase" value="true"/>
* <property name="databaseColumnStyle" value="#"/>
* </properties>
*
* <settings>
* <setting name="defaultScriptingLanguage" value="com.viiyue.plugins.mybatis.MyBatisMapperLanguageDriver"/>
* </settings>
*
* <!-- It is generally recommended to configure the bean alias -->
* <!-- so that it is easier to write and the plugin works better. -->
* <typeAliases>
* <package name="your.model.classpath"/>
* <package name="or.more"/>
* </typeAliases>
* </configuration>
*
* @author tangxbai
* @since 1.1.0
* @since mybatis 3.2.4
* @see org.apache.ibatis.scripting.xmltags.XMLLanguageDriver
*/
public final class MyBatisMapperLanguageDriver extends XMLLanguageDriver {
private final StopWatch monitor = new StopWatch();
// Provide XMLScriptBuilder#parseDynamicTags method by reflection
private static class Holder {
private static final Method parseDynamicTags = MethodUtil.findMethod(
XMLScriptBuilder.class, true, "parseDynamicTags", XNode.class
);
}
@Override
public SqlSource createSqlSource( Configuration configuration, XNode script, Class> parameterType ) {
// Build XML script parsing object
XMLScriptBuilder builder = new XMLScriptBuilder( configuration, script, parameterType ); // since mybase 3.2.4+
XNode parent = script.getParent();
if ( parent == null ) {
return builder.parseScriptNode();
}
// If it is a normal mapper/statement node,
// continue to judge whether it conforms to the template syntax.
String classpath = parent.getStringAttribute( "namespace" );
if ( StringUtil.isBlank( classpath ) ) {
return builder.parseScriptNode();
}
// The target mapper interface does not intervene if it does not
// inherit the Marker interface.
Class> mapperInterface = ClassUtil.forName( classpath );
if ( !ClassUtil.isAssignable( mapperInterface, Marker.class, false ) ) {
return builder.parseScriptNode();
}
// Record template syntax parsing time consumption start time
this.monitor.reset();
this.monitor.start();
Node content = script.getNode();
// Get the basic information of the statement from the node,
// only used as template syntax analysis.
String methodName = script.getStringAttribute( "id" ); // Id is the method name
String namespace = classpath + "." + methodName; // Namespace is the classpath
Class> modelBeanType = GenericTypeUtil.getInterfaceGenericType( mapperInterface, 0 );
Class> returnBeanType = GenericTypeUtil.getInterfaceGenericType( mapperInterface, 1 );
SqlCommandType commandType = SqlCommandType.valueOf( script.getName().toUpperCase( Locale.ENGLISH ) );
// The first time to register the Mapper interface,
// if there is no xml file corresponding to the interface,
// it will not be executed here, but if there is an xml file,
// it will be executed first.
MyBatisMapperBuilder.registry.registerMapper( configuration, mapperInterface, modelBeanType, returnBeanType );
// Template syntax parsing of all text nodes of a statement
StringBuffer originals = new StringBuffer();
StringBuffer templates = new StringBuffer();
nodeTemplateParaser( content, configuration, commandType, modelBeanType, originals, templates );
// Print script template compilation log
String template = templates.toString();
if ( LoggerUtil.isEnableCompilationLog() ) {
String resource = FieldUtil.readValue( ErrorContext.instance(), "resource" );
LoggerUtil.printCompilationLog( namespace, "<" + script.getName() + ">", originals.toString(), template, resource, monitor );
}
// Handling xml syntax nodes with mybatis' XMLScriptBuilder object
SqlNode parsedSqlNode = getParasedSqlNode( builder, script, content );
// Check whether the dynamic template syntax is included in
// the sql script in advance to avoid real-time parsing in the sql source.
boolean isNeedDynamicProcessing = TemplateHandler.isNeedDynamicProcessing( template );
// After the parsing is completed, the sql source dedicated
// to mybatis-mapper is generated.
// When the actual mapper method is actually called,
// some dynamic template syntax in some sql scripts will be parsed.
return new MyBatisMapperSqlSource( parsedSqlNode, configuration,
commandType, modelBeanType, namespace, isNeedDynamicProcessing, true );
}
private void nodeTemplateParaser(
Node node,
Configuration configuration,
SqlCommandType commandType,
Class> modelBeanType,
StringBuffer originals,
StringBuffer templates ) {
NodeList children = node.getChildNodes();
for ( int i = 0, size = children.getLength(); i < size; i ++ ) {
Node item = children.item( i );
Short nodeType = item.getNodeType();
if ( ObjectUtil.equalsAny( nodeType, Node.CDATA_SECTION_NODE, Node.TEXT_NODE ) ) {
String compiled = null;
String original = item.getTextContent();
if ( StringUtil.isNotBlank( original ) ) {
original = TemplateHandler.processTextComments( original );
compiled = TemplateHandler.processStaticTemplate( configuration, commandType, original, modelBeanType );
compiled = TemplateHandler.processKeyWordsTemplate( configuration, compiled );
originals.append( original ).append( " " );
templates.append( compiled ).append( " " );
} else {
compiled = Constants.EMPTY; // Space, \n, \t, \s
}
item.setNodeValue( compiled );
} else if ( nodeType == Node.ELEMENT_NODE ) {
String nodeName = "<#" + item.getNodeName() + " ";
originals.append( nodeName );
templates.append( nodeName );
nodeTemplateParaser( item, configuration, commandType, modelBeanType, originals, templates );
templates.append( ">" );
originals.append( ">" );
}
}
}
private SqlNode getParasedSqlNode( XMLScriptBuilder builder, XNode script, Node content ) {
Object contents = MethodUtil.invoke( builder, Holder.parseDynamicTags, script.newXNode( content ) );
// since mybatis 3.4.6+
if ( contents instanceof SqlNode ) {
return ( SqlNode ) contents;
}
// since mybatis( 3.2.4 ~ 3.4.6 )
if ( contents instanceof List ) {
return new MixedSqlNode( ( List ) contents );
}
throw new VersionConflictException( "Get the xml script parsing exception, it may be a version conflict" );
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy