mybatis的执行的大概过程:首先需要有sqlSessionFactroy,然后通过sqlSessionFactory拿到sqlSession,然后通过sqlSession调用getMapper拿到代理的接口,然后拿到代理的接口的信息mapperInterface,从而找到需要执行的具体的方法中的sql方法,,如果执行过,同时没有发生改变的话,则直接返回结果,否则会进行更新,同时如果执行过的话,会直接返回结果,此时会看到methodCache中有我们执行过的方法。
mybatis中,如果想进行sql的拦截,需要对其基于interceptor做拦截。因为mybatis的执行中,我们需要获取它的boundSql,而获取boundSql,需要获取MappedStatement,而MappedStatement可以在StatementHandler语句处理器中找到,因此可以在此基础上获取,通过反射的方式获取,此时可以用到插件模块中的invocation对象获取,然后对其进行增强。
基于interceptor可以实现sql的完整打印,除了实现打印之外。其实还可以实现分页和排序,下面的分页和排序基于aop+mybatis的interceptor实现。其本质还是对mappedStament的boundSql进行增强。
下面的项目来源于github,通过这个我们可以很好的学习mybatis中插件interceptor的使用。
首先定义分页元注解:
/*** 自定义分页注解*/ElementType.METHOD) (RetentionPolicy.RUNTIME) (public@interfaceLimit { /*** 当前页面* * @return*/intpage() default0; /*** 每页显示数量* * @return*/intpageSize() default10; }
定义排序元注解:
/*** 自定义排序注解*/ElementType.METHOD) (RetentionPolicy.RUNTIME) (public@interfaceOrderBy { /*** 表的别名*/StringtableAlias() default""; /*** 排序字段*/StringorderColumn() default""; /*** ASC/DESC 默认倒序* @return*/booleanisAsc() defaultfalse; }
定义基础切面处理类:
/*** @ClassName BaseAspectAbstract* @Description 基础切面处理类,每一个Spring的切面都需要去继承此抽象类* @Date**/publicabstractclassBaseAspectAbstract { privatestaticTreeMap<Integer, SQLLanguage>CONTAINERS=newTreeMap<>(); // 放入sql 切点、sql类型、sqlpublicvoidputSQL(JoinPointpoint, SQLEnumssqlEnums, SQLLanguagesqlLanguage) { CONTAINERS.put(sqlEnums.getId(), sqlLanguage); // 获取方法里的参数Objectparmas=point.getArgs()[0]; Mapmap= (Map)parmas; map.put("SQL", getSQL()); } publicTreeMap<Integer, SQLLanguage>getSQL() { returnCONTAINERS; } }
进行分页切面:
3) //拼接sql时的顺序 (publicclassLimitAspectextendsBaseAspectAbstract { "@annotation(com.mybatis.interceptor.annotation.Limit)") (publicvoidlimitCut() {} "limitCut()") (publicvoidlimit(JoinPointpoint) { StringBuilderlimitBuilder=newStringBuilder(" LIMIT "); MethodSignaturemethodSignature= (MethodSignature)point.getSignature(); // 获得对应注解Limitlimit=methodSignature.getMethod().getAnnotation(Limit.class); if (!StringUtils.isEmpty(limit)) { limitBuilder.append(limit.page()).append(",").append(limit.pageSize()); putSQL(point, LIMIT, newSQLLanguage(limitBuilder.toString())); } } }
进行排序切面:
2) (publicclassOrderByAspectextendsBaseAspectAbstract { // 切点:对注解中的特定注解进行拦截,进行增强"@annotation(com.mybatis.interceptor.annotation.OrderBy)") (publicvoidorderByCut() {} // 执行切点操作,将其进行增强,放入排序"orderByCut()") (publicvoidorderBy(JoinPointpoint) { StringBuilderorderByBuilder=newStringBuilder(" ORDER BY "); MethodSignaturemethodSignature= (MethodSignature)point.getSignature(); // 获得对应注解OrderByorderBy=methodSignature.getMethod().getAnnotation(OrderBy.class); if (!StringUtils.isEmpty(orderBy)) { Stringsort=orderBy.isAsc() ?" asc " : " desc"; orderByBuilder.append(orderBy.orderColumn()).append(sort); putSQL(point, ORDERBY, newSQLLanguage(orderByBuilder.toString())); } } }
枚举sql顺序:
/*** sql类型枚举*/publicenumSQLEnums { /*** 数字越靠前 则拼接SQL语句越靠前执行,目前拼接顺序为* SELECT * FROM table GROUP BY ORDER BY xxx LIMIT 0, 10*/LIKE(1, "LIKE"), GROUPBY(2, "GROUP BY"), ORDERBY(3, "ORDER BY"), LIMIT(4, "LIMIT"); privateintid; privateStringcondition; SQLEnums(intid, Stringcondition) { this.id=id; this.condition=condition; } publicintgetId() { returnid; } publicvoidsetId(intid) { this.id=id; } publicStringgetCondition() { returncondition; } publicvoidsetCondition(Stringcondition) { this.condition=condition; } }
执行sql增强的注解放置在serviceImpl里面:
"/nba") (publicclassPlayController { privatePlayerServiceplayerService; "/player") (publicList<Player>getList(Map<String, Object>params) { List<Player>players=playerService.getList(params); returnplayers; } }
执行sql增强:
publicclassPlayerServiceImplimplementsPlayerService { privatePlayerMapperplayerMapper; // 加入自定义注解,方便切点进行增强orderColumn="height") ( () publicList<Player>getList(Map<String, Object>params) { returnplayerMapper.getList(params); } }
执行到这里会执行动态代理,然后执行sql拦截。
执行sql拦截:
type=StatementHandler.class, method="prepare", args= {Connection.class, Integer.class})) ( (publicclassDataFilterInterceptorextendsAbstractSqlParserHandlerimplementsInterceptor { // 拦截器publicObjectintercept(Invocationinvocation) throwsThrowable { StatementHandlerstatementHandler=PluginUtils.realTarget(invocation.getTarget()); MetaObjectmetaObject=SystemMetaObject.forObject(statementHandler); // SQLLanguage 解析sqlParser(metaObject); // 非查询操作MappedStatementmappedStatement= (MappedStatement)metaObject.getValue("delegate.mappedStatement"); if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) { returninvocation.proceed(); } // 取出原始SQL 取出参数BoundSqlboundSql= (BoundSql)metaObject.getValue("delegate.boundSql"); StringdataSql=boundSql.getSql(); ObjectparamObj=boundSql.getParameterObject(); Mapmap= (Map)paramObj; StringsqlLanguage=getSQLLanguage(map); Stringsql=dataSql+sqlLanguage; // 重写sqlmetaObject.setValue("delegate.boundSql.sql", sql); returninvocation.proceed(); } // 插件publicObjectplugin(Objecttarget) { if (targetinstanceofStatementHandler) { returnPlugin.wrap(target, this); } returntarget; } publicvoidsetProperties(Propertiesproperties) { } // 获取sql语句privateStringgetSQLLanguage(Map<String, Object>map) { TreeMap<Integer, SQLLanguage>sqlMap= (TreeMap)map.get("SQL"); StringBuildersqlBuilder=newStringBuilder(); for (Map.EntrytreeMap : sqlMap.entrySet()) { SQLLanguagesql= (SQLLanguage)treeMap.getValue(); if (null!=sql) { sqlBuilder.append(sql); } } returnsqlBuilder.toString(); } }