Browse Source

Merge remote-tracking branch 'origin/3.0' into 3.0

HouKunLin 2 years ago
parent
commit
7ad81ad5dc
28 changed files with 923 additions and 98 deletions
  1. 54 0
      mybatis-plus-boot-starter/src/main/java/com/baomidou/mybatisplus/autoconfigure/DdlApplicationRunner.java
  2. 13 0
      mybatis-plus-boot-starter/src/main/java/com/baomidou/mybatisplus/autoconfigure/MybatisPlusAutoConfiguration.java
  3. 3 2
      mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisMapperAnnotationBuilder.java
  4. 3 1
      mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisParameterHandler.java
  5. 10 0
      mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/AbstractWrapper.java
  6. 34 0
      mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/interfaces/Compare.java
  7. 20 0
      mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/plugins/IgnoreStrategy.java
  8. 58 40
      mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/plugins/InterceptorIgnoreHelper.java
  9. 2 0
      mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/StringPool.java
  10. 2 2
      mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/core/conditions/QueryWrapperTest.java
  11. 2 2
      mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/core/plugins/InterceptorIgnoreHelperTest.java
  12. 0 1
      mybatis-plus-extension/build.gradle
  13. 16 4
      mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/conditions/AbstractChainWrapper.java
  14. 143 0
      mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/ddl/DdlHelper.java
  15. 87 0
      mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/ddl/DdlScript.java
  16. 53 0
      mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/ddl/IDdl.java
  17. 42 0
      mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/ddl/SimpleDdl.java
  18. 82 0
      mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/ddl/history/IDdlGenerator.java
  19. 56 0
      mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/ddl/history/MysqlDdlGenerator.java
  20. 53 0
      mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/ddl/history/OracleDdlGenerator.java
  21. 67 0
      mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/ddl/history/PostgreDdlGenerator.java
  22. 2 2
      mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/BaseMultiTableInnerInterceptor.java
  23. 1 1
      mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/pagination/dialects/SQLServer2005Dialect.java
  24. 62 2
      mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/pagination/dialects/SybaseDialect.java
  25. 3 3
      mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/toolkit/Db.java
  26. 27 29
      mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/toolkit/SimpleQuery.java
  27. 21 9
      mybatis-plus-extension/src/test/java/com/baomidou/mybatisplus/extension/plugins/inner/TenantLineInnerInterceptorTest.java
  28. 7 0
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/H2UserTest.java

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

@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2011-2022, baomidou (jobob@qq.com).
+ *
+ * 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.autoconfigure;
+
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
+import com.baomidou.mybatisplus.extension.ddl.DdlHelper;
+import com.baomidou.mybatisplus.extension.ddl.IDdl;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+
+import java.util.List;
+
+/**
+ * DDL 启动应用后执行
+ *
+ * @author hubin
+ * @since 2021-06-22
+ */
+@Slf4j
+public class DdlApplicationRunner implements ApplicationRunner {
+    private List<IDdl> ddlList;
+
+    public DdlApplicationRunner(List<IDdl> ddlList) {
+        this.ddlList = ddlList;
+    }
+
+    @Override
+    public void run(ApplicationArguments args) throws Exception {
+        log.debug("  ...  DDL start create  ...  ");
+        if (ObjectUtils.isNotEmpty(ddlList)) {
+            /**
+             * 执行 SQL 脚本
+             * <p>多数据源情况可按需初始化</p>
+             */
+            ddlList.forEach(ddl -> ddl.runScript(dataSource -> DdlHelper.runScript(ddl.getDdlGenerator(),
+                    dataSource, ddl.getSqlFiles(), true)));
+        }
+        log.debug("  ...  DDL end create  ...  ");
+    }
+}

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

@@ -23,6 +23,7 @@ import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
 import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;
 import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
 import com.baomidou.mybatisplus.core.injector.ISqlInjector;
+import com.baomidou.mybatisplus.extension.ddl.IDdl;
 import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.mapping.DatabaseIdProvider;
@@ -41,6 +42,7 @@ import org.slf4j.LoggerFactory;
 import org.springframework.beans.BeanWrapper;
 import org.springframework.beans.BeanWrapperImpl;
 import org.springframework.beans.factory.*;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.beans.factory.support.BeanDefinitionBuilder;
 import org.springframework.beans.factory.support.BeanDefinitionRegistry;
@@ -58,6 +60,7 @@ import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
 import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
+import org.springframework.core.annotation.Order;
 import org.springframework.core.env.Environment;
 import org.springframework.core.io.Resource;
 import org.springframework.core.io.ResourceLoader;
@@ -379,4 +382,14 @@ public class MybatisPlusAutoConfiguration implements InitializingBean {
                 "Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer.");
         }
     }
+
+    @Order
+    @Bean
+    @ConditionalOnMissingBean
+    public DdlApplicationRunner ddlApplicationRunner(@Autowired(required = false) List<IDdl> ddlList) {
+        if (ObjectUtils.isEmpty(ddlList)) {
+            return null;
+        }
+        return new DdlApplicationRunner(ddlList);
+    }
 }

+ 3 - 2
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisMapperAnnotationBuilder.java

@@ -16,6 +16,7 @@
 package com.baomidou.mybatisplus.core;
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy;
 import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
 import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
 import com.baomidou.mybatisplus.core.toolkit.StringPool;
@@ -97,7 +98,7 @@ public class MybatisMapperAnnotationBuilder extends MapperAnnotationBuilder {
             assistant.setCurrentNamespace(mapperName);
             parseCache();
             parseCacheRef();
-            InterceptorIgnoreHelper.InterceptorIgnoreCache cache = InterceptorIgnoreHelper.initSqlParserInfoCache(type);
+            IgnoreStrategy ignoreStrategy = InterceptorIgnoreHelper.initSqlParserInfoCache(type);
             for (Method method : type.getMethods()) {
                 if (!canHaveStatement(method)) {
                     continue;
@@ -108,7 +109,7 @@ public class MybatisMapperAnnotationBuilder extends MapperAnnotationBuilder {
                 }
                 try {
                     // TODO 加入 注解过滤缓存
-                    InterceptorIgnoreHelper.initSqlParserInfoCache(cache, mapperName, method);
+                    InterceptorIgnoreHelper.initSqlParserInfoCache(ignoreStrategy, mapperName, method);
                     parseStatement(method);
                 } catch (IncompleteElementException e) {
                     // TODO 使用 MybatisMethodResolver 而不是 MethodResolver

+ 3 - 1
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisParameterHandler.java

@@ -146,8 +146,10 @@ public class MybatisParameterHandler implements ParameterHandler {
                         } else {
                             throw new MybatisPlusException("Key type '" + keyType + "' not supported");
                         }
-                    } else {
+                    } else if (String.class.isAssignableFrom(keyType)) {
                         metaObject.setValue(keyProperty, identifierGenerator.nextId(entity).toString());
+                    } else {
+                        metaObject.setValue(keyProperty, identifierGenerator.nextId(entity));
                     }
                 } else if (idType.getKey() == IdType.ASSIGN_UUID.getKey()) {
                     metaObject.setValue(keyProperty, identifierGenerator.nextUUID(entity));

+ 10 - 0
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/AbstractWrapper.java

@@ -188,6 +188,16 @@ public abstract class AbstractWrapper<T, R, Children extends AbstractWrapper<T,
         return likeValue(condition, LIKE, column, val, SqlLike.RIGHT);
     }
 
+    @Override
+    public Children notLikeLeft(boolean condition, R column, Object val) {
+        return likeValue(condition, NOT_LIKE, column, val, SqlLike.LEFT);
+    }
+
+    @Override
+    public Children notLikeRight(boolean condition, R column, Object val) {
+        return likeValue(condition, NOT_LIKE, column, val, SqlLike.RIGHT);
+    }
+
     @Override
     public Children between(boolean condition, R column, Object val1, Object val2) {
         return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), BETWEEN,

+ 34 - 0
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/interfaces/Compare.java

@@ -249,6 +249,40 @@ public interface Compare<Children, R> extends Serializable {
      */
     Children notLike(boolean condition, R column, Object val);
 
+    /**
+     * ignore
+     */
+    default Children notLikeLeft(R column, Object val) {
+        return notLikeLeft(true, column, val);
+    }
+
+    /**
+     * NOT LIKE '%值'
+     *
+     * @param condition
+     * @param column
+     * @param val
+     * @return children
+     */
+    Children notLikeLeft(boolean condition, R column, Object val);
+
+    /**
+     * ignore
+     */
+    default Children notLikeRight(R column, Object val) {
+        return notLikeRight(true, column, val);
+    }
+
+    /**
+     * NOT LIKE '值%'
+     *
+     * @param condition
+     * @param column
+     * @param val
+     * @return children
+     */
+    Children notLikeRight(boolean condition, R column, Object val);
+
     /**
      * ignore
      */

+ 20 - 0
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/plugins/IgnoreStrategy.java

@@ -0,0 +1,20 @@
+package com.baomidou.mybatisplus.core.plugins;
+
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Map;
+
+@Getter
+@Setter
+@Builder
+public class IgnoreStrategy {
+    private Boolean tenantLine;
+    private Boolean dynamicTableName;
+    private Boolean blockAttack;
+    private Boolean illegalSql;
+    private Boolean dataPermission;
+    private Map<String, Boolean> others;
+
+}

+ 58 - 40
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/plugins/InterceptorIgnoreHelper.java

@@ -17,8 +17,6 @@ package com.baomidou.mybatisplus.core.plugins;
 
 import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
 import com.baomidou.mybatisplus.core.toolkit.*;
-import lombok.Builder;
-import lombok.Data;
 import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
 
 import java.lang.reflect.Method;
@@ -38,7 +36,33 @@ public abstract class InterceptorIgnoreHelper {
      * SQL 解析缓存
      * key 可能是 mappedStatement 的 ID,也可能是 class 的 name
      */
-    private static final Map<String, InterceptorIgnoreCache> INTERCEPTOR_IGNORE_CACHE = new ConcurrentHashMap<>();
+    private static final Map<String, IgnoreStrategy> IGNORE_STRATEGY_CACHE = new ConcurrentHashMap<>();
+    /**
+     *  本地线程拦截器忽略策略缓存
+     */
+    private static final ThreadLocal<IgnoreStrategy> IGNORE_STRATEGY_LOCAL = new ThreadLocal<>();
+
+    /**
+     * 手动设置拦截器忽略执行策略,权限大于注解权限
+     * <p>
+     * InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().tenantLine(true).build());
+     * </p>
+     * <p>
+     * 注意,需要手动关闭调用方法 InterceptorIgnoreHelper.clearIgnoreStrategy();
+     * </p>
+     *
+     * @param ignoreStrategy {@link IgnoreStrategy}
+     */
+    public static void handle(IgnoreStrategy ignoreStrategy) {
+        IGNORE_STRATEGY_LOCAL.set(ignoreStrategy);
+    }
+
+    /**
+     * 清空本地忽略策略
+     */
+    public static void clearIgnoreStrategy() {
+        IGNORE_STRATEGY_LOCAL.remove();
+    }
 
     /**
      * 初始化缓存
@@ -47,12 +71,12 @@ public abstract class InterceptorIgnoreHelper {
      *
      * @param mapperClass Mapper Class
      */
-    public synchronized static InterceptorIgnoreCache initSqlParserInfoCache(Class<?> mapperClass) {
+    public synchronized static IgnoreStrategy initSqlParserInfoCache(Class<?> mapperClass) {
         InterceptorIgnore ignore = mapperClass.getAnnotation(InterceptorIgnore.class);
         if (ignore != null) {
             String key = mapperClass.getName();
-            InterceptorIgnoreCache cache = buildInterceptorIgnoreCache(key, ignore);
-            INTERCEPTOR_IGNORE_CACHE.put(key, cache);
+            IgnoreStrategy cache = buildIgnoreStrategy(key, ignore);
+            IGNORE_STRATEGY_CACHE.put(key, cache);
             return cache;
         }
         return null;
@@ -66,62 +90,67 @@ public abstract class InterceptorIgnoreHelper {
      * @param mapperAnnotation Mapper Class Name
      * @param method           Method
      */
-    public static void initSqlParserInfoCache(InterceptorIgnoreCache mapperAnnotation, String mapperClassName, Method method) {
-        InterceptorIgnore ignore = method.getAnnotation(InterceptorIgnore.class);
+    public static void initSqlParserInfoCache(IgnoreStrategy mapperAnnotation, String mapperClassName, Method method) {
+        InterceptorIgnore ignoreStrategy = method.getAnnotation(InterceptorIgnore.class);
         String key = mapperClassName.concat(StringPool.DOT).concat(method.getName());
         String name = mapperClassName.concat(StringPool.HASH).concat(method.getName());
-        if (ignore != null) {
-            InterceptorIgnoreCache methodCache = buildInterceptorIgnoreCache(name, ignore);
+        if (ignoreStrategy != null) {
+            IgnoreStrategy methodCache = buildIgnoreStrategy(name, ignoreStrategy);
             if (mapperAnnotation == null) {
-                INTERCEPTOR_IGNORE_CACHE.put(key, methodCache);
+                IGNORE_STRATEGY_CACHE.put(key, methodCache);
                 return;
             }
-            INTERCEPTOR_IGNORE_CACHE.put(key, chooseCache(mapperAnnotation, methodCache));
+            IGNORE_STRATEGY_CACHE.put(key, chooseCache(mapperAnnotation, methodCache));
         }
     }
 
     public static boolean willIgnoreTenantLine(String id) {
-        return willIgnore(id, InterceptorIgnoreCache::getTenantLine);
+        return willIgnore(id, IgnoreStrategy::getTenantLine);
     }
 
     public static boolean willIgnoreDynamicTableName(String id) {
-        return willIgnore(id, InterceptorIgnoreCache::getDynamicTableName);
+        return willIgnore(id, IgnoreStrategy::getDynamicTableName);
     }
 
     public static boolean willIgnoreBlockAttack(String id) {
-        return willIgnore(id, InterceptorIgnoreCache::getBlockAttack);
+        return willIgnore(id, IgnoreStrategy::getBlockAttack);
     }
 
     public static boolean willIgnoreIllegalSql(String id) {
-        return willIgnore(id, InterceptorIgnoreCache::getIllegalSql);
+        return willIgnore(id, IgnoreStrategy::getIllegalSql);
     }
 
     public static boolean willIgnoreDataPermission(String id) {
-        return willIgnore(id, InterceptorIgnoreCache::getDataPermission);
+        return willIgnore(id, IgnoreStrategy::getDataPermission);
     }
 
     public static boolean willIgnoreOthersByKey(String id, String key) {
         return willIgnore(id, i -> CollectionUtils.isNotEmpty(i.getOthers()) && i.getOthers().getOrDefault(key, false));
     }
 
-    public static boolean willIgnore(String id, Function<InterceptorIgnoreCache, Boolean> function) {
-        InterceptorIgnoreCache cache = INTERCEPTOR_IGNORE_CACHE.get(id);
-        if (cache == null && id.endsWith(SelectKeyGenerator.SELECT_KEY_SUFFIX)) {
+    public static boolean willIgnore(String id, Function<IgnoreStrategy, Boolean> function) {
+        // 1,优化获取本地忽略策略
+        IgnoreStrategy ignoreStrategy = IGNORE_STRATEGY_LOCAL.get();
+        if (null == ignoreStrategy) {
+            // 2,不存在取注解策略
+            ignoreStrategy = IGNORE_STRATEGY_CACHE.get(id);
+        }
+        if (ignoreStrategy == null && id.endsWith(SelectKeyGenerator.SELECT_KEY_SUFFIX)) {
             // 支持一下 selectKey
-            cache = INTERCEPTOR_IGNORE_CACHE.get(id.substring(0, id.length() - SelectKeyGenerator.SELECT_KEY_SUFFIX.length()));
+            ignoreStrategy = IGNORE_STRATEGY_CACHE.get(id.substring(0, id.length() - SelectKeyGenerator.SELECT_KEY_SUFFIX.length()));
         }
-        if (cache == null) {
-            cache = INTERCEPTOR_IGNORE_CACHE.get(id.substring(0, id.lastIndexOf(StringPool.DOT)));
+        if (ignoreStrategy == null) {
+            ignoreStrategy = IGNORE_STRATEGY_CACHE.get(id.substring(0, id.lastIndexOf(StringPool.DOT)));
         }
-        if (cache != null) {
-            Boolean apply = function.apply(cache);
+        if (ignoreStrategy != null) {
+            Boolean apply = function.apply(ignoreStrategy);
             return apply != null && apply;
         }
         return false;
     }
 
-    private static InterceptorIgnoreCache chooseCache(InterceptorIgnoreCache mapper, InterceptorIgnoreCache method) {
-        return InterceptorIgnoreCache.builder()
+    private static IgnoreStrategy chooseCache(IgnoreStrategy mapper, IgnoreStrategy method) {
+        return IgnoreStrategy.builder()
             .tenantLine(chooseBoolean(mapper.getTenantLine(), method.getTenantLine()))
             .dynamicTableName(chooseBoolean(mapper.getDynamicTableName(), method.getDynamicTableName()))
             .blockAttack(chooseBoolean(mapper.getBlockAttack(), method.getBlockAttack()))
@@ -131,8 +160,8 @@ public abstract class InterceptorIgnoreHelper {
             .build();
     }
 
-    private static InterceptorIgnoreCache buildInterceptorIgnoreCache(String name, InterceptorIgnore ignore) {
-        return InterceptorIgnoreCache.builder()
+    private static IgnoreStrategy buildIgnoreStrategy(String name, InterceptorIgnore ignore) {
+        return IgnoreStrategy.builder()
             .tenantLine(getBoolean("tenantLine", name, ignore.tenantLine()))
             .dynamicTableName(getBoolean("dynamicTableName", name, ignore.dynamicTableName()))
             .blockAttack(getBoolean("blockAttack", name, ignore.blockAttack()))
@@ -204,15 +233,4 @@ public abstract class InterceptorIgnoreHelper {
         methodKeys.forEach(k -> map.put(k, chooseBoolean(mapper.get(k), method.get(k))));
         return map;
     }
-
-    @Data
-    @Builder
-    public static class InterceptorIgnoreCache {
-        private Boolean tenantLine;
-        private Boolean dynamicTableName;
-        private Boolean blockAttack;
-        private Boolean illegalSql;
-        private Boolean dataPermission;
-        private Map<String, Boolean> others;
-    }
 }

+ 2 - 0
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/StringPool.java

@@ -64,6 +64,7 @@ public interface StringPool {
     String N = "n";
     String NO = "no";
     String NULL = "null";
+    String NUM = "NUM";
     String OFF = "off";
     String ON = "on";
     String PERCENT = "%";
@@ -81,6 +82,7 @@ public interface StringPool {
     String SINGLE_QUOTE = "'";
     String BACKTICK = "`";
     String SPACE = " ";
+    String SQL = "sql";
     String TILDA = "~";
     String LEFT_SQ_BRACKET = "[";
     String RIGHT_SQ_BRACKET = "]";

+ 2 - 2
mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/core/conditions/QueryWrapperTest.java

@@ -90,9 +90,9 @@ class QueryWrapperTest extends BaseWrapperTest {
             .or().gt("id", 1).ge("id", 1)
             .lt("id", 1).le("id", 1)
             .or().between("id", 1, 2).notBetween("id", 1, 3)
-            .like("id", 1).notLike("id", 1)
+            .like("id", 1).notLike("id", 1).notLikeLeft("id", 3).notLikeRight("id", 4)
             .or().likeLeft("id", 1).likeRight("id", 1);
-        logSqlWhere("测试 Compare 下的方法", queryWrapper, "(column1 = ? AND column0 = ? AND nullColumn IS NULL AND column1 = ? AND column0 = ? AND nullColumn IS NULL AND id = ? AND id <> ? OR id > ? AND id >= ? AND id < ? AND id <= ? OR id BETWEEN ? AND ? AND id NOT BETWEEN ? AND ? AND id LIKE ? AND id NOT LIKE ? OR id LIKE ? AND id LIKE ?)");
+        logSqlWhere("测试 Compare 下的方法", queryWrapper, "(column1 = ? AND column0 = ? AND nullColumn IS NULL AND column1 = ? AND column0 = ? AND nullColumn IS NULL AND id = ? AND id <> ? OR id > ? AND id >= ? AND id < ? AND id <= ? OR id BETWEEN ? AND ? AND id NOT BETWEEN ? AND ? AND id LIKE ? AND id NOT LIKE ? AND id NOT LIKE ? AND id NOT LIKE ? OR id LIKE ? AND id LIKE ?)");
         logParams(queryWrapper);
     }
 

+ 2 - 2
mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/core/plugins/InterceptorIgnoreHelperTest.java

@@ -46,9 +46,9 @@ class InterceptorIgnoreHelperTest {
     }
 
     private void init(Class<?> clazz) {
-        InterceptorIgnoreHelper.InterceptorIgnoreCache cache = InterceptorIgnoreHelper.initSqlParserInfoCache(clazz);
+        IgnoreStrategy ignoreStrategy = InterceptorIgnoreHelper.initSqlParserInfoCache(clazz);
         for (Method method : clazz.getMethods()) {
-            InterceptorIgnoreHelper.initSqlParserInfoCache(cache, clazz.getName(), method);
+            InterceptorIgnoreHelper.initSqlParserInfoCache(ignoreStrategy, clazz.getName(), method);
         }
     }
 

+ 0 - 1
mybatis-plus-extension/build.gradle

@@ -1,4 +1,3 @@
-apply plugin: 'kotlin'
 
 dependencies {
     api project(":mybatis-plus-core")

+ 16 - 4
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/conditions/AbstractChainWrapper.java

@@ -134,6 +134,18 @@ public abstract class AbstractChainWrapper<T, R, Children extends AbstractChainW
         return typedThis;
     }
 
+    @Override
+    public Children likeLeft(boolean condition, R column, Object val) {
+        getWrapper().likeLeft(condition, column, val);
+        return typedThis;
+    }
+
+    @Override
+    public Children likeRight(boolean condition, R column, Object val) {
+        getWrapper().likeRight(condition, column, val);
+        return typedThis;
+    }
+
     @Override
     public Children notLike(boolean condition, R column, Object val) {
         getWrapper().notLike(condition, column, val);
@@ -141,14 +153,14 @@ public abstract class AbstractChainWrapper<T, R, Children extends AbstractChainW
     }
 
     @Override
-    public Children likeLeft(boolean condition, R column, Object val) {
-        getWrapper().likeLeft(condition, column, val);
+    public Children notLikeLeft(boolean condition, R column, Object val) {
+        getWrapper().notLikeLeft(condition, column, val);
         return typedThis;
     }
 
     @Override
-    public Children likeRight(boolean condition, R column, Object val) {
-        getWrapper().likeRight(condition, column, val);
+    public Children notLikeRight(boolean condition, R column, Object val) {
+        getWrapper().notLikeRight(condition, column, val);
         return typedThis;
     }
 

+ 143 - 0
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/ddl/DdlHelper.java

@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2011-2022, baomidou (jobob@qq.com).
+ *
+ * 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.extension.ddl;
+
+import com.baomidou.mybatisplus.core.toolkit.StringPool;
+import com.baomidou.mybatisplus.extension.ddl.history.IDdlGenerator;
+import com.baomidou.mybatisplus.extension.ddl.history.MysqlDdlGenerator;
+import com.baomidou.mybatisplus.extension.ddl.history.OracleDdlGenerator;
+import com.baomidou.mybatisplus.extension.ddl.history.PostgreDdlGenerator;
+import com.baomidou.mybatisplus.extension.plugins.pagination.DialectFactory;
+import com.baomidou.mybatisplus.extension.plugins.pagination.dialects.*;
+import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.ibatis.jdbc.ScriptRunner;
+import org.apache.ibatis.jdbc.SqlRunner;
+import org.springframework.core.io.ClassPathResource;
+
+import javax.sql.DataSource;
+import java.io.*;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * DDL 辅助类
+ *
+ * @author hubin
+ * @since 2021-06-22
+ */
+@Slf4j
+public class DdlHelper {
+
+    /**
+     * 允许 SQL 脚本文件
+     *
+     * @param ddlGenerator DDL 生成器
+     * @param dataSource   数据源
+     * @param sqlFiles     SQL 文件列表
+     * @param autoCommit   字段提交事务
+     * @throws Exception
+     */
+    public static void runScript(IDdlGenerator ddlGenerator, DataSource dataSource, List<String> sqlFiles, boolean autoCommit) {
+        try (Connection connection = dataSource.getConnection()) {
+            // 执行自定义 DDL 信息
+            final String jdbcUrl = connection.getMetaData().getURL();
+            final String schema = DdlHelper.getDatabase(jdbcUrl);
+            SqlRunner sqlRunner = new SqlRunner(connection);
+            ScriptRunner scriptRunner = getScriptRunner(connection, autoCommit);
+            if (null == ddlGenerator) {
+                ddlGenerator = getDdlGenerator(jdbcUrl);
+            }
+            if (!ddlGenerator.existTable(schema, sql -> {
+                try {
+                    Map<String, Object> resultMap = sqlRunner.selectOne(sql);
+                    if (null != resultMap && !StringPool.ZERO.equals(String.valueOf(resultMap.get(StringPool.NUM)))) {
+                        return true;
+                    }
+                } catch (SQLException e) {
+                    log.error("run script sql:{} , error: {}", sql, e.getMessage());
+                }
+                return false;
+            })) {
+                scriptRunner.runScript(new StringReader(ddlGenerator.createDdlHistory()));
+            }
+            // 执行 SQL 脚本
+            for (String sqlFile : sqlFiles) {
+                try {
+                    List<Map<String, Object>> objectMap = sqlRunner.selectAll(ddlGenerator.selectDdlHistory(sqlFile, StringPool.SQL));
+                    if (null == objectMap || objectMap.isEmpty()) {
+                        log.debug("run script file: {}", sqlFile);
+                        File file = new File(sqlFile);
+                        if (file.exists()) {
+                            scriptRunner.runScript(new FileReader(file));
+                        } else {
+                            scriptRunner.runScript(new InputStreamReader(getInputStream(sqlFile)));
+                        }
+                        sqlRunner.insert(ddlGenerator.insertDdlHistory(sqlFile, StringPool.SQL, getNowTime()));
+                    }
+                } catch (Exception e) {
+                    log.error("run script sql:{} , error: {} , Please check if the table `ddl_history` exists", sqlFile, e.getMessage());
+                }
+            }
+        } catch (Exception e) {
+            log.error("run script error: {}", e.getMessage());
+        }
+    }
+
+    public static InputStream getInputStream(String path) throws Exception {
+        return new ClassPathResource(path).getInputStream();
+    }
+
+    protected static String getNowTime() {
+        return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmm"));
+    }
+
+    public static ScriptRunner getScriptRunner(Connection connection, boolean autoCommit) {
+        ScriptRunner scriptRunner = new ScriptRunner(connection);
+        scriptRunner.setAutoCommit(autoCommit);
+        scriptRunner.setStopOnError(true);
+        return scriptRunner;
+    }
+
+    protected static IDdlGenerator getDdlGenerator(String jdbcUrl) throws RuntimeException {
+        IDialect dialect = DialectFactory.getDialect(JdbcUtils.getDbType(jdbcUrl));
+        if (dialect instanceof MySqlDialect) {
+            return MysqlDdlGenerator.newInstance();
+        }
+        if (dialect instanceof PostgreDialect) {
+            return PostgreDdlGenerator.newInstance();
+        }
+        if (dialect instanceof OracleDialect || dialect instanceof Oracle12cDialect) {
+            return OracleDdlGenerator.newInstance();
+        }
+        throw new RuntimeException("The database is not supported");
+    }
+
+    public static String getDatabase(String jdbcUrl) {
+        String[] urlArr = jdbcUrl.split("://");
+        if (urlArr.length == 2) {
+            String[] dataArr = urlArr[1].split("/");
+            if (dataArr.length > 1) {
+                return dataArr[1].split("\\?")[0];
+            }
+        }
+        return null;
+    }
+}

+ 87 - 0
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/ddl/DdlScript.java

@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2011-2022, baomidou (jobob@qq.com).
+ *
+ * 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.extension.ddl;
+
+import com.baomidou.mybatisplus.extension.ddl.history.IDdlGenerator;
+
+import javax.sql.DataSource;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.List;
+
+/**
+ * Ddl 脚本执行
+ *
+ * @author hubin
+ * @since 2021-07-23
+ */
+public class DdlScript {
+    private DataSource dataSource;
+    private IDdlGenerator ddlGenerator;
+    private boolean autoCommit;
+
+    public DdlScript(DataSource dataSource) {
+        this(dataSource, null);
+    }
+
+    public DdlScript(DataSource dataSource, IDdlGenerator ddlGenerator) {
+        this(dataSource, ddlGenerator, false);
+    }
+
+    public DdlScript(DataSource dataSource, IDdlGenerator ddlGenerator, boolean autoCommit) {
+        this.dataSource = dataSource;
+        this.ddlGenerator = ddlGenerator;
+        this.autoCommit = autoCommit;
+    }
+
+    public void run(List<String> sqlFiles) {
+        this.run(sqlFiles, this.autoCommit);
+    }
+
+    /**
+     * 执行 SQL 脚本文件
+     *
+     * @param sqlFiles SQL 脚本文件列表
+     */
+    public void run(List<String> sqlFiles, boolean autoCommit) {
+        DdlHelper.runScript(this.ddlGenerator, this.dataSource, sqlFiles, autoCommit);
+    }
+
+    /**
+     * 执行 SQL 脚本
+     *
+     * @param sqlScript SQL 脚本内容
+     * @throws Exception
+     */
+    public void run(String sqlScript) throws Exception {
+        this.run(new StringReader(sqlScript));
+    }
+
+    public void run(Reader reader) throws Exception {
+        this.run(reader, this.autoCommit);
+    }
+
+    /**
+     * 执行 SQL 脚本
+     *
+     * @param reader     SQL 脚本内容
+     * @param autoCommit 自动提交事务
+     * @throws Exception
+     */
+    public void run(Reader reader, boolean autoCommit) throws Exception {
+        DdlHelper.getScriptRunner(dataSource.getConnection(), autoCommit).runScript(reader);
+    }
+}

+ 53 - 0
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/ddl/IDdl.java

@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2011-2022, baomidou (jobob@qq.com).
+ *
+ * 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.extension.ddl;
+
+import com.baomidou.mybatisplus.extension.ddl.history.IDdlGenerator;
+
+import javax.sql.DataSource;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * DDL 处理器
+ *
+ * @author hubin
+ * @since 2021-06-22
+ */
+public interface IDdl {
+
+    /**
+     * 执行 SQL 脚本
+     *
+     * @param consumer 指定数据源执行
+     */
+    void runScript(Consumer<DataSource> consumer);
+
+    /**
+     * DDL 生成器
+     */
+    default IDdlGenerator getDdlGenerator() {
+        return null;
+    }
+
+    /**
+     * 执行 SQL 脚本
+     * <p>Resources.getResourceAsReader("db/test.sql")</p>
+     *
+     * @return SQL 脚本文件列表
+     */
+    List<String> getSqlFiles();
+}

+ 42 - 0
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/ddl/SimpleDdl.java

@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2011-2022, baomidou (jobob@qq.com).
+ *
+ * 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.extension.ddl;
+
+import javax.annotation.Resource;
+import javax.sql.DataSource;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * 非多数据源 DDL 实现
+ *
+ * @author hubin
+ * @since 2021-09-23
+ */
+public class SimpleDdl implements IDdl {
+    @Resource
+    private DataSource dataSource;
+
+    @Override
+    public void runScript(Consumer<DataSource> consumer) {
+        consumer.accept(dataSource);
+    }
+
+    @Override
+    public List<String> getSqlFiles() {
+        return null;
+    }
+}

+ 82 - 0
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/ddl/history/IDdlGenerator.java

@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2011-2022, baomidou (jobob@qq.com).
+ *
+ * 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.extension.ddl.history;
+
+import java.util.function.Function;
+
+/**
+ * DDL 生成器接口
+ *
+ * @author hubin
+ * @since 2021-06-22
+ */
+public interface IDdlGenerator {
+
+    /**
+     * 表是否存在
+     *
+     * @param databaseName    数据库名称
+     * @param executeFunction 执行判断函数
+     * @return exist or no
+     */
+    boolean existTable(String databaseName, Function<String, Boolean> executeFunction);
+
+    /**
+     * 返回 DDL_HISTORY 表名
+     *
+     * @return SQL
+     */
+    default String getDdlHistory() {
+        return "ddl_history";
+    }
+
+    /**
+     * ddl_history sql
+     *
+     * @return SQL
+     */
+    String createDdlHistory();
+
+    /**
+     * select ddl_history sql
+     *
+     * @param script Sql Script
+     * @param type   Execute Type
+     * @return SQL
+     */
+    default String selectDdlHistory(String script, String type) {
+        StringBuffer sql = new StringBuffer();
+        sql.append("SELECT version FROM ").append(getDdlHistory()).append(" WHERE script='").append(script);
+        sql.append("' AND type='").append(type).append("'");
+        return sql.toString();
+    }
+
+    /**
+     * insert ddl_history sql
+     *
+     * @param script  Sql Script
+     * @param type    Execute Type
+     * @param version Execute Version
+     * @return SQL
+     */
+    default String insertDdlHistory(String script, String type, String version) {
+        StringBuffer sql = new StringBuffer();
+        sql.append("INSERT INTO ").append(getDdlHistory()).append("(script,type,version) VALUES ('");
+        sql.append(script).append("','").append(type).append("','").append(version).append("')");
+        return sql.toString();
+    }
+
+}

+ 56 - 0
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/ddl/history/MysqlDdlGenerator.java

@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2011-2022, baomidou (jobob@qq.com).
+ *
+ * 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.extension.ddl.history;
+
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+
+import java.util.function.Function;
+
+/**
+ * Mysql DDL 生成器
+ *
+ * @author hubin
+ * @since 2021-06-22
+ */
+public class MysqlDdlGenerator implements IDdlGenerator {
+
+    public static IDdlGenerator newInstance() {
+        return new MysqlDdlGenerator();
+    }
+
+    @Override
+    public boolean existTable(String databaseName, Function<String, Boolean> executeFunction) {
+        StringBuffer sql = new StringBuffer();
+        sql.append("SELECT COUNT(1) AS NUM from INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='");
+        sql.append(getDdlHistory()).append("' AND TABLE_TYPE='BASE TABLE'");
+        if (StringUtils.isNotBlank(databaseName)) {
+            sql.append(" AND TABLE_SCHEMA='").append(databaseName).append("'");
+        }
+        return executeFunction.apply(sql.toString());
+    }
+
+    @Override
+    public String createDdlHistory() {
+        StringBuffer sql = new StringBuffer();
+        sql.append("CREATE TABLE IF NOT EXISTS `").append(getDdlHistory()).append("` (");
+        sql.append("`script` varchar(500) NOT NULL COMMENT '脚本',");
+        sql.append("`type` varchar(30) NOT NULL COMMENT '类型',");
+        sql.append("`version` varchar(30) NOT NULL COMMENT '版本',");
+        sql.append("PRIMARY KEY (`script`)");
+        sql.append(") COMMENT = 'DDL 版本';");
+        return sql.toString();
+    }
+}

+ 53 - 0
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/ddl/history/OracleDdlGenerator.java

@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2011-2022, baomidou (jobob@qq.com).
+ *
+ * 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.extension.ddl.history;
+
+import java.util.function.Function;
+
+/**
+ * Oracle DDL 生成器
+ *
+ * @author hubin
+ * @since 2021-06-22
+ */
+public class OracleDdlGenerator implements IDdlGenerator {
+
+    public static IDdlGenerator newInstance() {
+        return new OracleDdlGenerator();
+    }
+
+    @Override
+    public boolean existTable(String databaseName, Function<String, Boolean> executeFunction) {
+        return executeFunction.apply("SELECT COUNT(1) AS NUM FROM user_tables WHERE table_name='"
+                + getDdlHistory() + "'");
+    }
+
+    @Override
+    public String getDdlHistory() {
+        return "DDL_HISTORY";
+    }
+
+    @Override
+    public String createDdlHistory() {
+        StringBuffer sql = new StringBuffer();
+        sql.append("CREATE TABLE ").append(getDdlHistory()).append("(");
+        sql.append("script NVARCHAR2(500) NOT NULL,");
+        sql.append("type NVARCHAR2(30) NOT NULL,");
+        sql.append("version NVARCHAR2(30) NOT NULL");
+        sql.append(");");
+        return sql.toString();
+    }
+}

+ 67 - 0
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/ddl/history/PostgreDdlGenerator.java

@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2011-2022, baomidou (jobob@qq.com).
+ *
+ * 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.extension.ddl.history;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.function.Function;
+
+
+/**
+ * PostgreSQL DDL 生成器
+ *
+ * @author hubin
+ * @since 2021-06-22
+ */
+public class PostgreDdlGenerator implements IDdlGenerator {
+
+    public static IDdlGenerator newInstance() {
+        return new PostgreDdlGenerator();
+    }
+
+    @Override
+    public boolean existTable(String databaseName, Function<String, Boolean> executeFunction) {
+        StringBuffer sql = new StringBuffer();
+        sql.append("SELECT COUNT(1) AS NUM from INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='ddl_history' AND TABLE_TYPE='BASE TABLE'");
+        if (StringUtils.isNoneBlank(this.getSchema())) {
+            sql.append(" AND TABLE_SCHEMA='").append(this.getSchema()).append("'");
+        }
+        return executeFunction.apply(sql.toString());
+    }
+    @Override
+    public String getDdlHistory() {
+        return "\"" + this.getSchema() + "\".\"ddl_history\"";
+    }
+    @Override
+    public String createDdlHistory() {
+        StringBuffer sql = new StringBuffer();
+        String ddlHistory = this.getDdlHistory();
+        sql.append("CREATE TABLE IF NOT EXISTS ").append(ddlHistory).append(" (");
+        sql.append("\"script\" varchar(500) NOT NULL,");
+        sql.append("\"type\" varchar(30) NOT NULL,");
+        sql.append("\"version\" varchar(30) NOT NULL");
+        sql.append(");");
+        sql.append("COMMENT ON COLUMN ").append(ddlHistory).append(".\"script\" IS '脚本';");
+        sql.append("COMMENT ON COLUMN ").append(ddlHistory).append(".\"type\" IS '类型';");
+        sql.append("COMMENT ON COLUMN ").append(ddlHistory).append(".\"version\" IS '版本';");
+        sql.append("COMMENT ON TABLE ").append(ddlHistory).append(" IS 'DDL 版本';");
+        return sql.toString();
+    }
+
+    protected String getSchema() {
+        return "public";
+    }
+}

+ 2 - 2
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/BaseMultiTableInnerInterceptor.java

@@ -320,8 +320,6 @@ public abstract class BaseMultiTableInnerInterceptor extends JsqlParserSupport i
                     if (leftTable != null) {
                         onTables = Collections.singletonList(leftTable);
                     }
-                } else if (join.isLeft()) {
-                    onTables = Collections.singletonList(joinTable);
                 } else if (join.isInner()) {
                     if (mainTable == null) {
                         onTables = Collections.singletonList(joinTable);
@@ -329,6 +327,8 @@ public abstract class BaseMultiTableInnerInterceptor extends JsqlParserSupport i
                         onTables = Arrays.asList(mainTable, joinTable);
                     }
                     mainTable = null;
+                } else {
+                    onTables = Collections.singletonList(joinTable);
                 }
 
                 mainTables = new ArrayList<>();

+ 1 - 1
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/pagination/dialects/SQLServer2005Dialect.java

@@ -29,7 +29,7 @@ public class SQLServer2005Dialect implements IDialect {
 
     private static String getOrderByPart(String sql) {
         String loweredString = sql.toLowerCase();
-        int orderByIndex = loweredString.indexOf("order by");
+        int orderByIndex = loweredString.lastIndexOf("order by");
         if (orderByIndex != -1) {
             return sql.substring(orderByIndex);
         } else {

+ 62 - 2
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/pagination/dialects/SybaseDialect.java

@@ -17,6 +17,12 @@ package com.baomidou.mybatisplus.extension.plugins.pagination.dialects;
 
 import com.baomidou.mybatisplus.extension.plugins.pagination.DialectModel;
 
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
 /**
  * sybase 数据库分页方言
  *
@@ -37,8 +43,10 @@ public class SybaseDialect implements IDialect {
 
     @Override
     public DialectModel buildPaginationSql(String originalSql, long offset, long limit) {
-        String tempSql = originalSql.toUpperCase();
-        int index = tempSql.indexOf(" FROM ");
+        int index = findMainFROM(originalSql);
+        if(index == -1){
+            index = originalSql.toUpperCase().indexOf(" FROM ");
+        }
         String sql = "select";
         if (hasTop) {
             sql += " top " + (offset + limit);
@@ -48,4 +56,56 @@ public class SybaseDialect implements IDialect {
         sql += "drop table #t ";
         return new DialectModel(sql, offset, offset + limit).setConsumerChain();
     }
+
+    /**
+     * 查找主查询的FROM位置
+     * @param sql   需要查找的SQL
+     * @return  FROM位置的起始下标
+     * @author lroyia
+     * @since  2022年6月15日 17:57:28
+     */
+    private int findMainFROM(String sql){
+        String tempSql = sql.toUpperCase();
+        tempSql = tempSql.replace("\n", " ").replace("\t", " ").replace("\r", " ");
+        Matcher select_ = Pattern.compile("SELECT ").matcher(tempSql);
+        Matcher from_ = Pattern.compile(" FROM ").matcher(tempSql);
+        List<Integer> selectIndex = new ArrayList<>(10);
+        List<Integer> fromIndex = new ArrayList<>(10);
+        while (select_.find()){
+            int start = select_.start();
+            if(start == 0 || tempSql.charAt(start - 1) == ' '|| tempSql.charAt(start - 1) == '('){
+                selectIndex.add(start);
+            }
+        }
+        while (from_.find()){
+            fromIndex.add(from_.start());
+        }
+
+        // 形成select与from的混合顺序下标列表
+        List<Integer> indexList = new ArrayList<>(20);
+        indexList.addAll(selectIndex);
+        indexList.addAll(fromIndex);
+        indexList.sort(Comparator.naturalOrder());
+        // 无法匹配有效下标
+        if(indexList.size() < 2){
+            return -1;
+        }
+        // 利用栈逻辑匹配select与from
+        int selectCount = 1;
+        for(int i = 1; i < indexList.size(); i++){
+            int each = indexList.get(i);
+            if(fromIndex.contains(each)){
+                // pointer弹栈
+                selectCount--;
+            }else{
+                // pointer压栈
+                selectCount++;
+            }
+            // from将全部select弹出,代表当前这个from为主要from
+            if(selectCount == 0){
+                return each;
+            }
+        }
+        return -1;
+    }
 }

+ 3 - 3
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/toolkit/Db.java

@@ -509,7 +509,7 @@ public class Db {
      * @return 实体类型
      */
     @SuppressWarnings("unchecked")
-    private static <T> Class<T> getEntityClass(Collection<T> entityList) {
+    protected static <T> Class<T> getEntityClass(Collection<T> entityList) {
         Class<T> entityClass = null;
         for (T entity : entityList) {
             if (entity != null && entity.getClass() != null) {
@@ -529,7 +529,7 @@ public class Db {
      * @return 实体类型
      */
     @SuppressWarnings("unchecked")
-    private static <T> Class<T> getEntityClass(AbstractWrapper<T, ?, ?> queryWrapper) {
+    protected static <T> Class<T> getEntityClass(AbstractWrapper<T, ?, ?> queryWrapper) {
         Class<T> entityClass = queryWrapper.getEntityClass();
         if (entityClass == null) {
             T entity = queryWrapper.getEntity();
@@ -548,7 +548,7 @@ public class Db {
      * @param <T>         实体类型
      * @return 对应表信息
      */
-    private static <T> TableInfo getTableInfo(Class<T> entityClass) {
+    protected static <T> TableInfo getTableInfo(Class<T> entityClass) {
         return Optional.ofNullable(TableInfoHelper.getTableInfo(entityClass)).orElseThrow(() -> ExceptionUtils.mpe("error: can not find TableInfo from Class: \"%s\".", entityClass.getName()));
     }
 }

+ 27 - 29
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/toolkit/SimpleQuery.java

@@ -15,18 +15,28 @@
  */
 package com.baomidou.mybatisplus.extension.toolkit;
 
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
-import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
-import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
-
-import java.util.*;
-import java.util.function.*;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.BinaryOperator;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
 import java.util.stream.Collector;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
+import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
+
 /**
  * simple-query 让简单的查询更简单
  *
@@ -55,7 +65,7 @@ public class SimpleQuery {
      */
     @SafeVarargs
     public static <E, A> Map<A, E> keyMap(LambdaQueryWrapper<E> wrapper, SFunction<E, A> sFunction, Consumer<E>... peeks) {
-        return list2Map(selectList(getType(sFunction), wrapper), sFunction, Function.identity(), peeks);
+        return list2Map(Db.list(wrapper.setEntityClass(getType(sFunction))), sFunction, Function.identity(), peeks);
     }
 
     /**
@@ -71,7 +81,7 @@ public class SimpleQuery {
      */
     @SafeVarargs
     public static <E, A> Map<A, E> keyMap(LambdaQueryWrapper<E> wrapper, SFunction<E, A> sFunction, boolean isParallel, Consumer<E>... peeks) {
-        return list2Map(selectList(getType(sFunction), wrapper), sFunction, Function.identity(), isParallel, peeks);
+        return list2Map(Db.list(wrapper.setEntityClass(getType(sFunction))), sFunction, Function.identity(), isParallel, peeks);
     }
 
     /**
@@ -79,7 +89,7 @@ public class SimpleQuery {
      */
     @SafeVarargs
     public static <E, A, P> Map<A, P> map(LambdaQueryWrapper<E> wrapper, SFunction<E, A> keyFunc, SFunction<E, P> valueFunc, Consumer<E>... peeks) {
-        return list2Map(selectList(getType(keyFunc), wrapper), keyFunc, valueFunc, peeks);
+        return list2Map(Db.list(wrapper.setEntityClass(getType(keyFunc))), keyFunc, valueFunc, peeks);
     }
 
     /**
@@ -97,7 +107,7 @@ public class SimpleQuery {
      */
     @SafeVarargs
     public static <E, A, P> Map<A, P> map(LambdaQueryWrapper<E> wrapper, SFunction<E, A> keyFunc, SFunction<E, P> valueFunc, boolean isParallel, Consumer<E>... peeks) {
-        return list2Map(selectList(getType(keyFunc), wrapper), keyFunc, valueFunc, isParallel, peeks);
+        return list2Map(Db.list(wrapper.setEntityClass(getType(keyFunc))), keyFunc, valueFunc, isParallel, peeks);
     }
 
     /**
@@ -105,7 +115,7 @@ public class SimpleQuery {
      */
     @SafeVarargs
     public static <E, A> Map<A, List<E>> group(LambdaQueryWrapper<E> wrapper, SFunction<E, A> sFunction, Consumer<E>... peeks) {
-        return listGroupBy(selectList(getType(sFunction), wrapper), sFunction, peeks);
+        return listGroupBy(Db.list(wrapper.setEntityClass(getType(sFunction))), sFunction, peeks);
     }
 
     /**
@@ -113,7 +123,7 @@ public class SimpleQuery {
      */
     @SafeVarargs
     public static <T, K> Map<K, List<T>> group(LambdaQueryWrapper<T> wrapper, SFunction<T, K> sFunction, boolean isParallel, Consumer<T>... peeks) {
-        return listGroupBy(selectList(getType(sFunction), wrapper), sFunction, isParallel, peeks);
+        return listGroupBy(Db.list(wrapper.setEntityClass(getType(sFunction))), sFunction, isParallel, peeks);
     }
 
     /**
@@ -121,7 +131,7 @@ public class SimpleQuery {
      */
     @SafeVarargs
     public static <T, K, D, A> Map<K, D> group(LambdaQueryWrapper<T> wrapper, SFunction<T, K> sFunction, Collector<T, A, D> downstream, Consumer<T>... peeks) {
-        return listGroupBy(selectList(getType(sFunction), wrapper), sFunction, downstream, false, peeks);
+        return listGroupBy(Db.list(wrapper.setEntityClass(getType(sFunction))), sFunction, downstream, false, peeks);
     }
 
     /**
@@ -140,7 +150,7 @@ public class SimpleQuery {
      */
     @SafeVarargs
     public static <T, K, D, A> Map<K, D> group(LambdaQueryWrapper<T> wrapper, SFunction<T, K> sFunction, Collector<T, A, D> downstream, boolean isParallel, Consumer<T>... peeks) {
-        return listGroupBy(selectList(getType(sFunction), wrapper), sFunction, downstream, isParallel, peeks);
+        return listGroupBy(Db.list(wrapper.setEntityClass(getType(sFunction))), sFunction, downstream, isParallel, peeks);
     }
 
     /**
@@ -148,7 +158,7 @@ public class SimpleQuery {
      */
     @SafeVarargs
     public static <E, A> List<A> list(LambdaQueryWrapper<E> wrapper, SFunction<E, A> sFunction, Consumer<E>... peeks) {
-        return list2List(selectList(getType(sFunction), wrapper), sFunction, peeks);
+        return list2List(Db.list(wrapper.setEntityClass(getType(sFunction))), sFunction, peeks);
     }
 
     /**
@@ -163,7 +173,7 @@ public class SimpleQuery {
      */
     @SafeVarargs
     public static <E, A> List<A> list(LambdaQueryWrapper<E> wrapper, SFunction<E, A> sFunction, boolean isParallel, Consumer<E>... peeks) {
-        return list2List(selectList(getType(sFunction), wrapper), sFunction, isParallel, peeks);
+        return list2List(Db.list(wrapper.setEntityClass(getType(sFunction))), sFunction, isParallel, peeks);
     }
 
     /**
@@ -317,16 +327,4 @@ public class SimpleQuery {
         return Stream.of(peeks).reduce(StreamSupport.stream(list.spliterator(), isParallel), Stream::peek, Stream::concat);
     }
 
-    /**
-     * 通过entityClass查询列表,并关闭sqlSession
-     *
-     * @param entityClass 表对应实体
-     * @param wrapper     条件构造器
-     * @param <E>         实体类型
-     * @return 查询列表结果
-     */
-    public static <E> List<E> selectList(Class<E> entityClass, LambdaQueryWrapper<E> wrapper) {
-        return SqlHelper.execute(entityClass, m -> m.selectList(wrapper));
-    }
-
 }

+ 21 - 9
mybatis-plus-extension/src/test/java/com/baomidou/mybatisplus/extension/plugins/inner/TenantLineInnerInterceptorTest.java

@@ -51,7 +51,7 @@ class TenantLineInnerInterceptorTest {
             "INSERT INTO entity (id, name, tenant_id) VALUES (?, ?, ?)");
         // insert into select
         assertSql("insert into entity (id,name) select id,name from entity2",
-            "INSERT INTO entity (id, name, tenant_id) SELECT id, name, tenant_id FROM entity2 WHERE tenant_id = 1");
+            "INSERT INTO entity (id, name, tenant_id) SELECT id, name, tenant_id FROM entity2 WHERE entity2.tenant_id = 1");
 
         assertSql("insert into entity (id,name) select * from entity2 e2",
             "INSERT INTO entity (id, name, tenant_id) SELECT * FROM entity2 e2 WHERE e2.tenant_id = 1");
@@ -69,30 +69,33 @@ class TenantLineInnerInterceptorTest {
     @Test
     void delete() {
         assertSql("delete from entity where id = ?",
-            "DELETE FROM entity WHERE tenant_id = 1 AND id = ?");
+            "DELETE FROM entity WHERE entity.tenant_id = 1 AND id = ?");
     }
 
     @Test
     void update() {
         assertSql("update entity set name = ? where id = ?",
-            "UPDATE entity SET name = ? WHERE tenant_id = 1 AND id = ?");
+            "UPDATE entity SET name = ? WHERE entity.tenant_id = 1 AND id = ?");
     }
 
     @Test
     void selectSingle() {
         // 单表
         assertSql("select * from entity where id = ?",
-            "SELECT * FROM entity WHERE id = ? AND tenant_id = 1");
+            "SELECT * FROM entity WHERE id = ? AND entity.tenant_id = 1");
 
         assertSql("select * from entity where id = ? or name = ?",
-            "SELECT * FROM entity WHERE (id = ? OR name = ?) AND tenant_id = 1");
+            "SELECT * FROM entity WHERE (id = ? OR name = ?) AND entity.tenant_id = 1");
 
         assertSql("SELECT * FROM entity WHERE (id = ? OR name = ?)",
-            "SELECT * FROM entity WHERE (id = ? OR name = ?) AND tenant_id = 1");
+            "SELECT * FROM entity WHERE (id = ? OR name = ?) AND entity.tenant_id = 1");
 
         /* not */
         assertSql("SELECT * FROM entity WHERE not (id = ? OR name = ?)",
-            "SELECT * FROM entity WHERE NOT (id = ? OR name = ?) AND tenant_id = 1");
+            "SELECT * FROM entity WHERE NOT (id = ? OR name = ?) AND entity.tenant_id = 1");
+
+        assertSql("SELECT * FROM entity u WHERE not (u.id = ? OR u.name = ?)",
+            "SELECT * FROM entity u WHERE NOT (u.id = ? OR u.name = ?) AND u.tenant_id = 1");
     }
 
     @Test
@@ -414,11 +417,20 @@ class TenantLineInnerInterceptorTest {
 
     }
 
+    @Test
+    void selectJoin() {
+        // join
+        assertSql("SELECT * FROM entity e join entity1 e1 on e1.id = e.id WHERE e.id = ? OR e.name = ?",
+            "SELECT * FROM entity e JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1");
+
+        assertSql("SELECT * FROM entity e join entity1 e1 on e1.id = e.id WHERE (e.id = ? OR e.name = ?)",
+            "SELECT * FROM entity e JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1");
+    }
 
     @Test
     void selectWithAs() {
         assertSql("with with_as_A as (select * from entity) select * from with_as_A",
-            "WITH with_as_A AS (SELECT * FROM entity WHERE tenant_id = 1) SELECT * FROM with_as_A");
+            "WITH with_as_A AS (SELECT * FROM entity WHERE entity.tenant_id = 1) SELECT * FROM with_as_A");
     }
 
 
@@ -432,7 +444,7 @@ class TenantLineInnerInterceptorTest {
     void test6() {
         // 不显式指定 JOIN 类型时 JOIN 右侧表无法识进行拼接条件(在未改动之前就已经有这个问题)
         assertSql("select u.username from sys_user u join sys_user_role r on u.id=r.user_id",
-            "SELECT u.username FROM sys_user u JOIN sys_user_role r ON u.id = r.user_id WHERE u.tenant_id = 1");
+            "SELECT u.username FROM sys_user u JOIN sys_user_role r ON u.id = r.user_id AND r.tenant_id = 1 WHERE u.tenant_id = 1");
     }
 
     @Test

+ 7 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/H2UserTest.java

@@ -558,6 +558,13 @@ class H2UserTest extends BaseTest {
             .orderByDesc(H2User::getTestId)).getPages() > 0);
     }
 
+    @Test
+    void testPageNegativeSize() {
+        Page page = Page.of(1, -1);
+        userService.lambdaQuery().page(page);
+        Assertions.assertEquals(page.getTotal(), 0);
+    }
+
     @Test
     void testDeleteByFill() {
         H2User h2User = new H2User(3L, "test");