Quellcode durchsuchen

升级 1.4.0 新增 delete update 全表操作禁止执行拦截器

青苗 vor 9 Jahren
Ursprung
Commit
3abc1a40db

+ 149 - 0
mybatis-plus/src/main/java/com/baomidou/mybatisplus/plugins/SqlExplainInterceptor.java

@@ -0,0 +1,149 @@
+/**
+
+ * Copyright (c) 2011-2020, hubin (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.plugins;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.util.Properties;
+import java.util.logging.Logger;
+
+import org.apache.ibatis.builder.StaticSqlSource;
+import org.apache.ibatis.executor.Executor;
+import org.apache.ibatis.mapping.BoundSql;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.mapping.SqlCommandType;
+import org.apache.ibatis.plugin.Interceptor;
+import org.apache.ibatis.plugin.Intercepts;
+import org.apache.ibatis.plugin.Invocation;
+import org.apache.ibatis.plugin.Plugin;
+import org.apache.ibatis.plugin.Signature;
+import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
+import org.apache.ibatis.session.Configuration;
+
+import com.baomidou.mybatisplus.exceptions.MybatisPlusException;
+
+/**
+ * <p>
+ * SQL 执行分析拦截器
+ * </p>
+ * 
+ * @author hubin
+ * @Date 2016-08-16
+ */
+@Intercepts({@Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) })
+public class SqlExplainInterceptor implements Interceptor {
+	protected final Logger logger = Logger.getLogger("SqlExplainInterceptor");
+	
+	/**
+	 * 发现执行全表 delete update 语句是否停止执行
+	 */
+	private boolean stopProceed = false;
+	
+	public Object intercept(Invocation invocation) throws Throwable {
+		/**
+		 * 处理 DELETE UPDATE 语句
+		 */
+		MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
+		if (ms.getSqlCommandType() == SqlCommandType.DELETE || ms.getSqlCommandType() == SqlCommandType.UPDATE) {
+			Configuration configuration = ms.getConfiguration();
+			Object parameter = invocation.getArgs()[1];
+			BoundSql boundSql = ms.getBoundSql(parameter);
+			Executor exe = (Executor) invocation.getTarget();
+			Connection connection = exe.getTransaction().getConnection();
+
+			/**
+			 * 执行 SQL 分析
+			 */
+			sqlExplain(configuration, ms, boundSql, connection, parameter);
+		}
+		return invocation.proceed();
+	}
+
+	/**
+	 * <p>
+	 * 判断是否执行 SQL
+	 * </p>
+	 * @param configuration
+	 * @param mappedStatement
+	 * @param boundSql
+	 * @param connection
+	 * @param parameter
+	 * @return
+	 * @throws Exception
+	 */
+	protected void sqlExplain( Configuration configuration, MappedStatement mappedStatement, BoundSql boundSql,
+			Connection connection, Object parameter ) throws Exception {
+		PreparedStatement stmt = null;
+		ResultSet rs = null;
+		try {
+			StringBuilder explain = new StringBuilder("EXPLAIN ");
+			explain.append(boundSql.getSql());
+			String sqlExplain = explain.toString();
+			StaticSqlSource sqlsource = new StaticSqlSource(configuration, sqlExplain, boundSql.getParameterMappings());
+			MappedStatement.Builder builder = new MappedStatement.Builder(configuration, "explain_sql", sqlsource, SqlCommandType.SELECT);
+			builder.resultMaps(mappedStatement.getResultMaps()).resultSetType(mappedStatement.getResultSetType()).statementType(mappedStatement.getStatementType());
+			MappedStatement query_statement = builder.build();
+			DefaultParameterHandler handler = new DefaultParameterHandler(query_statement, parameter, boundSql);
+			stmt = connection.prepareStatement(sqlExplain);
+			handler.setParameters(stmt);
+			rs = stmt.executeQuery();
+			while ( rs.next() ) {
+				if (!"Using where".equals(rs.getString("Extra"))) {
+					String tip = " Full table operation is prohibited. SQL: " + boundSql.getSql();
+					if (this.isStopProceed()) {
+						throw new MybatisPlusException(tip);
+					}
+					logger.severe(tip);
+					break;
+				}
+			}
+
+		} catch ( Exception e ) {
+			throw new MybatisPlusException(e); 
+		} finally {
+			if ( rs != null ) {
+				rs.close();
+				rs = null;
+			}
+			if ( stmt != null ) {
+				stmt.close();
+				stmt = null;
+			}
+		}
+	}
+
+	public Object plugin(Object target) {
+		if (target instanceof Executor) {
+			return Plugin.wrap(target, this);
+		}
+		return target;
+	}
+
+	public void setProperties(Properties prop) {
+		// TODO
+	}
+
+	public boolean isStopProceed() {
+		return stopProceed;
+	}
+
+	public void setStopProceed(boolean stopProceed) {
+		this.stopProceed = stopProceed;
+	}
+
+}