ソースを参照

feat: 轻量级分表插件

zengzhihong 4 年 前
コミット
9775170259
17 ファイル変更904 行追加4 行削除
  1. 5 0
      mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/InterceptorIgnore.java
  2. 6 0
      mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/plugins/InterceptorIgnoreHelper.java
  3. 12 4
      mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/MybatisPlusInterceptor.java
  4. 20 0
      mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/handler/sharding/ShardingNode.java
  5. 311 0
      mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/handler/sharding/ShardingNodeExtractor.java
  6. 20 0
      mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/handler/sharding/ShardingProcessor.java
  7. 13 0
      mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/handler/sharding/ShardingRuleEnum.java
  8. 63 0
      mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/handler/sharding/ShardingStrategy.java
  9. 11 0
      mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/InnerInterceptor.java
  10. 139 0
      mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/ShardingInnerInterceptor.java
  11. 89 0
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/sharding/ShardingConfig.java
  12. 127 0
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/sharding/ShardingTest.java
  13. 15 0
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/sharding/mapper/ShardingOrderMapper.java
  14. 24 0
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/sharding/model/ShardingOrder.java
  15. 15 0
      mybatis-plus/src/test/resources/h2/spring-sharding-h2.xml
  16. 11 0
      mybatis-plus/src/test/resources/sharding/ShardingOrderMapper.xml
  17. 23 0
      mybatis-plus/src/test/resources/sharding/sharding_order.ddl.sql

+ 5 - 0
mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/InterceptorIgnore.java

@@ -63,6 +63,11 @@ public @interface InterceptorIgnore {
      */
     String dataPermission() default "1";
 
+    /**
+     * 分表 {@link com.baomidou.mybatisplus.extension.plugins.inner.ShardingInnerInterceptor}
+     */
+    String sharding() default "";
+
     /**
      * 其他的
      * <p>

+ 6 - 0
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/plugins/InterceptorIgnoreHelper.java

@@ -99,6 +99,10 @@ public abstract class InterceptorIgnoreHelper {
         return willIgnore(id, InterceptorIgnoreCache::getDataPermission);
     }
 
+    public static boolean willIgnoreSharding(String id) {
+        return willIgnore(id, InterceptorIgnoreCache::getSharding);
+    }
+
     public static boolean willIgnoreOthersByKey(String id, String key) {
         return willIgnore(id, i -> CollectionUtils.isNotEmpty(i.getOthers()) && i.getOthers().getOrDefault(key, false));
     }
@@ -133,6 +137,7 @@ public abstract class InterceptorIgnoreHelper {
             .blockAttack(getBoolean("blockAttack", name, ignore.blockAttack()))
             .illegalSql(getBoolean("illegalSql", name, ignore.illegalSql()))
             .dataPermission(getBoolean("dataPermission", name, ignore.dataPermission()))
+            .sharding(getBoolean("sharding", name, ignore.sharding()))
             .others(getOthers(name, ignore.others()))
             .build();
     }
@@ -208,6 +213,7 @@ public abstract class InterceptorIgnoreHelper {
         private Boolean blockAttack;
         private Boolean illegalSql;
         private Boolean dataPermission;
+        private Boolean sharding;
         private Map<String, Boolean> others;
     }
 }

+ 12 - 4
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/MybatisPlusInterceptor.java

@@ -41,6 +41,7 @@ import java.util.*;
 @Intercepts(
     {
         @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}),
+        @Signature(type = StatementHandler.class, method = "getBoundSql", args = {}),
         @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
         @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
         @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
@@ -89,10 +90,17 @@ public class MybatisPlusInterceptor implements Interceptor {
         } else {
             // StatementHandler
             final StatementHandler sh = (StatementHandler) target;
-            Connection connections = (Connection) args[0];
-            Integer transactionTimeout = (Integer) args[1];
-            for (InnerInterceptor innerInterceptor : interceptors) {
-                innerInterceptor.beforePrepare(sh, connections, transactionTimeout);
+            // 目前只有StatementHandler.getBoundSql方法args才为null
+            if (null == args) {
+                for (InnerInterceptor innerInterceptor : interceptors) {
+                    innerInterceptor.beforeGetBoundSql(sh);
+                }
+            } else {
+                Connection connections = (Connection) args[0];
+                Integer transactionTimeout = (Integer) args[1];
+                for (InnerInterceptor innerInterceptor : interceptors) {
+                    innerInterceptor.beforePrepare(sh, connections, transactionTimeout);
+                }
             }
         }
         return invocation.proceed();

+ 20 - 0
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/handler/sharding/ShardingNode.java

@@ -0,0 +1,20 @@
+package com.baomidou.mybatisplus.extension.plugins.handler.sharding;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+
+/**
+ * @author zengzhihong
+ */
+@AllArgsConstructor
+@Getter
+@Setter
+public class ShardingNode<T, C> {
+
+    private T node;
+
+    private List<C> list;
+}

+ 311 - 0
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/handler/sharding/ShardingNodeExtractor.java

@@ -0,0 +1,311 @@
+package com.baomidou.mybatisplus.extension.plugins.handler.sharding;
+
+import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
+import lombok.Getter;
+import net.sf.jsqlparser.expression.AllComparisonExpression;
+import net.sf.jsqlparser.expression.AnyComparisonExpression;
+import net.sf.jsqlparser.expression.CastExpression;
+import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.expression.JdbcNamedParameter;
+import net.sf.jsqlparser.expression.JdbcParameter;
+import net.sf.jsqlparser.expression.Parenthesis;
+import net.sf.jsqlparser.expression.operators.arithmetic.Addition;
+import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseAnd;
+import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseOr;
+import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseXor;
+import net.sf.jsqlparser.expression.operators.arithmetic.Concat;
+import net.sf.jsqlparser.expression.operators.arithmetic.Division;
+import net.sf.jsqlparser.expression.operators.arithmetic.Subtraction;
+import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
+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.insert.Insert;
+import net.sf.jsqlparser.statement.select.Join;
+import net.sf.jsqlparser.statement.select.PlainSelect;
+import net.sf.jsqlparser.statement.select.SubJoin;
+import net.sf.jsqlparser.statement.select.SubSelect;
+import net.sf.jsqlparser.statement.update.Update;
+import net.sf.jsqlparser.util.TablesNamesFinder;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ShardingNodeExtractor extends TablesNamesFinder {
+
+    @Getter
+    private final List<ShardingNode<Table, ShardingNode<String, Integer>>> nodes;
+
+    private Column currentColumn;
+
+    public ShardingNodeExtractor(Statement statement) {
+        this.nodes = new ArrayList<>();
+        super.getTableList(statement);
+    }
+
+
+    @Override
+    public void visit(Table table) {
+        nodes.add(new ShardingNode<>(table, new ArrayList<>()));
+    }
+
+
+    @Override
+    public void visit(Column column) {
+        this.currentColumn = column;
+        final ShardingNode<Table, ShardingNode<String, Integer>> tableNode =
+            obtainTableNode(column);
+        // SQL正确的前提下 tableNode一定不为null
+        if (null == tableNode) {
+            throw ExceptionUtils.mpe("please determine the alias on sql");
+        }
+        final ShardingNode<String, Integer> columnNode =
+            tableNode.getList().stream().filter(i -> i.getNode().equals(column.getColumnName())).findFirst().orElse(null);
+        if (null == columnNode) {
+            tableNode.getList().add(new ShardingNode<>(column.getColumnName(), new ArrayList<>()));
+        }
+    }
+
+    @Override
+    public void visit(Insert insert) {
+        visit(insert.getTable());
+        if (insert.getColumns() != null && insert.getItemsList() != null && insert.getItemsList() instanceof ExpressionList) {
+            final ExpressionList itemsList = (ExpressionList) insert.getItemsList();
+            if (null != itemsList.getExpressions() && insert.getColumns().size() == itemsList.getExpressions().size()) {
+                for (int i = 0; i < insert.getColumns().size(); i++) {
+                    final Expression expression = itemsList.getExpressions().get(i);
+                    if (!(expression instanceof JdbcParameter)) {
+                        continue;
+                    }
+                    visit(insert.getColumns().get(i));
+                    visit((JdbcParameter)expression);
+                }
+            }
+        }
+        if (insert.getSelect() != null) {
+            visit(insert.getSelect());
+        }
+    }
+
+    @Override
+    public void visit(Update update) {
+        visit(update.getTable());
+        if (update.getStartJoins() != null) {
+            for (Join join : update.getStartJoins()) {
+                join.getRightItem().accept(this);
+            }
+        }
+        /*if (update.getExpressions() != null) {
+            for (Expression expression : update.getExpressions()) {
+                expression.accept(this);
+            }
+        }*/
+
+        if (update.getFromItem() != null) {
+            update.getFromItem().accept(this);
+        }
+
+        if (update.getJoins() != null) {
+            for (Join join : update.getJoins()) {
+                join.getRightItem().accept(this);
+            }
+        }
+
+        if (update.getWhere() != null) {
+            update.getWhere().accept(this);
+        }
+    }
+
+    @Override
+    public void visit(JdbcParameter jdbcParameter) {
+        final ShardingNode<Table, ShardingNode<String, Integer>> tableNode =
+            obtainTableNode(this.currentColumn);
+        if (null == tableNode) {
+            throw ExceptionUtils.mpe("please determine the alias on sql");
+        }
+        final ShardingNode<String, Integer> columnNode =
+            tableNode.getList().stream().filter(i -> i.getNode().equals(this.currentColumn.getColumnName())).findFirst().orElse(null);
+        if (null == columnNode) {
+            throw ExceptionUtils.mpe("please determine the alias on sql");
+        }
+        columnNode.getList().add(jdbcParameter.getIndex());
+    }
+
+    @Override
+    public void visit(ExpressionList expressionList) {
+        for (Expression expression : expressionList.getExpressions()) {
+            expression.accept(this);
+        }
+    }
+
+    private ShardingNode<Table, ShardingNode<String, Integer>> obtainTableNode(Column column) {
+        return null == column || null == column.getTable() || null == column.getTable().getName() ? nodes.get(0) :
+            nodes.stream().filter(i -> i.getNode().getAlias().getName().equals(column.getTable().getName())).findFirst().orElse(null);
+    }
+
+
+    @Override
+    public void visit(JdbcNamedParameter jdbcNamedParameter) {
+
+    }
+
+
+    @Override
+    public void visit(Parenthesis parenthesis) {
+        parenthesis.getExpression().accept(this);
+    }
+
+
+    @Override
+    public void visit(Addition addition) {
+        visitBinaryExpression(addition);
+    }
+
+    @Override
+    public void visit(Division division) {
+        visitBinaryExpression(division);
+    }
+
+
+    @Override
+    public void visit(Subtraction subtraction) {
+        visitBinaryExpression(subtraction);
+    }
+
+    @Override
+    public void visit(AndExpression andExpression) {
+        visitBinaryExpression(andExpression);
+    }
+
+    @Override
+    public void visit(OrExpression orExpression) {
+        visitBinaryExpression(orExpression);
+    }
+
+    @Override
+    public void visit(Between between) {
+        between.getLeftExpression().accept(this);
+        between.getBetweenExpressionStart().accept(this);
+        between.getBetweenExpressionEnd().accept(this);
+    }
+
+    @Override
+    public void visit(EqualsTo equalsTo) {
+        visitBinaryExpression(equalsTo);
+    }
+
+    @Override
+    public void visit(GreaterThan greaterThan) {
+        visitBinaryExpression(greaterThan);
+    }
+
+    @Override
+    public void visit(GreaterThanEquals greaterThanEquals) {
+        visitBinaryExpression(greaterThanEquals);
+    }
+
+    @Override
+    public void visit(InExpression inExpression) {
+        inExpression.getLeftExpression().accept(this);
+        if (null != inExpression.getLeftItemsList()) {
+            inExpression.getLeftItemsList().accept(this);
+        }
+        if (null != inExpression.getRightItemsList()) {
+            inExpression.getRightItemsList().accept(this);
+        }
+    }
+
+    @Override
+    public void visit(LikeExpression likeExpression) {
+        visitBinaryExpression(likeExpression);
+    }
+
+    @Override
+    public void visit(MinorThan minorThan) {
+        visitBinaryExpression(minorThan);
+    }
+
+    @Override
+    public void visit(MinorThanEquals minorThanEquals) {
+        visitBinaryExpression(minorThanEquals);
+    }
+
+    @Override
+    public void visit(NotEqualsTo notEqualsTo) {
+        visitBinaryExpression(notEqualsTo);
+    }
+
+    @Override
+    public void visit(ExistsExpression existsExpression) {
+        existsExpression.getRightExpression().accept(this);
+    }
+
+    @Override
+    public void visit(AllComparisonExpression allComparisonExpression) {
+        allComparisonExpression.getSubSelect().getSelectBody().accept(this);
+    }
+
+    @Override
+    public void visit(AnyComparisonExpression anyComparisonExpression) {
+        anyComparisonExpression.getSubSelect().getSelectBody().accept(this);
+    }
+
+    @Override
+    public void visit(Concat concat) {
+        visitBinaryExpression(concat);
+    }
+
+    @Override
+    public void visit(Matches matches) {
+        visitBinaryExpression(matches);
+    }
+
+    @Override
+    public void visit(BitwiseAnd bitwiseAnd) {
+        visitBinaryExpression(bitwiseAnd);
+    }
+
+    @Override
+    public void visit(BitwiseOr bitwiseOr) {
+        visitBinaryExpression(bitwiseOr);
+    }
+
+    @Override
+    public void visit(BitwiseXor bitwiseXor) {
+        visitBinaryExpression(bitwiseXor);
+    }
+
+    @Override
+    public void visit(CastExpression cast) {
+    }
+
+    @Override
+    public void visit(SubSelect subSelect) {
+        subSelect.getSelectBody().accept(this);
+    }
+
+    @Override
+    public void visit(SubJoin subjoin) {
+        subjoin.getLeft().accept(this);
+        for (Join join : subjoin.getJoinList()) {
+            join.getRightItem().accept(this);
+        }
+    }
+
+
+    @Override
+    public void visit(PlainSelect plainSelect) {
+        plainSelect.getFromItem().accept(this);
+        if (plainSelect.getJoins() != null) {
+            for (Join join : plainSelect.getJoins()) {
+                join.getRightItem().accept(this);
+            }
+        }
+        if (plainSelect.getWhere() != null) {
+            plainSelect.getWhere().accept(this);
+        }
+    }
+
+}

+ 20 - 0
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/handler/sharding/ShardingProcessor.java

@@ -0,0 +1,20 @@
+package com.baomidou.mybatisplus.extension.plugins.handler.sharding;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author zengzhihong
+ */
+public interface ShardingProcessor {
+
+    /**
+     * 分表执行
+     *
+     * @param strategy       策略
+     * @param shardingValues 分片字段和字段值
+     * @return 真实表名
+     */
+    String doSharding(ShardingStrategy strategy, Map<String, List<Object>> shardingValues);
+
+}

+ 13 - 0
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/handler/sharding/ShardingRuleEnum.java

@@ -0,0 +1,13 @@
+package com.baomidou.mybatisplus.extension.plugins.handler.sharding;
+
+/**
+ * @author zengzhihong
+ */
+public enum ShardingRuleEnum {
+
+    /**
+     * = in 绝对定位
+     */
+    ABSOLUTE,
+    ;
+}

+ 63 - 0
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/handler/sharding/ShardingStrategy.java

@@ -0,0 +1,63 @@
+package com.baomidou.mybatisplus.extension.plugins.handler.sharding;
+
+import java.util.Arrays;
+import java.util.List;
+
+import com.baomidou.mybatisplus.core.toolkit.StringPool;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author zengzhihong
+ */
+@EqualsAndHashCode
+public class ShardingStrategy {
+
+    /**
+     * 逻辑表名 如:order_info
+     */
+    @Getter
+    @Setter
+    private String logicTable;
+
+    /**
+     * 分片字段 , 隔开 如: id,create_time
+     */
+    private String column;
+
+    /**
+     * 分片规则 {@link ShardingRuleEnum}
+     */
+    @Getter
+    @Setter
+    private ShardingRuleEnum rule;
+
+    @Getter
+    @Setter
+    private Class<? extends ShardingProcessor> processor;
+
+    private List<String> shardingColumnList;
+
+    public ShardingStrategy(String logicTable, String column, Class<? extends ShardingProcessor> processor) {
+        this(logicTable, column, ShardingRuleEnum.ABSOLUTE, processor);
+    }
+
+    public ShardingStrategy(String logicTable, String column, ShardingRuleEnum rule, Class<? extends ShardingProcessor> processor) {
+        this.logicTable = logicTable;
+        this.rule = rule;
+        this.processor = processor;
+        this.setColumn(column);
+    }
+
+    public void setColumn(String column) {
+        this.column = column;
+        this.shardingColumnList = Arrays.asList(this.column.split(StringPool.COMMA));
+    }
+
+    public boolean containsColumn(String column) {
+        return shardingColumnList.contains(column);
+    }
+
+}

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

@@ -20,6 +20,7 @@ import org.apache.ibatis.executor.Executor;
 import org.apache.ibatis.executor.statement.StatementHandler;
 import org.apache.ibatis.mapping.BoundSql;
 import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.session.Configuration;
 import org.apache.ibatis.session.ResultHandler;
 import org.apache.ibatis.session.RowBounds;
 
@@ -107,6 +108,16 @@ public interface InnerInterceptor {
         // do nothing
     }
 
+    /**
+     * 创建完StatementHandler生成proxy时传递
+     * {@link Configuration#newStatementHandler(org.apache.ibatis.executor.Executor, org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.mapping.BoundSql)}
+     *
+     * @param sh 原生对象 而非代理对象
+     */
+    default void beforeGetBoundSql(StatementHandler sh) {
+        // do nothing
+    }
+
     default void setProperties(Properties properties) {
         // do nothing
     }

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

@@ -0,0 +1,139 @@
+package com.baomidou.mybatisplus.extension.plugins.inner;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.ibatis.executor.statement.StatementHandler;
+import org.apache.ibatis.mapping.ParameterMapping;
+import org.apache.ibatis.mapping.ParameterMode;
+import org.apache.ibatis.reflection.MetaObject;
+
+import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
+import com.baomidou.mybatisplus.core.toolkit.ClassUtils;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
+import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
+import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
+import com.baomidou.mybatisplus.extension.plugins.handler.sharding.ShardingNode;
+import com.baomidou.mybatisplus.extension.plugins.handler.sharding.ShardingNodeExtractor;
+import com.baomidou.mybatisplus.extension.plugins.handler.sharding.ShardingProcessor;
+import com.baomidou.mybatisplus.extension.plugins.handler.sharding.ShardingStrategy;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+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.Select;
+import net.sf.jsqlparser.statement.update.Update;
+
+/**
+ * @author zengzhihong
+ */
+public class ShardingInnerInterceptor extends JsqlParserSupport implements InnerInterceptor {
+
+    private final Map<String, ShardingStrategyProcessor> shardingMap;
+
+
+    public ShardingInnerInterceptor(ShardingStrategy... shardingStrategies) {
+        shardingMap =
+                Arrays.stream(shardingStrategies).collect(Collectors.toMap(ShardingStrategy::getLogicTable, i -> new ShardingStrategyProcessor(i, ClassUtils.newInstance(i.getProcessor()))));
+    }
+
+    @AllArgsConstructor
+    @Getter
+    static class ShardingStrategyProcessor {
+
+        private final ShardingStrategy strategy;
+        private final ShardingProcessor processor;
+    }
+
+    @Override
+    public void beforeGetBoundSql(StatementHandler sh) {
+        PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
+        if (InterceptorIgnoreHelper.willIgnoreSharding(mpSh.mappedStatement().getId())) {
+            return;
+        }
+        PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
+        mpBs.sql(parserMulti(mpBs.sql(), mpSh));
+    }
+
+    @Override
+    protected void processSelect(Select select, int index, String sql, Object obj) {
+        process(select, (PluginUtils.MPStatementHandler) obj);
+    }
+
+    @Override
+    protected void processInsert(Insert insert, int index, String sql, Object obj) {
+        process(insert, (PluginUtils.MPStatementHandler) obj);
+    }
+
+    @Override
+    protected void processUpdate(Update update, int index, String sql, Object obj) {
+        process(update, (PluginUtils.MPStatementHandler) obj);
+    }
+
+    @Override
+    protected void processDelete(Delete delete, int index, String sql, Object obj) {
+        process(delete, (PluginUtils.MPStatementHandler) obj);
+    }
+
+    private void process(Statement statement, PluginUtils.MPStatementHandler mpSh) {
+        final ShardingNodeExtractor shardingNodeExtractor = new ShardingNodeExtractor(statement);
+        if (CollectionUtils.isEmpty(shardingNodeExtractor.getNodes())) {
+            return;
+        }
+        final List<Object> parameterValues = handleParameter(mpSh);
+        for (ShardingNode<Table, ShardingNode<String, Integer>> tableNode : shardingNodeExtractor.getNodes()) {
+            final ShardingStrategyProcessor strategyProcessor = shardingMap.get(tableNode.getNode().getName());
+            if (null == strategyProcessor) {
+                continue;
+            }
+            Map<String, List<Object>> shardingValues = new HashMap<>(tableNode.getList().size());
+            for (ShardingNode<String, Integer> columnNode : tableNode.getList()) {
+                if (CollectionUtils.isEmpty(columnNode.getList()) || !strategyProcessor.getStrategy().containsColumn(columnNode.getNode())) {
+                    continue;
+                }
+                shardingValues.put(columnNode.getNode(),
+                        columnNode.getList().stream().map(i -> parameterValues.get(i - 1)).collect(Collectors.toList()));
+            }
+            if (CollectionUtils.isEmpty(shardingValues)) {
+                throw ExceptionUtils.mpe("no fragment sharding column found");
+            }
+            tableNode.getNode().setName(strategyProcessor.getProcessor().doSharding(strategyProcessor.getStrategy(), shardingValues));
+        }
+    }
+
+    private List<Object> handleParameter(PluginUtils.MPStatementHandler mpSh) {
+        List<Object> values = new ArrayList<>();
+        final Object parameterObject = mpSh.boundSql().getParameterObject();
+        List<ParameterMapping> parameterMappings = mpSh.boundSql().getParameterMappings();
+        if (parameterMappings != null) {
+            for (ParameterMapping parameterMapping : parameterMappings) {
+                if (parameterMapping.getMode() != ParameterMode.OUT) {
+                    Object value;
+                    String propertyName = parameterMapping.getProperty();
+                    if (mpSh.boundSql().hasAdditionalParameter(propertyName)) { // issue #448 ask first for
+                        // additional params
+                        value = mpSh.boundSql().getAdditionalParameter(propertyName);
+                    } else if (parameterObject == null) {
+                        value = null;
+                    } else if (mpSh.configuration().getTypeHandlerRegistry().hasTypeHandler(parameterObject.getClass())) {
+                        value = parameterObject;
+                    } else {
+                        MetaObject metaObject = mpSh.configuration().newMetaObject(parameterObject);
+                        value = metaObject.getValue(propertyName);
+                    }
+                    values.add(value);
+                }
+            }
+        }
+        return values;
+    }
+
+}

+ 89 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/sharding/ShardingConfig.java

@@ -0,0 +1,89 @@
+package com.baomidou.mybatisplus.test.h2.sharding;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.sql.DataSource;
+
+import org.apache.ibatis.session.ExecutorType;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.apache.ibatis.type.EnumOrdinalTypeHandler;
+import org.apache.ibatis.type.JdbcType;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.core.MybatisConfiguration;
+import com.baomidou.mybatisplus.core.config.GlobalConfig;
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.handler.sharding.ShardingProcessor;
+import com.baomidou.mybatisplus.extension.plugins.handler.sharding.ShardingRuleEnum;
+import com.baomidou.mybatisplus.extension.plugins.handler.sharding.ShardingStrategy;
+import com.baomidou.mybatisplus.extension.plugins.inner.ShardingInnerInterceptor;
+import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
+
+/**
+ * @author zengzhihong
+ */
+@Configuration
+@MapperScan("com.baomidou.mybatisplus.test.h2.sharding.mapper")
+public class ShardingConfig {
+
+
+    @Bean("mybatisSqlSession")
+    public SqlSessionFactory sqlSessionFactory(DataSource dataSource, ResourceLoader resourceLoader,
+                                               GlobalConfig globalConfig) throws Exception {
+        MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
+        sqlSessionFactory.setDataSource(dataSource);
+//        sqlSessionFactory.setConfigLocation(resourceLoader.getResource("classpath:mybatis-config-object-factory
+//        .xml"));
+        MybatisConfiguration configuration = new MybatisConfiguration();
+//        configuration.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class);
+//        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
+        configuration.setJdbcTypeForNull(JdbcType.NULL);
+        /*
+         * 下划线转驼峰开启
+         */
+        configuration.setMapUnderscoreToCamelCase(true);
+        configuration.setDefaultExecutorType(ExecutorType.REUSE);
+        configuration.setDefaultEnumTypeHandler(EnumOrdinalTypeHandler.class);  //默认枚举处理
+        sqlSessionFactory.setConfiguration(configuration);
+        sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
+                .getResources("classpath:/sharding/*.xml"));
+        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
+
+        final ShardingStrategy orderShardingHashStrategy = new ShardingStrategy("sharding_order", "order_id", ShardingRuleEnum.ABSOLUTE, OrderShardingProcessor.class);
+
+        interceptor.addInnerInterceptor(new ShardingInnerInterceptor(orderShardingHashStrategy));
+        sqlSessionFactory.setPlugins(interceptor);
+        return sqlSessionFactory.getObject();
+    }
+
+    static class OrderShardingProcessor implements ShardingProcessor {
+
+        @Override
+        public String doSharding(ShardingStrategy strategy, Map<String, List<Object>> shardingValues) {
+            final List<Object> values = shardingValues.get("order_id");
+            // value是一个集合 比如 in查询
+            // 不管有几个value 此处最终return一个真实表名
+            // 未携带分配字段会报错 所以shardingValues 一定是 notEmpty
+            return "sharding_order_" + String.format("%02d", ((Long) values.get(0) % 3 + 1));
+        }
+    }
+
+
+    @Bean
+    public GlobalConfig globalConfiguration() {
+        GlobalConfig conf = new GlobalConfig();
+        conf.setEnableSqlRunner(true)
+                .setDbConfig(new GlobalConfig.DbConfig()
+                        .setLogicDeleteValue("1")
+                        .setLogicNotDeleteValue("0")
+                        .setIdType(IdType.ID_WORKER));
+        return conf;
+    }
+
+}

+ 127 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/sharding/ShardingTest.java

@@ -0,0 +1,127 @@
+package com.baomidou.mybatisplus.test.h2.sharding;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ibatis.logging.Log;
+import org.apache.ibatis.logging.LogFactory;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import com.baomidou.mybatisplus.core.enums.SqlMethod;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
+import com.baomidou.mybatisplus.test.h2.sharding.mapper.ShardingOrderMapper;
+import com.baomidou.mybatisplus.test.h2.sharding.model.ShardingOrder;
+
+/**
+ * @author zengzhihong
+ */
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+@ExtendWith(SpringExtension.class)
+@ContextConfiguration(locations = {"classpath:h2/spring-sharding-h2.xml"})
+public class ShardingTest {
+
+    private Log log = LogFactory.getLog(getClass());
+
+    @Autowired
+    ShardingOrderMapper shardingOrderMapper;
+
+    private final LocalDateTime date1 = LocalDateTime.of(2021, 1, 1, 0, 0, 0);
+    private final LocalDateTime date2 = LocalDateTime.of(2021, 2, 1, 0, 0, 0);
+    private final LocalDateTime date3 = LocalDateTime.of(2021, 3, 1, 0, 0, 0);
+
+    @Test
+    void insertTest() {
+        ShardingOrder shardingOrder1 =
+                new ShardingOrder().setOrderId(1L).setSubject("test1").setCreateTime(date1);
+        shardingOrderMapper.insert(shardingOrder1);
+        shardingOrderMapper.insert1(2L);
+    }
+
+    @Test
+    void insertBatchTest() {
+        ShardingOrder shardingOrder1 =
+                new ShardingOrder().setOrderId(1L).setSubject("test1").setCreateTime(date1);
+        ShardingOrder shardingOrder2 =
+                new ShardingOrder().setOrderId(2L).setSubject("test2").setCreateTime(date2);
+        ShardingOrder shardingOrder3 =
+                new ShardingOrder().setOrderId(3L).setSubject("test3").setCreateTime(date3);
+        List<ShardingOrder> list = new ArrayList<>();
+        list.add(shardingOrder1);
+        list.add(shardingOrder2);
+        list.add(shardingOrder3);
+        String sqlStatement = SqlHelper.getSqlStatement(ShardingOrderMapper.class, SqlMethod.INSERT_ONE);
+        SqlHelper.executeBatch(ShardingOrder.class, log, list, 1000,
+                (sqlSession, entity) -> sqlSession.insert(sqlStatement, entity));
+
+    }
+
+    @Test
+    void selectTest() {
+        insertBatchTest();
+        shardingOrderMapper.selectPage(new Page<>(),
+                Wrappers.<ShardingOrder>lambdaQuery().eq(ShardingOrder::getOrderId, 1L));
+
+        Map<String, Object> params = new HashMap<>();
+        params.put("order_id", 1L);
+        params.put("create_time", date1);
+        shardingOrderMapper.selectByMap(params);
+
+        shardingOrderMapper.selectById(1L);
+
+        shardingOrderMapper.selectList(Wrappers.<ShardingOrder>lambdaQuery().eq(ShardingOrder::getCreateTime, date1)
+                .in(ShardingOrder::getOrderId, Arrays.asList(1L, 2L)));
+
+        shardingOrderMapper.selectMaps(Wrappers.<ShardingOrder>lambdaQuery().in(ShardingOrder::getOrderId, 3L
+        ).eq(ShardingOrder::getCreateTime,
+                date1));
+
+        shardingOrderMapper.selectBatchIds(Arrays.asList(1L, 2L));
+
+        final Integer count =
+                shardingOrderMapper.selectCount(Wrappers.<ShardingOrder>lambdaQuery().eq(ShardingOrder::getOrderId, 1L
+                ).eq(ShardingOrder::getCreateTime,
+                        date1));
+
+        shardingOrderMapper.selectMapsPage(new Page<>(),
+                Wrappers.<ShardingOrder>lambdaQuery().eq(ShardingOrder::getOrderId, 1L));
+
+        shardingOrderMapper.selectOne(Wrappers.<ShardingOrder>lambdaQuery().eq(ShardingOrder::getOrderId, 1L));
+
+        shardingOrderMapper.selectPage(new Page<>(),
+                Wrappers.<ShardingOrder>lambdaQuery().eq(ShardingOrder::getOrderId, 1L));
+
+    }
+
+
+    @Test
+    void updateTest() {
+        ShardingOrder order = new ShardingOrder();
+        order.setOrderId(1L);
+        order.setSubject("66");
+        shardingOrderMapper.update(order, Wrappers.<ShardingOrder>lambdaQuery().eq(ShardingOrder::getOrderId, 1L));
+        shardingOrderMapper.updateById(order);
+    }
+
+    @Test
+    void deleteTest() {
+        shardingOrderMapper.delete(Wrappers.<ShardingOrder>lambdaQuery().eq(ShardingOrder::getOrderId, 1L));
+        shardingOrderMapper.deleteById(1L);
+        shardingOrderMapper.deleteBatchIds(Arrays.asList(1L, 2L));
+        Map<String, Object> params = new HashMap<>();
+        params.put("order_id", 1L);
+        shardingOrderMapper.deleteByMap(params);
+    }
+
+}

+ 15 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/sharding/mapper/ShardingOrderMapper.java

@@ -0,0 +1,15 @@
+package com.baomidou.mybatisplus.test.h2.sharding.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.test.h2.sharding.model.ShardingOrder;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * @author zengzhihong
+ */
+public interface ShardingOrderMapper extends BaseMapper<ShardingOrder> {
+
+    int insert1(@Param("orderId") Long orderId);
+
+
+}

+ 24 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/sharding/model/ShardingOrder.java

@@ -0,0 +1,24 @@
+package com.baomidou.mybatisplus.test.h2.sharding.model;
+
+import java.time.LocalDateTime;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+/**
+ * @author miemie
+ * @since 2020-06-24
+ */
+@Data
+@Accessors(chain = true)
+public class ShardingOrder {
+
+    @TableId
+    private Long orderId;
+
+    private String subject;
+
+    private LocalDateTime createTime;
+}

+ 15 - 0
mybatis-plus/src/test/resources/h2/spring-sharding-h2.xml

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xmlns="http://www.springframework.org/schema/beans"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
+           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
+
+    <context:component-scan base-package="com.baomidou.mybatisplus.test.h2.sharding"/>
+
+    <bean class="com.baomidou.mybatisplus.test.h2.sharding.ShardingConfig"/>
+
+    <bean class="com.baomidou.mybatisplus.test.h2.config.DBConfig">
+        <property name="locationPattern" value="classpath:/sharding/*.sql"/>
+    </bean>
+</beans>

+ 11 - 0
mybatis-plus/src/test/resources/sharding/ShardingOrderMapper.xml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="com.baomidou.mybatisplus.test.h2.sharding.mapper.ShardingOrderMapper">
+    <!-- 一定要携带分片字段 -->
+    <insert id="insert1">
+        INSERT INTO sharding_order (order_id,subject,create_time)
+        VALUES (#{orderId}, '2', '2021-01-01 00:00:00')
+    </insert>
+
+
+</mapper>

+ 23 - 0
mybatis-plus/src/test/resources/sharding/sharding_order.ddl.sql

@@ -0,0 +1,23 @@
+CREATE TABLE IF NOT EXISTS sharding_order_01
+(
+    order_id     BIGINT(20)  NOT NULL AUTO_INCREMENT,
+    subject   VARCHAR(30) NULL DEFAULT NULL,
+    create_time  DATETIME      NULL,
+    PRIMARY KEY (order_id)
+);
+
+CREATE TABLE IF NOT EXISTS sharding_order_02
+(
+    order_id     BIGINT(20)  NOT NULL AUTO_INCREMENT,
+    subject   VARCHAR(30) NULL DEFAULT NULL,
+    create_time  DATETIME      NULL,
+    PRIMARY KEY (order_id)
+);
+
+CREATE TABLE IF NOT EXISTS sharding_order_03
+(
+    order_id     BIGINT(20)  NOT NULL AUTO_INCREMENT,
+    subject   VARCHAR(30) NULL DEFAULT NULL,
+    create_time  DATETIME      NULL,
+    PRIMARY KEY (order_id)
+);