miemie 5 년 전
부모
커밋
a18ee98e90

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

@@ -1,3 +1,18 @@
+/*
+ * Copyright (c) 2011-2020, baomidou (jobob@qq.com).
+ * <p>
+ * 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
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.plugins.inner;
 
 import com.baomidou.mybatisplus.core.toolkit.Assert;
@@ -13,8 +28,10 @@ import org.apache.ibatis.mapping.SqlCommandType;
 import java.sql.Connection;
 
 /**
- * @author miemie
- * @since 2020-06-22
+ * 攻击 SQL 阻断解析器
+ *
+ * @author hubin
+ * @since 2020-06-24
  */
 public class BlockAttackInnerInterceptor extends JsqlParserSupport implements InnerInterceptor {
 

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

@@ -1,3 +1,18 @@
+/*
+ * Copyright (c) 2011-2020, baomidou (jobob@qq.com).
+ * <p>
+ * 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
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.plugins.inner;
 
 import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
@@ -35,8 +50,31 @@ import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
 
 /**
- * @author miemie
- * @since 2020-06-22
+ * 由于开发人员水平参差不齐,即使订了开发规范很多人也不遵守
+ * <p>SQL是影响系统性能最重要的因素,所以拦截掉垃圾SQL语句</p>
+ * <br>
+ * <p>拦截SQL类型的场景</p>
+ * <p>1.必须使用到索引,包含left join连接字段,符合索引最左原则</p>
+ * <p>必须使用索引好处,</p>
+ * <p>1.1 如果因为动态SQL,bug导致update的where条件没有带上,全表更新上万条数据</p>
+ * <p>1.2 如果检查到使用了索引,SQL性能基本不会太差</p>
+ * <br>
+ * <p>2.SQL尽量单表执行,有查询left join的语句,必须在注释里面允许该SQL运行,否则会被拦截,有left join的语句,如果不能拆成单表执行的SQL,请leader商量在做</p>
+ * <p>https://gaoxianglong.github.io/shark</p>
+ * <p>SQL尽量单表执行的好处</p>
+ * <p>2.1 查询条件简单、易于开理解和维护;</p>
+ * <p>2.2 扩展性极强;(可为分库分表做准备)</p>
+ * <p>2.3 缓存利用率高;</p>
+ * <p>2.在字段上使用函数</p>
+ * <br>
+ * <p>3.where条件为空</p>
+ * <p>4.where条件使用了 !=</p>
+ * <p>5.where条件使用了 not 关键字</p>
+ * <p>6.where条件使用了 or 关键字</p>
+ * <p>7.where条件使用了 使用子查询</p>
+ *
+ * @author willenfoo
+ * @since 2020-06-24
  */
 public class IllegalSQLInnerInterceptor extends JsqlParserSupport implements InnerInterceptor {
 

+ 15 - 0
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/InnerInterceptor.java

@@ -1,3 +1,18 @@
+/*
+ * Copyright (c) 2011-2020, baomidou (jobob@qq.com).
+ * <p>
+ * 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
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.plugins.inner;
 
 import org.apache.ibatis.executor.Executor;

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

@@ -1,5 +1,21 @@
+/*
+ * Copyright (c) 2011-2020, baomidou (jobob@qq.com).
+ * <p>
+ * 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
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.plugins.inner;
 
+import com.baomidou.mybatisplus.annotation.Version;
 import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
@@ -8,6 +24,7 @@ import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
 import com.baomidou.mybatisplus.core.toolkit.Constants;
 import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
 import com.baomidou.mybatisplus.core.toolkit.StringPool;
+import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
 import org.apache.ibatis.executor.Executor;
 import org.apache.ibatis.mapping.MappedStatement;
 import org.apache.ibatis.mapping.SqlCommandType;
@@ -20,8 +37,24 @@ import java.util.Date;
 import java.util.Map;
 
 /**
- * @author miemie
- * @since 2020-06-21
+ * Optimistic Lock Light version
+ * <p>Intercept on {@link Executor}.update;</p>
+ * <p>Support version types: int/Integer, long/Long, java.util.Date, java.sql.Timestamp</p>
+ * <p>For extra types, please define a subclass and override {@code getUpdatedVersionVal}() method.</p>
+ * <br>
+ * <p>How to use?</p>
+ * <p>(1) Define an Entity and add {@link Version} annotation on one entity field.</p>
+ * <p>(2) Add {@link OptimisticLockerInterceptor} into mybatis plugin.</p>
+ * <br>
+ * <p>How to work?</p>
+ * <p>if update entity with version column=1:</p>
+ * <p>(1) no {@link OptimisticLockerInterceptor}:</p>
+ * <p>SQL: update tbl_test set name='abc' where id=100001;</p>
+ * <p>(2) add {@link OptimisticLockerInterceptor}:</p>
+ * <p>SQL: update tbl_test set name='abc',version=2 where id=100001 and version=1;</p>
+ *
+ * @author yuxiaobin
+ * @since 2020-06-24
  */
 @SuppressWarnings({"unchecked"})
 public class OptimisticLockerInnerInterceptor implements InnerInterceptor {

+ 95 - 9
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/PaginationInnerInterceptor.java

@@ -1,10 +1,23 @@
+/*
+ * Copyright (c) 2011-2020, baomidou (jobob@qq.com).
+ * <p>
+ * 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
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.plugins.inner;
 
 import com.baomidou.mybatisplus.annotation.DbType;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.metadata.OrderItem;
-import com.baomidou.mybatisplus.core.parser.ISqlParser;
-import com.baomidou.mybatisplus.core.parser.SqlInfo;
 import com.baomidou.mybatisplus.core.toolkit.*;
 import com.baomidou.mybatisplus.extension.plugins.pagination.DialectFactory;
 import com.baomidou.mybatisplus.extension.plugins.pagination.DialectModel;
@@ -14,8 +27,14 @@ import com.baomidou.mybatisplus.extension.toolkit.SqlParserUtils;
 import lombok.Data;
 import lombok.experimental.Accessors;
 import net.sf.jsqlparser.JSQLParserException;
+import net.sf.jsqlparser.expression.Alias;
+import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.expression.Function;
+import net.sf.jsqlparser.expression.LongValue;
+import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
 import net.sf.jsqlparser.parser.CCJSqlParserUtil;
 import net.sf.jsqlparser.schema.Column;
+import net.sf.jsqlparser.schema.Table;
 import net.sf.jsqlparser.statement.select.*;
 import org.apache.ibatis.cache.CacheKey;
 import org.apache.ibatis.executor.Executor;
@@ -25,7 +44,6 @@ import org.apache.ibatis.mapping.BoundSql;
 import org.apache.ibatis.mapping.MappedStatement;
 import org.apache.ibatis.mapping.ParameterMapping;
 import org.apache.ibatis.mapping.ResultMap;
-import org.apache.ibatis.reflection.SystemMetaObject;
 import org.apache.ibatis.session.Configuration;
 import org.apache.ibatis.session.ResultHandler;
 import org.apache.ibatis.session.RowBounds;
@@ -36,7 +54,11 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.Collectors;
 
 /**
- * @author miemie
+ * 分页拦截器
+ * <p>
+ * 默认对 left join 进行优化
+ *
+ * @author hubin
  * @since 2020-06-16
  */
 @Data
@@ -44,13 +66,21 @@ import java.util.stream.Collectors;
 @SuppressWarnings({"rawtypes"})
 public class PaginationInnerInterceptor implements InnerInterceptor {
 
+    private static final List<SelectItem> COUNT_SELECT_ITEM = Collections.singletonList(defaultCountSelectItem());
     protected static final Map<String, MappedStatement> countMsCache = new ConcurrentHashMap<>();
     protected final Log logger = LogFactory.getLog(this.getClass());
 
     /**
-     * COUNT SQL 解析
+     * 获取jsqlparser中count的SelectItem
      */
-    protected ISqlParser countSqlParser;
+    private static SelectItem defaultCountSelectItem() {
+        Function function = new Function();
+        ExpressionList expressionList = new ExpressionList(Collections.singletonList(new LongValue(1)));
+        function.setName("COUNT");
+        function.setParameters(expressionList);
+        return new SelectExpressionItem(function);
+    }
+
     /**
      * 溢出总页数后是否进行处理
      */
@@ -89,10 +119,9 @@ public class PaginationInnerInterceptor implements InnerInterceptor {
             countSql = countMs.getBoundSql(parameter);
         } else {
             countMs = buildAutoCountMappedStatement(ms);
-            SqlInfo countSqlInfo = SqlParserUtils.getOptimizeCountSql(page.optimizeCountSql(), countSqlParser,
-                boundSql.getSql(), SystemMetaObject.forObject(parameter));
+            String countSqlStr = autoCountSql(page.optimizeCountSql(), boundSql.getSql());
             PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);
-            countSql = new BoundSql(countMs.getConfiguration(), countSqlInfo.getSql(), mpBoundSql.parameterMappings(), parameter);
+            countSql = new BoundSql(countMs.getConfiguration(), countSqlStr, mpBoundSql.parameterMappings(), parameter);
             PluginUtils.setAdditionalParameter(countSql, mpBoundSql.additionalParameters());
         }
 
@@ -171,6 +200,63 @@ public class PaginationInnerInterceptor implements InnerInterceptor {
         });
     }
 
+    protected String autoCountSql(boolean optimizeCountSql, String sql) {
+        if (!optimizeCountSql) {
+            return SqlParserUtils.getOriginalCountSql(sql);
+        }
+        try {
+            Select select = (Select) CCJSqlParserUtil.parse(sql);
+            PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
+            Distinct distinct = plainSelect.getDistinct();
+            GroupByElement groupBy = plainSelect.getGroupBy();
+            List<OrderByElement> orderBy = plainSelect.getOrderByElements();
+
+            // 添加包含groupBy 不去除orderBy
+            if (null == groupBy && CollectionUtils.isNotEmpty(orderBy)) {
+                plainSelect.setOrderByElements(null);
+            }
+            //#95 Github, selectItems contains #{} ${}, which will be translated to ?, and it may be in a function: power(#{myInt},2)
+            for (SelectItem item : plainSelect.getSelectItems()) {
+                if (item.toString().contains(StringPool.QUESTION_MARK)) {
+                    return SqlParserUtils.getOriginalCountSql(select.toString());
+                }
+            }
+            // 包含 distinct、groupBy不优化
+            if (distinct != null || null != groupBy) {
+                return SqlParserUtils.getOriginalCountSql(select.toString());
+            }
+            // 包含 join 连表,进行判断是否移除 join 连表
+            List<Join> joins = plainSelect.getJoins();
+            if (CollectionUtils.isNotEmpty(joins)) {
+                boolean canRemoveJoin = true;
+                String whereS = Optional.ofNullable(plainSelect.getWhere()).map(Expression::toString).orElse(StringPool.EMPTY);
+                for (Join join : joins) {
+                    if (!join.isLeft()) {
+                        canRemoveJoin = false;
+                        break;
+                    }
+                    Table table = (Table) join.getRightItem();
+                    String str = Optional.ofNullable(table.getAlias()).map(Alias::getName).orElse(table.getName()) + StringPool.DOT;
+                    String onExpressionS = join.getOnExpression().toString();
+                    /* 如果 join 里包含 ?(代表有入参) 或者 where 条件里包含使用 join 的表的字段作条件,就不移除 join */
+                    if (onExpressionS.contains(StringPool.QUESTION_MARK) || whereS.contains(str)) {
+                        canRemoveJoin = false;
+                        break;
+                    }
+                }
+                if (canRemoveJoin) {
+                    plainSelect.setJoins(null);
+                }
+            }
+            // 优化 SQL
+            plainSelect.setSelectItems(COUNT_SELECT_ITEM);
+            return select.toString();
+        } catch (Throwable e) {
+            // 无法优化使用原 SQL
+            return SqlParserUtils.getOriginalCountSql(sql);
+        }
+    }
+
     /**
      * 查询SQL拼接Order By
      *

+ 16 - 1
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/TenantLineInnerInterceptor.java

@@ -1,3 +1,18 @@
+/*
+ * Copyright (c) 2011-2020, baomidou (jobob@qq.com).
+ * <p>
+ * 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
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.plugins.inner;
 
 import com.baomidou.mybatisplus.core.parser.SqlParserHelper;
@@ -32,7 +47,7 @@ import java.sql.SQLException;
 import java.util.List;
 
 /**
- * @author miemie
+ * @author hubin
  * @since 2020-06-20
  */
 @RequiredArgsConstructor