소스 검색

增加useGeneratedShortKey属性控制mybatis原生shortKey缓存生成.

https://github.com/baomidou/mybatis-plus/issues/2665
nieqiuqiu 5 년 전
부모
커밋
fe8453b5b5

+ 256 - 1
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisConfiguration.java

@@ -18,14 +18,24 @@ package com.baomidou.mybatisplus.core;
 import com.baomidou.mybatisplus.core.config.GlobalConfig;
 import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
 import org.apache.ibatis.binding.MapperRegistry;
+import org.apache.ibatis.cache.Cache;
+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.SqlSession;
 
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.BiFunction;
+
 /**
  * replace default Configuration class
  * <p>Caratacus 2016/9/25 replace mapperRegistry</p>
@@ -40,6 +50,21 @@ public class MybatisConfiguration extends Configuration {
      */
     protected final MybatisMapperRegistry mybatisMapperRegistry = new MybatisMapperRegistry(this);
 
+    protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");
+    protected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection");
+    protected final Map<String, ParameterMap> parameterMaps = new StrictMap<>("Parameter Maps collection");
+    protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<>("Key Generators collection");
+    protected final Map<String, XNode> sqlFragments = new StrictMap<>("XML fragments parsed from previous mappers");
+    protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection")
+        .conflictMessageProducer((savedValue, targetValue) ->
+            ". please check " + savedValue.getResource() + " and " + targetValue.getResource());
+    /**
+     * 是否生成短key缓存
+     *
+     * @since 3.3.3
+     */
+    private boolean useGeneratedShortKey = true;
+
     public MybatisConfiguration(Environment environment) {
         this();
         this.environment = environment;
@@ -89,7 +114,7 @@ public class MybatisConfiguration extends Configuration {
             logger.error("mapper[" + ms.getId() + "] is ignored, because it exists, maybe from xml file");
             return;
         }
-        super.addMappedStatement(ms);
+        mappedStatements.put(ms.getId(), ms);
     }
 
     /**
@@ -154,4 +179,234 @@ public class MybatisConfiguration extends Configuration {
         getLanguageRegistry().setDefaultDriverClass(driver);
     }
 
+    public void addKeyGenerator(String id, KeyGenerator keyGenerator) {
+        keyGenerators.put(id, keyGenerator);
+    }
+
+    public Collection<String> getKeyGeneratorNames() {
+        return keyGenerators.keySet();
+    }
+
+    public Collection<KeyGenerator> getKeyGenerators() {
+        return keyGenerators.values();
+    }
+
+    public KeyGenerator getKeyGenerator(String id) {
+        return keyGenerators.get(id);
+    }
+
+    public boolean hasKeyGenerator(String id) {
+        return keyGenerators.containsKey(id);
+    }
+
+    public void addCache(Cache cache) {
+        caches.put(cache.getId(), cache);
+    }
+
+    public Collection<String> getCacheNames() {
+        return caches.keySet();
+    }
+
+    public Collection<Cache> getCaches() {
+        return caches.values();
+    }
+
+    public Cache getCache(String id) {
+        return caches.get(id);
+    }
+
+    public boolean hasCache(String id) {
+        return caches.containsKey(id);
+    }
+
+    public void addResultMap(ResultMap rm) {
+        resultMaps.put(rm.getId(), rm);
+        checkLocallyForDiscriminatedNestedResultMaps(rm);
+        checkGloballyForDiscriminatedNestedResultMaps(rm);
+    }
+
+    public Collection<String> getResultMapNames() {
+        return resultMaps.keySet();
+    }
+
+    public Collection<ResultMap> getResultMaps() {
+        return resultMaps.values();
+    }
+
+    public ResultMap getResultMap(String id) {
+        return resultMaps.get(id);
+    }
+
+    public boolean hasResultMap(String id) {
+        return resultMaps.containsKey(id);
+    }
+
+    public void addParameterMap(ParameterMap pm) {
+        parameterMaps.put(pm.getId(), pm);
+    }
+
+    public Collection<String> getParameterMapNames() {
+        return parameterMaps.keySet();
+    }
+
+    public Collection<ParameterMap> getParameterMaps() {
+        return parameterMaps.values();
+    }
+
+    public ParameterMap getParameterMap(String id) {
+        return parameterMaps.get(id);
+    }
+
+    public boolean hasParameterMap(String id) {
+        return parameterMaps.containsKey(id);
+    }
+
+    public Map<String, XNode> getSqlFragments() {
+        return sqlFragments;
+    }
+
+    public Collection<String> getMappedStatementNames() {
+        buildAllStatements();
+        return mappedStatements.keySet();
+    }
+
+    public Collection<MappedStatement> getMappedStatements() {
+        buildAllStatements();
+        return mappedStatements.values();
+    }
+
+    public MappedStatement getMappedStatement(String id) {
+        return this.getMappedStatement(id, true);
+    }
+
+    public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
+        if (validateIncompleteStatements) {
+            buildAllStatements();
+        }
+        return mappedStatements.get(id);
+    }
+
+    public boolean hasStatement(String statementName, boolean validateIncompleteStatements) {
+        if (validateIncompleteStatements) {
+            buildAllStatements();
+        }
+        return mappedStatements.containsKey(statementName);
+    }
+
+
+    // Slow but a one time cost. A better solution is welcome.
+    protected void checkGloballyForDiscriminatedNestedResultMaps(ResultMap rm) {
+        if (rm.hasNestedResultMaps()) {
+            for (Map.Entry<String, ResultMap> entry : resultMaps.entrySet()) {
+                Object value = entry.getValue();
+                if (value instanceof ResultMap) {
+                    ResultMap entryResultMap = (ResultMap) value;
+                    if (!entryResultMap.hasNestedResultMaps() && entryResultMap.getDiscriminator() != null) {
+                        Collection<String> discriminatedResultMapNames = entryResultMap.getDiscriminator().getDiscriminatorMap().values();
+                        if (discriminatedResultMapNames.contains(rm.getId())) {
+                            entryResultMap.forceNestedResultMaps();
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    // Slow but a one time cost. A better solution is welcome.
+    protected void checkLocallyForDiscriminatedNestedResultMaps(ResultMap rm) {
+        if (!rm.hasNestedResultMaps() && rm.getDiscriminator() != null) {
+            for (Map.Entry<String, String> entry : rm.getDiscriminator().getDiscriminatorMap().entrySet()) {
+                String discriminatedResultMapName = entry.getValue();
+                if (hasResultMap(discriminatedResultMapName)) {
+                    ResultMap discriminatedResultMap = resultMaps.get(discriminatedResultMapName);
+                    if (discriminatedResultMap.hasNestedResultMaps()) {
+                        rm.forceNestedResultMaps();
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
+    public boolean isUseGeneratedShortKey() {
+        return useGeneratedShortKey;
+    }
+
+    public void setUseGeneratedShortKey(boolean useGeneratedShortKey) {
+        this.useGeneratedShortKey = useGeneratedShortKey;
+    }
+
+    protected class StrictMap<V> extends HashMap<String, V> {
+
+        private static final long serialVersionUID = -4950446264854982944L;
+        private final String name;
+        private BiFunction<V, V, String> 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.
+         * <p>
+         * 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<V> conflictMessageProducer(BiFunction<V, V, String> 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 ? "" : conflictMessageProducer.apply(super.get(key), value)));
+            }
+            if (useGeneratedShortKey) {
+                if (key.contains(".")) {
+                    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];
+        }
+    }
 }

+ 29 - 0
mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/MybatisConfigurationTest.java

@@ -18,8 +18,13 @@ package com.baomidou.mybatisplus.test;
 import com.baomidou.mybatisplus.core.MybatisConfiguration;
 import com.baomidou.mybatisplus.core.MybatisSqlSessionFactoryBuilder;
 import com.baomidou.mybatisplus.core.MybatisXMLLanguageDriver;
+import org.apache.ibatis.builder.StaticSqlSource;
+import org.apache.ibatis.executor.keygen.NoKeyGenerator;
 import org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory;
 import org.apache.ibatis.io.Resources;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.mapping.SqlCommandType;
+import org.apache.ibatis.mapping.StatementType;
 import org.apache.ibatis.session.AutoMappingBehavior;
 import org.apache.ibatis.session.AutoMappingUnknownColumnBehavior;
 import org.apache.ibatis.session.Configuration;
@@ -106,4 +111,28 @@ class MybatisConfigurationTest {
         Assertions.assertTrue(configuration.isUseActualParamName());
         Assertions.assertNull(configuration.getConfigurationFactory());
     }
+
+    @Test
+    void testUseGeneratedShortKey(){
+        MybatisConfiguration configuration = new MybatisConfiguration();
+        configuration.setUseGeneratedShortKey(true);
+        addMappedStatement(configuration, 200);
+        Assertions.assertEquals(400, configuration.getMappedStatements().size());
+
+        configuration = new MybatisConfiguration();
+        configuration.setUseGeneratedShortKey(false);
+        addMappedStatement(configuration, 200);
+        Assertions.assertEquals(200, configuration.getMappedStatements().size());
+    }
+
+    private void addMappedStatement(Configuration configuration, int size) {
+        for (int i = 0; i < size; i++) {
+            configuration.addMappedStatement(
+                new MappedStatement.Builder(configuration, "com.baomidou.test.method" + i,
+                    new StaticSqlSource(configuration, "select * from test"), SqlCommandType.SELECT)
+                    .statementType(StatementType.STATEMENT).resource("xxxxxx").useCache(true).keyGenerator(NoKeyGenerator.INSTANCE)
+                    .build()
+            );
+        }
+    }
 }