IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> 来来来,继续打印sql,利用 mybatis 插件功能 -> 正文阅读

[游戏开发]来来来,继续打印sql,利用 mybatis 插件功能

这里打印sql利用 mybatis 插件功能


	补充知识
	Mybatis拦截器能拦截
	Executor、ParameterHandler、StatementHandler、ResultSetHandler
	四个对象里面的方法
	
	核心逻辑 找到 ClientPreparedStatement.toString() 执行它
	
	这里选用 ParameterHandler 对象拦截 为什么呢
	因为所有sql都需要设置参数啊 不区分查询和更新多好
	注意自带插件拦截功能会先于拦截的方法执行
	也就是 springAOP 中的前置通知
	这就引发一个问题之前的sql参数全是占位符
	所以需要代理 ParameterHandler 
	使 ParameterHandler.setParameters() 方法先执行
	后执行自定义代理逻辑


package cn.com.XXX.config.custom;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.PreparedStatement;
import java.util.Arrays;
import java.util.List;

import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.logging.jdbc.PreparedStatementLogger;
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.springframework.objenesis.instantiator.util.UnsafeUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

/**
 * 拦截结果集对象打印完整sql
 * 
 * @author 
 * @since 2022-03-14
 */
@Component
@Intercepts(value = { @Signature(type = ParameterHandler.class, // 确定要拦截的对象
		method = "setParameters", // 确定要拦截的方法
		args = { PreparedStatement.class } // 拦截方法的参数
		) })
public class ParameterHandlerInterceptor implements Interceptor {
	
	private static final String WRAPPING_COM_MYSQL_CJ_JDBC_CLIENT_PREPARED_STATEMENT = "wrapping com.mysql.cj.jdbc.ClientPreparedStatement: ";
	
	@SuppressWarnings("restriction")
	public Object intercept(Invocation invocation) throws Throwable {
		ParameterHandler handler = (ParameterHandler) invocation.getTarget();
		// 代理 ParameterHandler
		ParameterHandler proxyParameterHandler = ProxyUtil.proxyParameterHandler(handler);
		// 偷天换日(换掉 invocation 的目标对象为代理对象)
		sun.misc.Unsafe unsafe = UnsafeUtils.getUnsafe();
		Field targetFiled = invocation.getClass().getDeclaredField("target");
		unsafe.putObject(invocation, unsafe.objectFieldOffset(targetFiled), proxyParameterHandler);
		
		return invocation.proceed();
	}
	
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}
	
	static class ProxyUtil {
		
		/** 拦截prepareStatement方法代理ClientPreparedStatement */
		public static final String PREPARE_STATEMENT = "prepareStatement";
		
		/** ClientPreparedStatement.toString() */
		public static final String TO_STRING = "toString";
		
		/** ClientPreparedStatement package name */
		public static final String STATEMEN_NAME = "com.mysql.cj.jdbc.ClientPreparedStatement: ";
		
		/** 代理sql执行方法 */
		public static final List<String> AGENTME_THODS = Arrays
				.asList("execute", "executeUpdate", "executeQuery");
		
		/**
		 * 代理PreparedStatement打印sql
		 * 
		 * @param stmt
		 * @return PreparedStatement
		 */
		public static final PreparedStatement proxyStmt(PreparedStatement stmt) {
			return (PreparedStatement) Proxy.newProxyInstance(stmt.getClass().getClassLoader(),
					stmt.getClass().getInterfaces(),
					new InvocationHandler() {
						@Override
						public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
							// 重写executeQuery方法
							if (AGENTME_THODS.contains(method.getName())) {
								Object invokeMethod = ReflectionUtils.invokeMethod(
										ReflectionUtils.findMethod(stmt.getClass(), TO_STRING),
										stmt);
								String sql = (String) invokeMethod;
								if (sql.contains(STATEMEN_NAME)) {
									sql = sql.substring(
											sql.indexOf(STATEMEN_NAME) + STATEMEN_NAME.length());
								}
								printSQL(sql);
							}
							return method.invoke(stmt, args);
						}
					});
		}
		
		/**
		 * 代理PreparedStatement打印sql
		 * 
		 * @param stmt
		 * @return PreparedStatement
		 */
		public static final ParameterHandler proxyParameterHandler(ParameterHandler handler) {
			return (ParameterHandler) Proxy.newProxyInstance(handler.getClass().getClassLoader(),
					handler.getClass().getInterfaces(),
					new InvocationHandler() {
						@Override
						public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
							// 重写executeQuery方法
							Object invoke = method.invoke(handler, args);
							if ("setParameters".equals(method.getName())) {
								PreparedStatement statement = null;
								InvocationHandler invocationHandler = Proxy
										.getInvocationHandler(args[0]);
								if (invocationHandler instanceof PreparedStatementLogger) {
									statement = ((PreparedStatementLogger) invocationHandler)
											.getPreparedStatement();
								}
								if (statement != null) {
									String sqlString = statement.toString();
									if (sqlString.contains(
											WRAPPING_COM_MYSQL_CJ_JDBC_CLIENT_PREPARED_STATEMENT)) {
										sqlString = sqlString.split(
												WRAPPING_COM_MYSQL_CJ_JDBC_CLIENT_PREPARED_STATEMENT)[1];
									}
									printSQL(sqlString);
								}
							}
							return invoke;
						}
					});
		}
		
		/**
		 * printSQL
		 * 
		 * @param sqlString void
		 */
		public static final void printSQL(String sqlString) {
			System.out.println("\033[32;4m"
					+ "\r\n;;-- ------------------------------------------------------------------------------------------------------------------------------\r\n"
						+ sqlString
						+ ";"
						+ "\r\n;;-- ------------------------------------------------------------------------------------------------------------------------------\r\n"
						+ "\033[0m");
		}
	}
}

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-03-15 22:58:26  更:2022-03-15 22:58:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/16 16:02:05-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码