com.baomidou.mybatisplus.core.MybatisConfiguration Maven / Gradle / Ivy
Show all versions of mybatis-plus-core Show documentation
/*
* Copyright (c) 2011-2021, baomidou ([email protected]).
*
* 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.core;
import com.baomidou.mybatisplus.core.handlers.CompositeEnumTypeHandler;
import com.baomidou.mybatisplus.core.mapper.Mapper;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import lombok.Getter;
import lombok.Setter;
import org.apache.ibatis.binding.MapperRegistry;
import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMap;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.transaction.Transaction;
import org.apache.ibatis.type.TypeHandler;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
/**
* replace default Configuration class
* Caratacus 2016/9/25 replace mapperRegistry
*
* @author hubin
* @since 2016-01-23
*/
public class MybatisConfiguration extends Configuration {
private static final Log logger = LogFactory.getLog(MybatisConfiguration.class);
/**
* Mapper 注册
*/
protected final MybatisMapperRegistry mybatisMapperRegistry = new MybatisMapperRegistry(this);
protected final Map caches = new StrictMap<>("Caches collection");
protected final Map resultMaps = new StrictMap<>("Result Maps collection");
protected final Map parameterMaps = new StrictMap<>("Parameter Maps collection");
protected final Map keyGenerators = new StrictMap<>("Key Generators collection");
protected final Map sqlFragments = new StrictMap<>("XML fragments parsed from previous mappers");
protected final Map mappedStatements = new StrictMap("Mapped Statements collection")
.conflictMessageProducer((savedValue, targetValue) ->
". please check " + savedValue.getResource() + " and " + targetValue.getResource());
/**
* 是否生成短key缓存
*
* @since 3.4.0
*/
@Setter
@Getter
private boolean useGeneratedShortKey = true;
public MybatisConfiguration(Environment environment) {
this();
this.environment = environment;
}
/**
* 初始化调用
*/
public MybatisConfiguration() {
super();
this.mapUnderscoreToCamelCase = true;
typeHandlerRegistry.setDefaultEnumTypeHandler(CompositeEnumTypeHandler.class);
languageRegistry.setDefaultDriverClass(MybatisXMLLanguageDriver.class);
}
/**
* MybatisPlus 加载 SQL 顺序:
* 1、加载 XML中的 SQL
* 2、加载 SqlProvider 中的 SQL
* 3、XmlSql 与 SqlProvider不能包含相同的 SQL
* 调整后的 SQL优先级:XmlSql > sqlProvider > CurdSql
*/
@Override
public void addMappedStatement(MappedStatement ms) {
if (mappedStatements.containsKey(ms.getId())) {
/*
* 说明已加载了xml中的节点; 忽略mapper中的 SqlProvider 数据
*/
logger.error("mapper[" + ms.getId() + "] is ignored, because it exists, maybe from xml file");
return;
}
mappedStatements.put(ms.getId(), ms);
}
/**
* 使用自己的 MybatisMapperRegistry
*/
@Override
public MapperRegistry getMapperRegistry() {
return mybatisMapperRegistry;
}
/**
* 使用自己的 MybatisMapperRegistry
*/
@Override
public void addMapper(Class type) {
mybatisMapperRegistry.addMapper(type);
}
/**
* 新增注入新的 Mapper 信息,新增前会清理之前的缓存信息
*
* @param type Mapper Type
* @param
*/
public void addNewMapper(Class type) {
this.removeMapper(type);
this.addMapper(type);
}
/**
* 移除 Mapper 相关缓存,支持 GroovyClassLoader 动态注入 Mapper
*
* @param type Mapper Type
* @param
*/
public void removeMapper(Class type) {
// TODO
Set mapperRegistryCache = GlobalConfigUtils.getGlobalConfig(this).getMapperRegistryCache();
final String mapperType = type.toString();
if (mapperRegistryCache.contains(mapperType)) {
// 清空实体表信息映射信息
TableInfoHelper.remove(ReflectionKit.getSuperClassGenericType(type, Mapper.class, 0));
// 清空 Mapper 缓存信息
this.mybatisMapperRegistry.removeMapper(type);
this.loadedResources.remove(type.toString());
mapperRegistryCache.remove(mapperType);
// 清空 Mapper 方法 mappedStatement 缓存信息
final String typeKey = type.getName() + StringPool.DOT;
Set mapperSet = mappedStatements.keySet().stream().filter(ms -> ms.startsWith(typeKey)).collect(Collectors.toSet());
if (!mapperSet.isEmpty()) {
mapperSet.forEach(mappedStatements::remove);
}
}
}
/**
* 使用自己的 MybatisMapperRegistry
*/
@Override
public void addMappers(String packageName, Class> superType) {
mybatisMapperRegistry.addMappers(packageName, superType);
}
/**
* 使用自己的 MybatisMapperRegistry
*/
@Override
public void addMappers(String packageName) {
mybatisMapperRegistry.addMappers(packageName);
}
/**
* 使用自己的 MybatisMapperRegistry
*/
@Override
public T getMapper(Class type, SqlSession sqlSession) {
return mybatisMapperRegistry.getMapper(type, sqlSession);
}
/**
* 使用自己的 MybatisMapperRegistry
*/
@Override
public boolean hasMapper(Class> type) {
return mybatisMapperRegistry.hasMapper(type);
}
/**
* 指定动态SQL生成的默认语言
*
* @param driver LanguageDriver
*/
@Override
public void setDefaultScriptingLanguage(Class extends LanguageDriver> driver) {
if (driver == null) {
//todo 替换动态SQL生成的默认语言为自己的。
driver = MybatisXMLLanguageDriver.class;
}
getLanguageRegistry().setDefaultDriverClass(driver);
}
@Override
public void setDefaultEnumTypeHandler(Class extends TypeHandler> typeHandler) {
if (typeHandler != null) {
CompositeEnumTypeHandler.setDefaultEnumTypeHandler(typeHandler);
}
}
@Override
public void addKeyGenerator(String id, KeyGenerator keyGenerator) {
keyGenerators.put(id, keyGenerator);
}
@Override
public Collection getKeyGeneratorNames() {
return keyGenerators.keySet();
}
@Override
public Collection getKeyGenerators() {
return keyGenerators.values();
}
@Override
public KeyGenerator getKeyGenerator(String id) {
return keyGenerators.get(id);
}
@Override
public boolean hasKeyGenerator(String id) {
return keyGenerators.containsKey(id);
}
@Override
public void addCache(Cache cache) {
caches.put(cache.getId(), cache);
}
@Override
public Collection getCacheNames() {
return caches.keySet();
}
@Override
public Collection getCaches() {
return caches.values();
}
@Override
public Cache getCache(String id) {
return caches.get(id);
}
@Override
public boolean hasCache(String id) {
return caches.containsKey(id);
}
@Override
public void addResultMap(ResultMap rm) {
resultMaps.put(rm.getId(), rm);
checkLocallyForDiscriminatedNestedResultMaps(rm);
checkGloballyForDiscriminatedNestedResultMaps(rm);
}
@Override
public Collection getResultMapNames() {
return resultMaps.keySet();
}
@Override
public Collection getResultMaps() {
return resultMaps.values();
}
@Override
public ResultMap getResultMap(String id) {
return resultMaps.get(id);
}
@Override
public boolean hasResultMap(String id) {
return resultMaps.containsKey(id);
}
@Override
public void addParameterMap(ParameterMap pm) {
parameterMaps.put(pm.getId(), pm);
}
@Override
public Collection getParameterMapNames() {
return parameterMaps.keySet();
}
@Override
public Collection getParameterMaps() {
return parameterMaps.values();
}
@Override
public ParameterMap getParameterMap(String id) {
return parameterMaps.get(id);
}
@Override
public boolean hasParameterMap(String id) {
return parameterMaps.containsKey(id);
}
@Override
public Map getSqlFragments() {
return sqlFragments;
}
@Override
public Collection getMappedStatementNames() {
buildAllStatements();
return mappedStatements.keySet();
}
@Override
public Collection getMappedStatements() {
buildAllStatements();
return mappedStatements.values();
}
@Override
public MappedStatement getMappedStatement(String id) {
return this.getMappedStatement(id, true);
}
@Override
public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
if (validateIncompleteStatements) {
buildAllStatements();
}
return mappedStatements.get(id);
}
@Override
public boolean hasStatement(String statementName, boolean validateIncompleteStatements) {
if (validateIncompleteStatements) {
buildAllStatements();
}
return mappedStatements.containsKey(statementName);
}
@Override
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
return super.newExecutor(transaction, executorType);
}
// Slow but a one time cost. A better solution is welcome.
@Override
protected void checkGloballyForDiscriminatedNestedResultMaps(ResultMap rm) {
if (rm.hasNestedResultMaps()) {
for (Map.Entry entry : resultMaps.entrySet()) {
Object value = entry.getValue();
if (value instanceof ResultMap) {
ResultMap entryResultMap = (ResultMap) value;
if (!entryResultMap.hasNestedResultMaps() && entryResultMap.getDiscriminator() != null) {
Collection discriminatedResultMapNames = entryResultMap.getDiscriminator().getDiscriminatorMap().values();
if (discriminatedResultMapNames.contains(rm.getId())) {
entryResultMap.forceNestedResultMaps();
}
}
}
}
}
}
// Slow but a one time cost. A better solution is welcome.
@Override
protected void checkLocallyForDiscriminatedNestedResultMaps(ResultMap rm) {
if (!rm.hasNestedResultMaps() && rm.getDiscriminator() != null) {
for (Map.Entry entry : rm.getDiscriminator().getDiscriminatorMap().entrySet()) {
String discriminatedResultMapName = entry.getValue();
if (hasResultMap(discriminatedResultMapName)) {
ResultMap discriminatedResultMap = resultMaps.get(discriminatedResultMapName);
if (discriminatedResultMap.hasNestedResultMaps()) {
rm.forceNestedResultMaps();
break;
}
}
}
}
}
protected class StrictMap extends HashMap {
private static final long serialVersionUID = -4950446264854982944L;
private final String name;
private BiFunction conflictMessageProducer;
public StrictMap(String name) {
super();
this.name = name;
}
/**
* Assign a function for producing a conflict error message when contains value with the same key.
*
* function arguments are 1st is saved value and 2nd is target value.
*
* @param conflictMessageProducer A function for producing a conflict error message
* @return a conflict error message
* @since 3.5.0
*/
public StrictMap conflictMessageProducer(BiFunction conflictMessageProducer) {
this.conflictMessageProducer = conflictMessageProducer;
return this;
}
@Override
@SuppressWarnings("unchecked")
public V put(String key, V value) {
if (containsKey(key)) {
throw new IllegalArgumentException(name + " already contains value for " + key
+ (conflictMessageProducer == null ? StringPool.EMPTY : conflictMessageProducer.apply(super.get(key), value)));
}
if (useGeneratedShortKey) {
if (key.contains(StringPool.DOT)) {
final String shortKey = getShortName(key);
if (super.get(shortKey) == null) {
super.put(shortKey, value);
} else {
super.put(shortKey, (V) new StrictMap.Ambiguity(shortKey));
}
}
}
return super.put(key, value);
}
@Override
public V get(Object key) {
V value = super.get(key);
if (value == null) {
throw new IllegalArgumentException(name + " does not contain value for " + key);
}
if (useGeneratedShortKey && value instanceof StrictMap.Ambiguity) {
throw new IllegalArgumentException(((StrictMap.Ambiguity) value).getSubject() + " is ambiguous in " + name
+ " (try using the full name including the namespace, or rename one of the entries)");
}
return value;
}
protected class Ambiguity {
private final String subject;
public Ambiguity(String subject) {
this.subject = subject;
}
public String getSubject() {
return subject;
}
}
private String getShortName(String key) {
final String[] keyParts = key.split("\\.");
return keyParts[keyParts.length - 1];
}
}
}