فهرست منبع

一波依赖升级,支持 mybatis.scripting 下3个language的自动注入

miemie 5 سال پیش
والد
کامیت
0b4f456d98

+ 4 - 1
build.gradle

@@ -33,8 +33,11 @@ ext {
         "kotlin-reflect"             : "org.jetbrains.kotlin:kotlin-reflect:1.3.50",
         "kotlin-stdlib-jdk8"         : "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.50",
         "jsqlparser"                 : "com.github.jsqlparser:jsqlparser:${jsqlparserVersion}",
-        "mybatis-spring"             : "org.mybatis:mybatis-spring:${mybatisSpringVersion}",
         "mybatis"                    : "org.mybatis:mybatis:${mybatisVersion}",
+        "mybatis-spring"             : "org.mybatis:mybatis-spring:${mybatisSpringVersion}",
+        "mybatis-thymeleaf"          : "org.mybatis.scripting:mybatis-thymeleaf:1.0.1",
+        "mybatis-freemarker"         : "org.mybatis.scripting:mybatis-freemarker:1.2.0",
+        "mybatis-velocity"           : "org.mybatis.scripting:mybatis-velocity:2.1.0",
         "spring-context-support"     : "org.springframework:spring-context-support:${springVersion}",
         "spring-jdbc"                : "org.springframework:spring-jdbc:${springVersion}",
         "spring-tx"                  : "org.springframework:spring-tx:${springVersion}",

+ 3 - 0
mybatis-plus-boot-starter/build.gradle

@@ -6,6 +6,9 @@ dependencies {
     api 'org.springframework.boot:spring-boot-autoconfigure'
     api 'org.springframework.boot:spring-boot-starter-jdbc'
     implementation 'org.springframework.boot:spring-boot-configuration-processor'
+    implementation "${lib.'mybatis-thymeleaf'}"
+    implementation "${lib.'mybatis-velocity'}"
+    implementation "${lib.'mybatis-freemarker'}"
     implementation 'org.springframework.boot:spring-boot-autoconfigure-processor'
     testImplementation 'org.springframework.boot:spring-boot-starter-test'
     testImplementation "${lib.'mybatis-spring-boot-starter'}"

+ 1 - 1
mybatis-plus-boot-starter/src/main/java/com/baomidou/mybatisplus/autoconfigure/MybatisPlusAutoConfiguration.java

@@ -90,7 +90,7 @@ import java.util.stream.Stream;
 @ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
 @ConditionalOnSingleCandidate(DataSource.class)
 @EnableConfigurationProperties(MybatisPlusProperties.class)
-@AutoConfigureAfter(DataSourceAutoConfiguration.class)
+@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisPlusLanguageDriverAutoConfiguration.class})
 public class MybatisPlusAutoConfiguration implements InitializingBean {
 
     private static final Logger logger = LoggerFactory.getLogger(MybatisPlusAutoConfiguration.class);

+ 118 - 0
mybatis-plus-boot-starter/src/main/java/com/baomidou/mybatisplus/autoconfigure/MybatisPlusLanguageDriverAutoConfiguration.java

@@ -0,0 +1,118 @@
+package com.baomidou.mybatisplus.autoconfigure;
+
+import com.baomidou.mybatisplus.core.toolkit.Constants;
+import org.apache.ibatis.scripting.LanguageDriver;
+import org.mybatis.scripting.freemarker.FreeMarkerLanguageDriver;
+import org.mybatis.scripting.freemarker.FreeMarkerLanguageDriverConfig;
+import org.mybatis.scripting.thymeleaf.ThymeleafLanguageDriver;
+import org.mybatis.scripting.thymeleaf.ThymeleafLanguageDriverConfig;
+import org.mybatis.scripting.velocity.VelocityLanguageDriver;
+import org.mybatis.scripting.velocity.VelocityLanguageDriverConfig;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author miemie
+ * @since 2019-10-22
+ */
+@Configuration
+@ConditionalOnClass(LanguageDriver.class)
+public class MybatisPlusLanguageDriverAutoConfiguration {
+
+    private static final String CONFIGURATION_PROPERTY_PREFIX = Constants.MYBATIS_PLUS + ".scripting-language-driver";
+
+    /**
+     * Configuration class for mybatis-freemarker 1.1.x or under.
+     */
+    @Configuration
+    @ConditionalOnClass(FreeMarkerLanguageDriver.class)
+    @ConditionalOnMissingClass("org.mybatis.scripting.freemarker.FreeMarkerLanguageDriverConfig")
+    public static class LegacyFreeMarkerConfiguration {
+
+        @Bean
+        @ConditionalOnMissingBean
+        FreeMarkerLanguageDriver freeMarkerLanguageDriver() {
+            return new FreeMarkerLanguageDriver();
+        }
+    }
+
+    /**
+     * Configuration class for mybatis-freemarker 1.2.x or above.
+     */
+    @Configuration
+    @ConditionalOnClass({FreeMarkerLanguageDriver.class, FreeMarkerLanguageDriverConfig.class})
+    public static class FreeMarkerConfiguration {
+
+        @Bean
+        @ConditionalOnMissingBean
+        FreeMarkerLanguageDriver freeMarkerLanguageDriver(FreeMarkerLanguageDriverConfig config) {
+            return new FreeMarkerLanguageDriver(config);
+        }
+
+        @Bean
+        @ConditionalOnMissingBean
+        @ConfigurationProperties(CONFIGURATION_PROPERTY_PREFIX + ".freemarker")
+        public FreeMarkerLanguageDriverConfig freeMarkerLanguageDriverConfig() {
+            return FreeMarkerLanguageDriverConfig.newInstance();
+        }
+    }
+
+    /**
+     * Configuration class for mybatis-velocity 2.0 or under.
+     */
+    @Configuration
+    @ConditionalOnClass(org.mybatis.scripting.velocity.Driver.class)
+    @ConditionalOnMissingClass("org.mybatis.scripting.velocity.VelocityLanguageDriverConfig")
+    @SuppressWarnings("deprecation")
+    public static class LegacyVelocityConfiguration {
+
+        @Bean
+        @ConditionalOnMissingBean
+        org.mybatis.scripting.velocity.Driver velocityLanguageDriver() {
+            return new org.mybatis.scripting.velocity.Driver();
+        }
+    }
+
+    /**
+     * Configuration class for mybatis-velocity 2.1.x or above.
+     */
+    @Configuration
+    @ConditionalOnClass({VelocityLanguageDriver.class, VelocityLanguageDriverConfig.class})
+    public static class VelocityConfiguration {
+
+        @Bean
+        @ConditionalOnMissingBean
+        VelocityLanguageDriver velocityLanguageDriver(VelocityLanguageDriverConfig config) {
+            return new VelocityLanguageDriver(config);
+        }
+
+        @Bean
+        @ConditionalOnMissingBean
+        @ConfigurationProperties(CONFIGURATION_PROPERTY_PREFIX + ".velocity")
+        public VelocityLanguageDriverConfig velocityLanguageDriverConfig() {
+            return VelocityLanguageDriverConfig.newInstance();
+        }
+    }
+
+    @Configuration
+    @ConditionalOnClass(ThymeleafLanguageDriver.class)
+    public static class ThymeleafConfiguration {
+
+        @Bean
+        @ConditionalOnMissingBean
+        ThymeleafLanguageDriver thymeleafLanguageDriver(ThymeleafLanguageDriverConfig config) {
+            return new ThymeleafLanguageDriver(config);
+        }
+
+        @Bean
+        @ConditionalOnMissingBean
+        @ConfigurationProperties(CONFIGURATION_PROPERTY_PREFIX + ".thymeleaf")
+        public ThymeleafLanguageDriverConfig thymeleafLanguageDriverConfig() {
+            return ThymeleafLanguageDriverConfig.newInstance();
+        }
+    }
+}

+ 1 - 1
mybatis-plus-boot-starter/src/main/java/com/baomidou/mybatisplus/autoconfigure/MybatisPlusProperties.java

@@ -88,7 +88,7 @@ public class MybatisPlusProperties {
     /**
      * The default scripting language driver class. (Available when use together with mybatis-spring 2.0.2+)
      * <p>
-     * 如果设置了这个,你会至少失去 mp 提供的有关填充到 entity 的 property 相关的功能
+     * 如果设置了这个,你会至少失去几乎所有 mp 提供的功能
      */
     private Class<? extends LanguageDriver> defaultScriptingLanguageDriver;
 

+ 17 - 4
mybatis-plus-boot-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json

@@ -50,16 +50,29 @@
             }
         },
         {
-            "sourceType": "com.baomidou.mybatisplus.core.MybatisConfiguration",
+            "sourceType": "com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties",
+            "name": "mybatis-plus.default-scripting-language-driver",
             "defaultValue": "com.baomidou.mybatisplus.core.MybatisXMLLanguageDriver",
+            "type": "java.lang.Class<? extends org.apache.ibatis.scripting.LanguageDriver>",
+            "deprecation": {
+                "level": "error",
+                "reason": "如果修改了该值,你会至少失去几乎所有 mp 提供的功能."
+            }
+        },
+        {
+            "sourceType": "com.baomidou.mybatisplus.core.MybatisConfiguration",
             "name": "mybatis-plus.configuration.default-scripting-language",
-            "description": "A default LanguageDriver class.",
-            "type": "java.lang.Class<? extends org.apache.ibatis.scripting.LanguageDriver>"
+            "defaultValue": "com.baomidou.mybatisplus.core.MybatisXMLLanguageDriver",
+            "type": "java.lang.Class<? extends org.apache.ibatis.scripting.LanguageDriver>",
+            "deprecation": {
+                "level": "error",
+                "reason": "设置无效."
+            }
         },
         {
             "sourceType": "com.baomidou.mybatisplus.core.MybatisConfiguration",
-            "defaultValue": "org.apache.ibatis.type.EnumTypeHandler",
             "name": "mybatis-plus.configuration.default-enum-type-handler",
+            "defaultValue": "org.apache.ibatis.type.EnumTypeHandler",
             "description": "A default TypeHandler class for Enum.",
             "type": "java.lang.Class<? extends org.apache.ibatis.type.TypeHandler>"
         }

+ 1 - 0
mybatis-plus-boot-starter/src/main/resources/META-INF/spring.factories

@@ -1,3 +1,4 @@
 # Auto Configure
 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+  com.baomidou.mybatisplus.autoconfigure.MybatisPlusLanguageDriverAutoConfiguration,\
   com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration

+ 50 - 13
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/override/MybatisMapperProxy.java

@@ -21,6 +21,7 @@ import org.apache.ibatis.session.SqlSession;
 
 import java.io.Serializable;
 import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
@@ -36,6 +37,10 @@ import java.util.Map;
 public class MybatisMapperProxy<T> implements InvocationHandler, Serializable {
 
     private static final long serialVersionUID = -6424540398559729838L;
+    private static final int ALLOWED_MODES = MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
+        | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC;
+    private static final Constructor<MethodHandles.Lookup> lookupConstructor;
+    private static final Method privateLookupInMethod;
     private final SqlSession sqlSession;
     private final Class<T> mapperInterface;
     private final Map<Method, MybatisMapperMethod> methodCache;
@@ -46,13 +51,43 @@ public class MybatisMapperProxy<T> implements InvocationHandler, Serializable {
         this.methodCache = methodCache;
     }
 
+    static {
+        Method privateLookupIn;
+        try {
+            privateLookupIn = MethodHandles.class.getMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class);
+        } catch (NoSuchMethodException e) {
+            privateLookupIn = null;
+        }
+        privateLookupInMethod = privateLookupIn;
+
+        Constructor<MethodHandles.Lookup> lookup = null;
+        if (privateLookupInMethod == null) {
+            // JDK 1.8
+            try {
+                lookup = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
+                lookup.setAccessible(true);
+            } catch (NoSuchMethodException e) {
+                throw new IllegalStateException(
+                    "There is neither 'privateLookupIn(Class, Lookup)' nor 'Lookup(Class, int)' method in java.lang.invoke.MethodHandles.",
+                    e);
+            } catch (Throwable t) {
+                lookup = null;
+            }
+        }
+        lookupConstructor = lookup;
+    }
+
     @Override
     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
         try {
             if (Object.class.equals(method.getDeclaringClass())) {
                 return method.invoke(this, args);
             } else if (method.isDefault()) {
-                return invokeDefaultMethod(proxy, method, args);
+                if (privateLookupInMethod == null) {
+                    return invokeDefaultMethodJava8(proxy, method, args);
+                } else {
+                    return invokeDefaultMethodJava9(proxy, method, args);
+                }
             }
         } catch (Throwable t) {
             throw ExceptionUtil.unwrapThrowable(t);
@@ -62,21 +97,23 @@ public class MybatisMapperProxy<T> implements InvocationHandler, Serializable {
     }
 
     private MybatisMapperMethod cachedMapperMethod(Method method) {
-        return methodCache.computeIfAbsent(method, k -> new MybatisMapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
+        return methodCache.computeIfAbsent(method,
+            k -> new MybatisMapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
     }
 
-    private Object invokeDefaultMethod(Object proxy, Method method, Object[] args)
+    private Object invokeDefaultMethodJava9(Object proxy, Method method, Object[] args)
+        throws Throwable {
+        final Class<?> declaringClass = method.getDeclaringClass();
+        return ((MethodHandles.Lookup) privateLookupInMethod.invoke(null, declaringClass, MethodHandles.lookup()))
+            .findSpecial(declaringClass, method.getName(),
+                MethodType.methodType(method.getReturnType(), method.getParameterTypes()), declaringClass)
+            .bindTo(proxy).invokeWithArguments(args);
+    }
+
+    private Object invokeDefaultMethodJava8(Object proxy, Method method, Object[] args)
         throws Throwable {
-        final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
-            .getDeclaredConstructor(Class.class, int.class);
-        if (!constructor.isAccessible()) {
-            constructor.setAccessible(true);
-        }
         final Class<?> declaringClass = method.getDeclaringClass();
-        return constructor
-            .newInstance(declaringClass,
-                MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
-                    | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC)
-            .unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
+        return lookupConstructor.newInstance(declaringClass, ALLOWED_MODES).unreflectSpecial(method, declaringClass)
+            .bindTo(proxy).invokeWithArguments(args);
     }
 }

+ 1 - 1
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/IllegalSQLInterceptor.java

@@ -319,7 +319,7 @@ public class IllegalSQLInterceptor implements Interceptor {
         } else if (statement instanceof Update) {
             Update update = (Update) statement;
             where = update.getWhere();
-            table = update.getTables().get(0);
+            table = update.getTable();
             joins = update.getJoins();
         } else if (statement instanceof Delete) {
             Delete delete = (Delete) statement;

+ 1 - 6
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/tenant/TenantSqlParser.java

@@ -16,7 +16,6 @@
 package com.baomidou.mybatisplus.extension.plugins.tenant;
 
 import com.baomidou.mybatisplus.core.parser.AbstractJsqlParser;
-import com.baomidou.mybatisplus.core.toolkit.Assert;
 import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
 import com.baomidou.mybatisplus.core.toolkit.StringPool;
 import lombok.Data;
@@ -30,7 +29,6 @@ import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
 import net.sf.jsqlparser.expression.operators.relational.*;
 import net.sf.jsqlparser.schema.Column;
 import net.sf.jsqlparser.schema.Table;
-import net.sf.jsqlparser.statement.Statement;
 import net.sf.jsqlparser.statement.delete.Delete;
 import net.sf.jsqlparser.statement.insert.Insert;
 import net.sf.jsqlparser.statement.select.*;
@@ -101,10 +99,7 @@ public class TenantSqlParser extends AbstractJsqlParser {
      */
     @Override
     public void processUpdate(Update update) {
-        List<Table> tableList = update.getTables();
-        Assert.isTrue(null != tableList && tableList.size() < 2,
-            "Failed to process multiple-table update, please exclude the statementId");
-        Table table = tableList.get(0);
+        final Table table = update.getTable();
         if (tenantHandler.doTableFilter(table.getName())) {
             // 过滤退出执行
             return;