Forráskód Böngészése

没有XML文件 同样注入CRUD方法

Caratacus 8 éve
szülő
commit
b3d79d1a9e

+ 47 - 0
mybatis-plus/src/main/java/com/baomidou/mybatisplus/MybatisConfiguration.java

@@ -15,10 +15,14 @@
  */
 package com.baomidou.mybatisplus;
 
+import java.util.Set;
+import java.util.concurrent.ConcurrentSkipListSet;
 import java.util.logging.Logger;
 
+import org.apache.ibatis.binding.MapperRegistry;
 import org.apache.ibatis.mapping.MappedStatement;
 import org.apache.ibatis.session.Configuration;
+import org.apache.ibatis.session.SqlSession;
 
 import com.baomidou.mybatisplus.mapper.AutoSqlInjector;
 import com.baomidou.mybatisplus.mapper.DBType;
@@ -29,6 +33,9 @@ import com.baomidou.mybatisplus.mapper.ISqlInjector;
  * <p>
  * replace default Configuration class
  * </p>
+ * <p>
+ * Caratacus 2016/9/25 replace mapperRegistry
+ * </p>
  *
  * @author hubin
  * @Date 2016-01-23
@@ -51,6 +58,10 @@ public class MybatisConfiguration extends Configuration {
 	 * SQL 注入器,实现 ISqlInjector 或继承 AutoSqlInjector 自定义方法
 	 */
 	public static ISqlInjector SQL_INJECTOR = new AutoSqlInjector();
+	/*
+	 * Mapper 注册
+	 */
+	public final MybatisPulsMapperRegistry mybatisPulsMapperRegistry = new MybatisPulsMapperRegistry(this);
 
 	/*
 	 * 元对象字段填充控制器
@@ -61,6 +72,10 @@ public class MybatisConfiguration extends Configuration {
 	 * 是否刷新mapper
 	 */
 	public static boolean IS_REFRESH = false;
+	/**
+	 * 缓存注册标识
+	 */
+	public static Set<String> MAPPER_REGISTRY_CACHE = new ConcurrentSkipListSet<String>();
 
 	/**
 	 * 初始化调用
@@ -107,5 +122,37 @@ public class MybatisConfiguration extends Configuration {
 		}
 		super.setDefaultScriptingLanguage(driver);
 	}
+	@Override
+	public <T> void addMapper(Class<T> type) {
+		mybatisPulsMapperRegistry.addMapper(type);
+	}
+
+	/**
+	 * Mapper注册
+	 */
+	@Override
+	public MapperRegistry getMapperRegistry() {
+		return mybatisPulsMapperRegistry;
+	}
+
+	@Override
+	public void addMappers(String packageName, Class<?> superType) {
+		mybatisPulsMapperRegistry.addMappers(packageName, superType);
+	}
+
+	@Override
+	public void addMappers(String packageName) {
+		mybatisPulsMapperRegistry.addMappers(packageName);
+	}
+
+	@Override
+	public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
+		return mybatisPulsMapperRegistry.getMapper(type, sqlSession);
+	}
+
+	@Override
+	public boolean hasMapper(Class<?> type) {
+		return mybatisPulsMapperRegistry.hasMapper(type);
+	}
 
 }

+ 618 - 0
mybatis-plus/src/main/java/com/baomidou/mybatisplus/MybatisPlusMapperBuilder.java

@@ -0,0 +1,618 @@
+/**
+ *    Copyright 2009-2016 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.baomidou.mybatisplus;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Array;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.ibatis.annotations.Arg;
+import org.apache.ibatis.annotations.CacheNamespace;
+import org.apache.ibatis.annotations.CacheNamespaceRef;
+import org.apache.ibatis.annotations.Case;
+import org.apache.ibatis.annotations.ConstructorArgs;
+import org.apache.ibatis.annotations.Delete;
+import org.apache.ibatis.annotations.DeleteProvider;
+import org.apache.ibatis.annotations.Insert;
+import org.apache.ibatis.annotations.InsertProvider;
+import org.apache.ibatis.annotations.Lang;
+import org.apache.ibatis.annotations.MapKey;
+import org.apache.ibatis.annotations.Options;
+import org.apache.ibatis.annotations.Result;
+import org.apache.ibatis.annotations.ResultMap;
+import org.apache.ibatis.annotations.ResultType;
+import org.apache.ibatis.annotations.Results;
+import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.annotations.SelectKey;
+import org.apache.ibatis.annotations.SelectProvider;
+import org.apache.ibatis.annotations.TypeDiscriminator;
+import org.apache.ibatis.annotations.Update;
+import org.apache.ibatis.annotations.UpdateProvider;
+import org.apache.ibatis.annotations.Options.FlushCachePolicy;
+import org.apache.ibatis.binding.BindingException;
+import org.apache.ibatis.binding.MapperMethod.ParamMap;
+import org.apache.ibatis.builder.BuilderException;
+import org.apache.ibatis.builder.IncompleteElementException;
+import org.apache.ibatis.builder.MapperBuilderAssistant;
+import org.apache.ibatis.builder.annotation.MapperAnnotationBuilder;
+import org.apache.ibatis.builder.annotation.MethodResolver;
+import org.apache.ibatis.builder.annotation.ProviderSqlSource;
+import org.apache.ibatis.builder.xml.XMLMapperBuilder;
+import org.apache.ibatis.cursor.Cursor;
+import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
+import org.apache.ibatis.executor.keygen.KeyGenerator;
+import org.apache.ibatis.executor.keygen.NoKeyGenerator;
+import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
+import org.apache.ibatis.io.Resources;
+import org.apache.ibatis.mapping.Discriminator;
+import org.apache.ibatis.mapping.FetchType;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.mapping.ResultFlag;
+import org.apache.ibatis.mapping.ResultMapping;
+import org.apache.ibatis.mapping.ResultSetType;
+import org.apache.ibatis.mapping.SqlCommandType;
+import org.apache.ibatis.mapping.SqlSource;
+import org.apache.ibatis.mapping.StatementType;
+import org.apache.ibatis.reflection.TypeParameterResolver;
+import org.apache.ibatis.scripting.LanguageDriver;
+import org.apache.ibatis.session.Configuration;
+import org.apache.ibatis.session.ResultHandler;
+import org.apache.ibatis.session.RowBounds;
+import org.apache.ibatis.type.JdbcType;
+import org.apache.ibatis.type.TypeHandler;
+import org.apache.ibatis.type.UnknownTypeHandler;
+
+import com.baomidou.mybatisplus.MybatisConfiguration;
+
+/**
+ * 继承 MapperAnnotationBuilder 没有XML配置文件注入基础CRUD方法
+ * 
+ * @author Caratacus
+ */
+public class MybatisPlusMapperBuilder extends MapperAnnotationBuilder {
+
+	private final Set<Class<? extends Annotation>> sqlAnnotationTypes = new HashSet<Class<? extends Annotation>>();
+	private final Set<Class<? extends Annotation>> sqlProviderAnnotationTypes = new HashSet<Class<? extends Annotation>>();
+
+	private Configuration configuration;
+	private MapperBuilderAssistant assistant;
+	private Class<?> type;
+
+	public MybatisPlusMapperBuilder(Configuration configuration, Class<?> type) {
+		super(configuration, type);
+		String resource = type.getName().replace('.', '/') + ".java (best guess)";
+		this.assistant = new MapperBuilderAssistant(configuration, resource);
+		this.configuration = configuration;
+		this.type = type;
+
+		sqlAnnotationTypes.add(Select.class);
+		sqlAnnotationTypes.add(Insert.class);
+		sqlAnnotationTypes.add(Update.class);
+		sqlAnnotationTypes.add(Delete.class);
+
+		sqlProviderAnnotationTypes.add(SelectProvider.class);
+		sqlProviderAnnotationTypes.add(InsertProvider.class);
+		sqlProviderAnnotationTypes.add(UpdateProvider.class);
+		sqlProviderAnnotationTypes.add(DeleteProvider.class);
+	}
+
+	public void parse() {
+		String resource = type.toString();
+
+		if (!configuration.isResourceLoaded(resource)) {
+			loadXmlResource();
+			configuration.addLoadedResource(resource);
+			assistant.setCurrentNamespace(type.getName());
+			parseCache();
+			parseCacheRef();
+			Method[] methods = type.getMethods();
+			// 注入增删改查方法
+			MybatisConfiguration.SQL_INJECTOR.inspectInject(configuration, assistant, type);
+			for (Method method : methods) {
+				try {
+					// issue #237
+					if (!method.isBridge()) {
+						parseStatement(method);
+					}
+				} catch (IncompleteElementException e) {
+					configuration.addIncompleteMethod(new MethodResolver(this, method));
+				}
+			}
+		}
+		parsePendingMethods();
+	}
+
+	private void parsePendingMethods() {
+		Collection<MethodResolver> incompleteMethods = configuration.getIncompleteMethods();
+		synchronized (incompleteMethods) {
+			Iterator<MethodResolver> iter = incompleteMethods.iterator();
+			while (iter.hasNext()) {
+				try {
+					iter.next().resolve();
+					iter.remove();
+				} catch (IncompleteElementException e) {
+					// This method is still missing a resource
+				}
+			}
+		}
+	}
+
+	private void loadXmlResource() {
+		// Spring may not know the real resource name so we check a flag
+		// to prevent loading again a resource twice
+		// this flag is set at XMLMapperBuilder#bindMapperForNamespace
+		if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
+			String xmlResource = type.getName().replace('.', '/') + ".xml";
+			InputStream inputStream = null;
+			try {
+				inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
+			} catch (IOException e) {
+				// ignore, resource is not required
+			}
+			if (inputStream != null) {
+				XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource,
+						configuration.getSqlFragments(), type.getName());
+				xmlParser.parse();
+			}
+		}
+	}
+
+	private void parseCache() {
+		CacheNamespace cacheDomain = type.getAnnotation(CacheNamespace.class);
+		if (cacheDomain != null) {
+			Integer size = cacheDomain.size() == 0 ? null : cacheDomain.size();
+			Long flushInterval = cacheDomain.flushInterval() == 0 ? null : cacheDomain.flushInterval();
+			assistant.useNewCache(cacheDomain.implementation(), cacheDomain.eviction(), flushInterval, size,
+					cacheDomain.readWrite(), cacheDomain.blocking(), null);
+		}
+	}
+
+	private void parseCacheRef() {
+		CacheNamespaceRef cacheDomainRef = type.getAnnotation(CacheNamespaceRef.class);
+		if (cacheDomainRef != null) {
+			assistant.useCacheRef(cacheDomainRef.value().getName());
+		}
+	}
+
+	private String parseResultMap(Method method) {
+		Class<?> returnType = getReturnType(method);
+		ConstructorArgs args = method.getAnnotation(ConstructorArgs.class);
+		Results results = method.getAnnotation(Results.class);
+		TypeDiscriminator typeDiscriminator = method.getAnnotation(TypeDiscriminator.class);
+		String resultMapId = generateResultMapName(method);
+		applyResultMap(resultMapId, returnType, argsIf(args), resultsIf(results), typeDiscriminator);
+		return resultMapId;
+	}
+
+	private String generateResultMapName(Method method) {
+		Results results = method.getAnnotation(Results.class);
+		if (results != null && !results.id().isEmpty()) {
+			return type.getName() + "." + results.id();
+		}
+		StringBuilder suffix = new StringBuilder();
+		for (Class<?> c : method.getParameterTypes()) {
+			suffix.append("-");
+			suffix.append(c.getSimpleName());
+		}
+		if (suffix.length() < 1) {
+			suffix.append("-void");
+		}
+		return type.getName() + "." + method.getName() + suffix;
+	}
+
+	private void applyResultMap(String resultMapId, Class<?> returnType, Arg[] args, Result[] results,
+			TypeDiscriminator discriminator) {
+		List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
+		applyConstructorArgs(args, returnType, resultMappings);
+		applyResults(results, returnType, resultMappings);
+		Discriminator disc = applyDiscriminator(resultMapId, returnType, discriminator);
+		// TODO add AutoMappingBehaviour
+		assistant.addResultMap(resultMapId, returnType, null, disc, resultMappings, null);
+		createDiscriminatorResultMaps(resultMapId, returnType, discriminator);
+	}
+
+	private void createDiscriminatorResultMaps(String resultMapId, Class<?> resultType, TypeDiscriminator discriminator) {
+		if (discriminator != null) {
+			for (Case c : discriminator.cases()) {
+				String caseResultMapId = resultMapId + "-" + c.value();
+				List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
+				// issue #136
+				applyConstructorArgs(c.constructArgs(), resultType, resultMappings);
+				applyResults(c.results(), resultType, resultMappings);
+				// TODO add AutoMappingBehaviour
+				assistant.addResultMap(caseResultMapId, c.type(), resultMapId, null, resultMappings, null);
+			}
+		}
+	}
+
+	private Discriminator applyDiscriminator(String resultMapId, Class<?> resultType, TypeDiscriminator discriminator) {
+		if (discriminator != null) {
+			String column = discriminator.column();
+			Class<?> javaType = discriminator.javaType() == void.class ? String.class : discriminator.javaType();
+			JdbcType jdbcType = discriminator.jdbcType() == JdbcType.UNDEFINED ? null : discriminator.jdbcType();
+			@SuppressWarnings("unchecked")
+			Class<? extends TypeHandler<?>> typeHandler = (Class<? extends TypeHandler<?>>) (discriminator.typeHandler() == UnknownTypeHandler.class ? null
+					: discriminator.typeHandler());
+			Case[] cases = discriminator.cases();
+			Map<String, String> discriminatorMap = new HashMap<String, String>();
+			for (Case c : cases) {
+				String value = c.value();
+				String caseResultMapId = resultMapId + "-" + value;
+				discriminatorMap.put(value, caseResultMapId);
+			}
+			return assistant.buildDiscriminator(resultType, column, javaType, jdbcType, typeHandler, discriminatorMap);
+		}
+		return null;
+	}
+
+	void parseStatement(Method method) {
+		Class<?> parameterTypeClass = getParameterType(method);
+		LanguageDriver languageDriver = getLanguageDriver(method);
+		SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
+		if (sqlSource != null) {
+			Options options = method.getAnnotation(Options.class);
+			final String mappedStatementId = type.getName() + "." + method.getName();
+			Integer fetchSize = null;
+			Integer timeout = null;
+			StatementType statementType = StatementType.PREPARED;
+			ResultSetType resultSetType = ResultSetType.FORWARD_ONLY;
+			SqlCommandType sqlCommandType = getSqlCommandType(method);
+			boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
+			boolean flushCache = !isSelect;
+			boolean useCache = isSelect;
+
+			KeyGenerator keyGenerator;
+			String keyProperty = "id";
+			String keyColumn = null;
+			if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
+				// first check for SelectKey annotation - that overrides
+				// everything else
+				SelectKey selectKey = method.getAnnotation(SelectKey.class);
+				if (selectKey != null) {
+					keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method),
+							languageDriver);
+					keyProperty = selectKey.keyProperty();
+				} else if (options == null) {
+					keyGenerator = configuration.isUseGeneratedKeys() ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
+				} else {
+					keyGenerator = options.useGeneratedKeys() ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
+					keyProperty = options.keyProperty();
+					keyColumn = options.keyColumn();
+				}
+			} else {
+				keyGenerator = new NoKeyGenerator();
+			}
+
+			if (options != null) {
+				if (FlushCachePolicy.TRUE.equals(options.flushCache())) {
+					flushCache = true;
+				} else if (FlushCachePolicy.FALSE.equals(options.flushCache())) {
+					flushCache = false;
+				}
+				useCache = options.useCache();
+				fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; // issue
+																																// #348
+				timeout = options.timeout() > -1 ? options.timeout() : null;
+				statementType = options.statementType();
+				resultSetType = options.resultSetType();
+			}
+
+			String resultMapId = null;
+			ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);
+			if (resultMapAnnotation != null) {
+				String[] resultMaps = resultMapAnnotation.value();
+				StringBuilder sb = new StringBuilder();
+				for (String resultMap : resultMaps) {
+					if (sb.length() > 0) {
+						sb.append(",");
+					}
+					sb.append(resultMap);
+				}
+				resultMapId = sb.toString();
+			} else if (isSelect) {
+				resultMapId = parseResultMap(method);
+			}
+
+			assistant.addMappedStatement(mappedStatementId, sqlSource, statementType, sqlCommandType, fetchSize, timeout,
+			// ParameterMapID
+					null, parameterTypeClass, resultMapId, getReturnType(method), resultSetType, flushCache, useCache,
+					// TODO gcode issue #577
+					false, keyGenerator, keyProperty, keyColumn,
+					// DatabaseID
+					null, languageDriver,
+					// ResultSets
+					options != null ? nullOrEmpty(options.resultSets()) : null);
+		}
+	}
+
+	private LanguageDriver getLanguageDriver(Method method) {
+		Lang lang = method.getAnnotation(Lang.class);
+		Class<?> langClass = null;
+		if (lang != null) {
+			langClass = lang.value();
+		}
+		return assistant.getLanguageDriver(langClass);
+	}
+
+	private Class<?> getParameterType(Method method) {
+		Class<?> parameterType = null;
+		Class<?>[] parameterTypes = method.getParameterTypes();
+		for (Class<?> currentParameterType : parameterTypes) {
+			if (!RowBounds.class.isAssignableFrom(currentParameterType)
+					&& !ResultHandler.class.isAssignableFrom(currentParameterType)) {
+				if (parameterType == null) {
+					parameterType = currentParameterType;
+				} else {
+					// issue #135
+					parameterType = ParamMap.class;
+				}
+			}
+		}
+		return parameterType;
+	}
+
+	private Class<?> getReturnType(Method method) {
+		Class<?> returnType = method.getReturnType();
+		Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, type);
+		if (resolvedReturnType instanceof Class) {
+			returnType = (Class<?>) resolvedReturnType;
+			if (returnType.isArray()) {
+				returnType = returnType.getComponentType();
+			}
+			// gcode issue #508
+			if (void.class.equals(returnType)) {
+				ResultType rt = method.getAnnotation(ResultType.class);
+				if (rt != null) {
+					returnType = rt.value();
+				}
+			}
+		} else if (resolvedReturnType instanceof ParameterizedType) {
+			ParameterizedType parameterizedType = (ParameterizedType) resolvedReturnType;
+			Class<?> rawType = (Class<?>) parameterizedType.getRawType();
+			if (Collection.class.isAssignableFrom(rawType) || Cursor.class.isAssignableFrom(rawType)) {
+				Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
+				if (actualTypeArguments != null && actualTypeArguments.length == 1) {
+					Type returnTypeParameter = actualTypeArguments[0];
+					if (returnTypeParameter instanceof Class<?>) {
+						returnType = (Class<?>) returnTypeParameter;
+					} else if (returnTypeParameter instanceof ParameterizedType) {
+						// (gcode issue #443) actual type can be a also a
+						// parameterized type
+						returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType();
+					} else if (returnTypeParameter instanceof GenericArrayType) {
+						Class<?> componentType = (Class<?>) ((GenericArrayType) returnTypeParameter).getGenericComponentType();
+						// (gcode issue #525) support List<byte[]>
+						returnType = Array.newInstance(componentType, 0).getClass();
+					}
+				}
+			} else if (method.isAnnotationPresent(MapKey.class) && Map.class.isAssignableFrom(rawType)) {
+				// (gcode issue 504) Do not look into Maps if there is not
+				// MapKey annotation
+				Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
+				if (actualTypeArguments != null && actualTypeArguments.length == 2) {
+					Type returnTypeParameter = actualTypeArguments[1];
+					if (returnTypeParameter instanceof Class<?>) {
+						returnType = (Class<?>) returnTypeParameter;
+					} else if (returnTypeParameter instanceof ParameterizedType) {
+						// (gcode issue 443) actual type can be a also a
+						// parameterized type
+						returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType();
+					}
+				}
+			}
+		}
+
+		return returnType;
+	}
+
+	private SqlSource getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver) {
+		try {
+			Class<? extends Annotation> sqlAnnotationType = getSqlAnnotationType(method);
+			Class<? extends Annotation> sqlProviderAnnotationType = getSqlProviderAnnotationType(method);
+			if (sqlAnnotationType != null) {
+				if (sqlProviderAnnotationType != null) {
+					throw new BindingException("You cannot supply both a static SQL and SqlProvider to method named "
+							+ method.getName());
+				}
+				Annotation sqlAnnotation = method.getAnnotation(sqlAnnotationType);
+				final String[] strings = (String[]) sqlAnnotation.getClass().getMethod("value").invoke(sqlAnnotation);
+				return buildSqlSourceFromStrings(strings, parameterType, languageDriver);
+			} else if (sqlProviderAnnotationType != null) {
+				Annotation sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType);
+				return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation);
+			}
+			return null;
+		} catch (Exception e) {
+			throw new BuilderException("Could not find value method on SQL annotation.  Cause: " + e, e);
+		}
+	}
+
+	private SqlSource buildSqlSourceFromStrings(String[] strings, Class<?> parameterTypeClass, LanguageDriver languageDriver) {
+		final StringBuilder sql = new StringBuilder();
+		for (String fragment : strings) {
+			sql.append(fragment);
+			sql.append(" ");
+		}
+		return languageDriver.createSqlSource(configuration, sql.toString().trim(), parameterTypeClass);
+	}
+
+	private SqlCommandType getSqlCommandType(Method method) {
+		Class<? extends Annotation> type = getSqlAnnotationType(method);
+
+		if (type == null) {
+			type = getSqlProviderAnnotationType(method);
+
+			if (type == null) {
+				return SqlCommandType.UNKNOWN;
+			}
+
+			if (type == SelectProvider.class) {
+				type = Select.class;
+			} else if (type == InsertProvider.class) {
+				type = Insert.class;
+			} else if (type == UpdateProvider.class) {
+				type = Update.class;
+			} else if (type == DeleteProvider.class) {
+				type = Delete.class;
+			}
+		}
+
+		return SqlCommandType.valueOf(type.getSimpleName().toUpperCase(Locale.ENGLISH));
+	}
+
+	private Class<? extends Annotation> getSqlAnnotationType(Method method) {
+		return chooseAnnotationType(method, sqlAnnotationTypes);
+	}
+
+	private Class<? extends Annotation> getSqlProviderAnnotationType(Method method) {
+		return chooseAnnotationType(method, sqlProviderAnnotationTypes);
+	}
+
+	private Class<? extends Annotation> chooseAnnotationType(Method method, Set<Class<? extends Annotation>> types) {
+		for (Class<? extends Annotation> type : types) {
+			Annotation annotation = method.getAnnotation(type);
+			if (annotation != null) {
+				return type;
+			}
+		}
+		return null;
+	}
+
+	private void applyResults(Result[] results, Class<?> resultType, List<ResultMapping> resultMappings) {
+		for (Result result : results) {
+			List<ResultFlag> flags = new ArrayList<ResultFlag>();
+			if (result.id()) {
+				flags.add(ResultFlag.ID);
+			}
+			@SuppressWarnings("unchecked")
+			Class<? extends TypeHandler<?>> typeHandler = (Class<? extends TypeHandler<?>>) ((result.typeHandler() == UnknownTypeHandler.class) ? null
+					: result.typeHandler());
+			ResultMapping resultMapping = assistant.buildResultMapping(resultType, nullOrEmpty(result.property()),
+					nullOrEmpty(result.column()), result.javaType() == void.class ? null : result.javaType(),
+					result.jdbcType() == JdbcType.UNDEFINED ? null : result.jdbcType(),
+					hasNestedSelect(result) ? nestedSelectId(result) : null, null, null, null, typeHandler, flags, null, null,
+					isLazy(result));
+			resultMappings.add(resultMapping);
+		}
+	}
+
+	private String nestedSelectId(Result result) {
+		String nestedSelect = result.one().select();
+		if (nestedSelect.length() < 1) {
+			nestedSelect = result.many().select();
+		}
+		if (!nestedSelect.contains(".")) {
+			nestedSelect = type.getName() + "." + nestedSelect;
+		}
+		return nestedSelect;
+	}
+
+	private boolean isLazy(Result result) {
+		boolean isLazy = configuration.isLazyLoadingEnabled();
+		if (result.one().select().length() > 0 && FetchType.DEFAULT != result.one().fetchType()) {
+			isLazy = (result.one().fetchType() == FetchType.LAZY);
+		} else if (result.many().select().length() > 0 && FetchType.DEFAULT != result.many().fetchType()) {
+			isLazy = (result.many().fetchType() == FetchType.LAZY);
+		}
+		return isLazy;
+	}
+
+	private boolean hasNestedSelect(Result result) {
+		if (result.one().select().length() > 0 && result.many().select().length() > 0) {
+			throw new BuilderException("Cannot use both @One and @Many annotations in the same @Result");
+		}
+		return result.one().select().length() > 0 || result.many().select().length() > 0;
+	}
+
+	private void applyConstructorArgs(Arg[] args, Class<?> resultType, List<ResultMapping> resultMappings) {
+		for (Arg arg : args) {
+			List<ResultFlag> flags = new ArrayList<ResultFlag>();
+			flags.add(ResultFlag.CONSTRUCTOR);
+			if (arg.id()) {
+				flags.add(ResultFlag.ID);
+			}
+			@SuppressWarnings("unchecked")
+			Class<? extends TypeHandler<?>> typeHandler = (Class<? extends TypeHandler<?>>) (arg.typeHandler() == UnknownTypeHandler.class ? null
+					: arg.typeHandler());
+			ResultMapping resultMapping = assistant.buildResultMapping(resultType, null, nullOrEmpty(arg.column()),
+					arg.javaType() == void.class ? null : arg.javaType(),
+					arg.jdbcType() == JdbcType.UNDEFINED ? null : arg.jdbcType(), nullOrEmpty(arg.select()),
+					nullOrEmpty(arg.resultMap()), null, null, typeHandler, flags, null, null, false);
+			resultMappings.add(resultMapping);
+		}
+	}
+
+	private String nullOrEmpty(String value) {
+		return value == null || value.trim().length() == 0 ? null : value;
+	}
+
+	private Result[] resultsIf(Results results) {
+		return results == null ? new Result[0] : results.value();
+	}
+
+	private Arg[] argsIf(ConstructorArgs args) {
+		return args == null ? new Arg[0] : args.value();
+	}
+
+	private KeyGenerator handleSelectKeyAnnotation(SelectKey selectKeyAnnotation, String baseStatementId,
+			Class<?> parameterTypeClass, LanguageDriver languageDriver) {
+		String id = baseStatementId + SelectKeyGenerator.SELECT_KEY_SUFFIX;
+		Class<?> resultTypeClass = selectKeyAnnotation.resultType();
+		StatementType statementType = selectKeyAnnotation.statementType();
+		String keyProperty = selectKeyAnnotation.keyProperty();
+		String keyColumn = selectKeyAnnotation.keyColumn();
+		boolean executeBefore = selectKeyAnnotation.before();
+
+		// defaults
+		boolean useCache = false;
+		KeyGenerator keyGenerator = new NoKeyGenerator();
+		Integer fetchSize = null;
+		Integer timeout = null;
+		boolean flushCache = false;
+		String parameterMap = null;
+		String resultMap = null;
+		ResultSetType resultSetTypeEnum = null;
+
+		SqlSource sqlSource = buildSqlSourceFromStrings(selectKeyAnnotation.statement(), parameterTypeClass, languageDriver);
+		SqlCommandType sqlCommandType = SqlCommandType.SELECT;
+
+		assistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap,
+				parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, false, keyGenerator,
+				keyProperty, keyColumn, null, languageDriver, null);
+
+		id = assistant.applyCurrentNamespace(id, false);
+
+		MappedStatement keyStatement = configuration.getMappedStatement(id, false);
+		SelectKeyGenerator answer = new SelectKeyGenerator(keyStatement, executeBefore);
+		configuration.addKeyGenerator(id, answer);
+		return answer;
+	}
+
+}

+ 115 - 0
mybatis-plus/src/main/java/com/baomidou/mybatisplus/MybatisPulsMapperRegistry.java

@@ -0,0 +1,115 @@
+/**
+ *    Copyright 2009-2015 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.baomidou.mybatisplus;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.ibatis.binding.BindingException;
+import org.apache.ibatis.binding.MapperProxyFactory;
+import org.apache.ibatis.binding.MapperRegistry;
+import org.apache.ibatis.io.ResolverUtil;
+import org.apache.ibatis.session.Configuration;
+import org.apache.ibatis.session.SqlSession;
+
+import com.baomidou.mybatisplus.MybatisPlusMapperBuilder;
+
+/**
+ * 继承至MapperRegistry
+ * @author Clinton Begin
+ * @author Eduardo Macarron
+ * @author Lasse Voss
+ */
+public class MybatisPulsMapperRegistry extends MapperRegistry {
+
+  private final Configuration config;
+  private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
+
+  public MybatisPulsMapperRegistry(Configuration config) {
+    super(config);
+    this.config = config;
+  }
+
+  @SuppressWarnings("unchecked")
+  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
+    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
+    if (mapperProxyFactory == null) {
+      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
+    }
+    try {
+      return mapperProxyFactory.newInstance(sqlSession);
+    } catch (Exception e) {
+      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
+    }
+  }
+
+  public <T> boolean hasMapper(Class<T> type) {
+    return knownMappers.containsKey(type);
+  }
+
+  public <T> void addMapper(Class<T> type) {
+    if (type.isInterface()) {
+      if (hasMapper(type)) {
+        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
+      }
+      boolean loadCompleted = false;
+      try {
+        knownMappers.put(type, new MapperProxyFactory<T>(type));
+        // It's important that the type is added before the parser is run
+        // otherwise the binding may automatically be attempted by the
+        // mapper parser. If the type is already known, it won't try.
+
+        MybatisPlusMapperBuilder parser = new MybatisPlusMapperBuilder(config, type);
+        parser.parse();
+        loadCompleted = true;
+      } finally {
+        if (!loadCompleted) {
+          knownMappers.remove(type);
+        }
+      }
+    }
+  }
+
+  /**
+   * @since 3.2.2
+   */
+  public Collection<Class<?>> getMappers() {
+    return Collections.unmodifiableCollection(knownMappers.keySet());
+  }
+
+  /**
+   * @since 3.2.2
+   */
+  public void addMappers(String packageName, Class<?> superType) {
+    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
+    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
+    Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
+    for (Class<?> mapperClass : mapperSet) {
+      addMapper(mapperClass);
+    }
+  }
+
+  /**
+   * @since 3.2.2
+   */
+  public void addMappers(String packageName) {
+    addMappers(packageName, Object.class);
+  }
+
+}

+ 1 - 1
mybatis-plus/src/main/java/com/baomidou/mybatisplus/MybatisXMLMapperBuilder.java

@@ -439,7 +439,7 @@ public class MybatisXMLMapperBuilder extends BaseBuilder {
 				}
 				//TODO 注入 CURD 动态 SQL
 				if (BaseMapper.class.isAssignableFrom(boundType)) {
-					MybatisConfiguration.SQL_INJECTOR.inject(configuration, builderAssistant, boundType);
+					MybatisConfiguration.SQL_INJECTOR.inspectInject(configuration, builderAssistant, boundType);
 				}
 			}
 		}

+ 18 - 1
mybatis-plus/src/main/java/com/baomidou/mybatisplus/mapper/AutoSqlInjector.java

@@ -19,6 +19,7 @@ import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.logging.Logger;
 
 import org.apache.ibatis.builder.MapperBuilderAssistant;
@@ -58,7 +59,23 @@ public class AutoSqlInjector implements ISqlInjector {
 	protected MapperBuilderAssistant builderAssistant;
 	
 	protected DBType dbType = DBType.MYSQL;
-	
+
+	/**
+	 * CRUD注入后给予标识 注入过后不再注入
+	 * 
+	 * @param configuration
+	 * @param builderAssistant
+	 * @param mapperClass
+	 */
+	public void inspectInject(Configuration configuration, MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
+		String className = mapperClass.toString();
+		Set<String> mapperRegistryCache = MybatisConfiguration.MAPPER_REGISTRY_CACHE;
+		if (!mapperRegistryCache.contains(className)) {
+			inject(configuration, builderAssistant, mapperClass);
+			mapperRegistryCache.add(className);
+		}
+	};
+
 	/**
 	 * 注入单点 crudSql
 	 */

+ 7 - 0
mybatis-plus/src/main/java/com/baomidou/mybatisplus/mapper/ISqlInjector.java

@@ -35,4 +35,11 @@ public interface ISqlInjector {
 	 */
 	void inject(Configuration configuration, MapperBuilderAssistant builderAssistant, Class<?> mapperClass);
 
+	/**
+	 * <p>
+	 * CRUD注入后给予标识 注入过后不再注入
+	 * </p>
+	 */
+	void inspectInject(Configuration configuration, MapperBuilderAssistant builderAssistant, Class<?> mapperClass);
+
 }