当前位置: 首页 > news >正文

金安区住房和城乡建设局网站全网关键词优化公司哪家好

金安区住房和城乡建设局网站,全网关键词优化公司哪家好,万网个人网站怎么备案,更改wordpress地址MyBatis插件介绍 MyBatis提供了一种插件(plugin)的功能,虽然叫做插件,但其实这是拦截器功能。 MyBatis允许使用者在映射语句执行过程中的某一些指定的节点进行拦截调用,通过织入拦截器,在不同节点修改一些执行过程中的关键属性&…

MyBatis插件介绍

MyBatis提供了一种插件(plugin)的功能,虽然叫做插件,但其实这是拦截器功能。

MyBatis允许使用者在映射语句执行过程中的某一些指定的节点进行拦截调用,通过织入拦截器,在不同节点修改一些执行过程中的关键属性,从而影响SQL的生成、执行和返回结果,如:来影响Mapper.xml到SQL语句的生成、执行SQL前对预编译的SQL执行参数的修改、SQL执行后返回结果到Mapper接口方法返参POJO对象的类型转换和封装等。

根据上面的对Mybatis拦截器作用的描述,可以分析其可能的用途;最常见的就是Mybatis自带的分页插件PageHelper或Rowbound参数,通过打印实际执行的SQL语句,发现我们的分页查询之前,先执行了COUNT(*)语句查询数量,然后再执行查询时修改了SQL语句即在我们写的SQL语句后拼接上了分页语句LIMIT(offset, pageSize);

此外,实际工作中,可以使用Mybatis拦截器来做一些数据过滤、数据加密脱敏、SQL执行时间性能监控和告警等;既然要准备使用它,下面先来了解下其原理;

默认情况下,MyBatis 允许使用插件来拦截的四种相关操作类方法:

Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)

这几个接口之间的关系大概是这样的:
在这里插入图片描述

Mybatis整体执行流程:

在这里插入图片描述

核心对象

  • Configuration:初始化基础配置,比如MyBatis的别名等,一些重要的类型对象,如插件,映射器,ObjectFactory和typeHandler对象,MyBatis所有的配置信息都维持在Configuration对象之中。
  • SqlSessionFactory:SqlSession工厂。
  • SqlSession:作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要的数据库增删改查功能。
  • Executor:MyBatis的内部执行器,它负责调用StatementHandler操作数据库,并把结果集通过ResultSetHandler进行自动映射,另外,它还处理二级缓存的操作。
  • StatementHandler:MyBatis直接在数据库执行SQL脚本的对象。另外它也实现了MyBatis的一级缓存。
  • ParameterHandler:负责将用户传递的参数转换成JDBC Statement所需要的参数。是MyBatis实现SQL入参设置的对象。
  • ResultSetHandler:负责将JDBC返回的ResultSet结果集对象转换成List类型的集合。是MyBatis把ResultSet集合映射成POJO的接口对象。
  • TypeHandler:负责Java数据类型和JDBC数据类型之间的映射和转换。
  • MappedStatement:MappedStatement维护了一条<select|update|delete|insert>节点的封装。
  • SqlSource :负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回。
  • BoundSql:表示动态生成的SQL语句以及相应的参数信息。

MyBatis自定义插件的实现

通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。

Interceptor 接口的定义如下所示:

public interface Interceptor {//拦截器具体实现Object intercept(Invocation invocation) throws Throwable;//拦截器的代理类Object plugin(Object target);//添加属性void setProperties(Properties properties);
}

相关注解:

@Intercepts	// 描述:标志该类是一个拦截器
@Signature 	// 描述:指明该拦截器需要拦截哪一个接口的哪一个方法// @Signature注解中属性:
type; // 四种类型接口中的某一个接口,如Executor.class;
method; // 对应接口中的某一个方法名,比如Executor的query方法;
args; // 对应接口中的某一个方法的参数,比如Executor中query方法因为重载原因,有多个,args就是指明参数类型,从而确定是具体哪一个方法;

下面来看一个自定义的简单Interceptor示例:

import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.stereotype.Component;import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.Properties;@Component
//拦截StatementHandler类中参数类型为Statement的prepare方法(prepare=在预编译SQL前加入修改的逻辑)
//即拦截 Statement prepare(Connection var1, Integer var2) 方法
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
@Slf4j
public class MyPlugin implements Interceptor  {@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 获取原始sqlStatementHandler statementHandler = (StatementHandler) invocation.getTarget();BoundSql boundSql = statementHandler.getBoundSql();// 通过MetaObject优雅访问对象的属性,这里是访问statementHandler的属性;:MetaObject是Mybatis提供的一个用于方便、// 优雅访问对象属性的对象,通过它可以简化代码、不需要try/catch各种reflect异常,同时它支持对JavaBean、Collection、Map三种类型对象的操作。MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,new DefaultReflectorFactory());// 先拦截到RoutingStatementHandler,里面有个StatementHandler类型的delegate变量,其实现类是BaseStatementHandler,然后就到BaseStatementHandler的成员变量mappedStatementMappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");// 通过反射,拦截方法上带有自定义@InterceptAnnotation注解的方法,并修改sqlString mSql = sqlAnnotationEnhance(mappedStatement, boundSql);Field field = boundSql.getClass().getDeclaredField("sql");field.setAccessible(true);field.set(boundSql, mSql);return invocation.proceed();}@Overridepublic Object plugin(Object target) {if (target instanceof StatementHandler) {return Plugin.wrap(target, this);} else {return target;}}@Overridepublic void setProperties(Properties properties) {}/*** 通过反射,拦截方法上带有自定义@InterceptAnnotation注解的方法,并增强sql* @param id 方法全路径* @param sqlCommandType sql类型* @param sql 所执行的sql语句*/private String sqlAnnotationEnhance(MappedStatement mappedStatement, BoundSql boundSql) throws ClassNotFoundException {// 获取到原始sql语句String sql = boundSql.getSql().toLowerCase();// sql语句类型 select、delete、insert、updateString sqlCommandType = mappedStatement.getSqlCommandType().toString();// 数据库连接信息//  Configuration configuration = mappedStatement.getConfiguration();//  ComboPooledDataSource dataSource = (ComboPooledDataSource)configuration.getEnvironment().getDataSource();//  dataSource.getJdbcUrl();// id为执行的mapper方法的全路径名,如com.cq.UserMapper.insertUser, 便于后续使用反射String id = mappedStatement.getId();// 获取当前所拦截的方法名称String mName = id.substring(id.lastIndexOf(".") + 1);// 通过类全路径获取Class对象Class<?> classType = Class.forName(id.substring(0, id.lastIndexOf(".")));// 获得参数集合String paramString = null;if (boundSql.getParameterObject() != null) {paramString = boundSql.getParameterObject().toString();}// 遍历类中所有方法名称,并匹配上当前所拦截的方法for (Method method : classType.getDeclaredMethods()) {if (mName.equals(method.getName())) {// 判断方法上是否带有自定义@InterceptAnnotation注解InterceptAnnotation interceptorAnnotation = method.getAnnotation(InterceptAnnotation.class);if (interceptorAnnotation != null && interceptorAnnotation.flag()) {log.info("intercept func:{}, type:{}, origin SQL:{}", mName, sqlCommandType, sql);// 场景1:分页功能: return sql + " limit 1";if ("select".equals(sqlCommandType.toLowerCase())) {if (!sql.toLowerCase().contains("limit")) {sql = sql + " limit 1";}}// 场景2:校验功能 :update/delete必须要有where条件,并且打印出where中的条件if ("update".equals((sqlCommandType.toLowerCase())) || "delete".equals(sqlCommandType.toLowerCase())) {if (!sql.toLowerCase().contains("where")) {log.warn("update or delete not safe!");}}// 场景3:分库分表: 根据userId哈希,替换注解中的表名if (sql.toLowerCase().contains(interceptorAnnotation.value())) {String userId = getValue(paramString, "userId");if (userId != null) {int num = Integer.parseInt(userId);// 模拟分10个库,5个表String data_source_id = String.valueOf(num % 10);String new_table = interceptorAnnotation.value().concat("_").concat(String.valueOf(num % 5));log.info("set data_source_id:{}, table: {}", data_source_id, new_table);// 设置data_source_id路由, 替换sql表名sql = StringUtils.replace(sql, interceptorAnnotation.value(), new_table);}}log.info("new SQL:{}", sql);return sql;}}}return sql;}String getValue(String param, String key) {if (param == null) {return null;}String[] keyValuePairs = param.substring(1, param.length() - 1).split(",");for (String pair : keyValuePairs) {String[] entry = pair.split("=");if (entry[0].trim().equals(key)) {return entry[1].trim();}}return null;}
}

自定义注解如下:

import java.lang.annotation.*;@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface InterceptAnnotation {String value() default "";/*** true增强、false忽略*/boolean flag() default true;
}

添加插件:

@Component
public class DynamicPluginHelper {@Autowiredprivate List<SqlSessionFactory> sqlSessionFactoryList;@Autowiredprivate MyPlugin myPlugin;@PostConstructpublic void addMysqlInterceptor() {for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) {org.apache.ibatis.session.Configuration configuration = sqlSessionFactory.getConfiguration();configuration.addInterceptor(myPlugin);}}
}

测试结果如下:

sql.MyPlugin  : intercept func:findUser, type:SELECT, origin SQL:select * from t_user where id = ?
sql.MyPlugin  : set data_source_id:4, table: t_user_4
sql.MyPlugin  : new SQL:select * from t_user_4 where id = ? limit 1

问题记录

错误描述:
There is no getter for property named 'delegate' in 'class com.sun.proxy.$Proxy32'

错误原因:
1、你有多个拦截器,拦截同一对象的同一行为。测试时避免其他拦截器的干扰可以先把注册的拦截器注释掉。
2、依赖包版本不对
3、拦截器配置类放置的位置不正确,导致包没找到

参考:
https://blog.csdn.net/minghao0508/article/details/124420953
https://blog.csdn.net/qq_36881887/article/details/111589294
https://www.cnblogs.com/simplejavahome/p/16617112.html
https://www.cnblogs.com/nefure/p/16948633.html
https://blog.csdn.net/u011602668/article/details/128735771

http://www.yayakq.cn/news/176904/

相关文章:

  • 如何修复网站中的死链dreamwear网页制作
  • 作品集制作网站博山网站seo
  • 江门专业网站建设价格广东华电建设股份有限公司网站
  • 网站建设运维方案移动互联网开发技术就业前景
  • 南宁网站建设托管白城学做网站
  • 做徒步网站怎么样花木网站建设
  • 代做毕业设计网站 道路桥梁杭州做美妆的网站
  • 做网站用图片算侵犯著作权吗重庆的网站设计公司
  • 国外教做蛋糕的网站淘宝客网站开发服务商
  • 想开个网站不知怎样做wordpress编辑器排行
  • 网站广告的图片怎么做网络营销的起源
  • 房产如何做网站西安营销策划推广公司
  • 河北网站制作公司报价网站开发的软件环境有哪些
  • 网站为什么没有排名了制作网页的第一步是什么
  • 推销什么企业做网站和app江苏交通运输厅网站建设
  • 大庆城乡建设局网站首页一元建站
  • iis7 部署网站做爰片姿势网站
  • 做宣传册参考的网站wordpress迁移保留账号
  • 做网站需完成的软件建立名词
  • 网站优化包括什么是网络营销?网络营销的基本职能有哪些方面
  • 现在网站建设还用测浏览器吗wordpress站点语言
  • 广东省农业农村厅官方网站三合一网站一般多少钱
  • 哪个网站可以做验证码兼职网络推广公司运营
  • 网站建设有关模板信息产业部icp备案中心网站
  • 南京网站建设与维护免费建站系统怎么用
  • 建立购物网站 app联盟营销平台
  • 做网站效果图总结wordpress更改前端引用
  • 女和男做的视频网站山东网
  • 网站备案 幕布机械设备上哪个网站做外贸推广
  • 做那个网站比较好天河网站 建设信科网络