Mybatis自定義攔截器,實(shí)現(xiàn)拼接sql和修改1

一、應(yīng)用場(chǎng)景

1.分頁(yè),如com.github.pagehelper的分頁(yè)插件實(shí)現(xiàn)亮垫;

2.攔截sql做日志監(jiān)控;

3.統(tǒng)一對(duì)某些sql進(jìn)行統(tǒng)一條件拼接,類(lèi)似于分頁(yè)外永。

二、MyBatis的攔截器簡(jiǎn)介

? ? ? ?然后我們要知道攔截器攔截什么樣的對(duì)象拧咳,攔截對(duì)象的什么行為伯顶,什么時(shí)候攔截?

???????在Mybatis框架中骆膝,已經(jīng)給我們提供了攔截器接口祭衩,防止我們改動(dòng)源碼來(lái)添加行為實(shí)現(xiàn)攔截。說(shuō)到攔截器阅签,不得不

提一下掐暮,攔截器是通過(guò)動(dòng)態(tài)代理對(duì)Mybatis加入一些自己的行為。

攔截對(duì)象

確立攔截對(duì)象范圍:要攔對(duì)人政钟,既要保證攔對(duì)人路克,又要保證對(duì)正確的人執(zhí)行正確的攔截動(dòng)作

攔截地點(diǎn)

???????在Mybatis的源碼中:

Executor、StatementHandler

攔截時(shí)機(jī)

? ? ? ? 如果mybatis為我們提供了攔截的功能养交,我們應(yīng)該在Mybatis執(zhí)行過(guò)程的哪一步攔截更加合適呢精算?

? ? ? ? 攔截某個(gè)對(duì)象干某件事的時(shí)候,攔截的時(shí)機(jī)要對(duì)碎连,過(guò)早的攔截會(huì)耽誤別人做自己的工作灰羽,攔截太晚達(dá)不到目的。

? ? ? ? Mybatis實(shí)際也是一個(gè)JDBC執(zhí)行的過(guò)程鱼辙,只不過(guò)被包裝起來(lái)了而已廉嚼,我們要攔截Mybatis的執(zhí)行,最遲也要在獲取

PreparedStatement時(shí)攔截:

PreparedStatement statement = conn.prepareStatement(sql.toString());

? ? ? 在此處偷偷的將sql語(yǔ)句換掉倒戏,就可以改變mybatis的執(zhí)行怠噪,加入自己想要的執(zhí)行行為。

? ? ? 而獲取Mybatis的Statement是在StatementHandler中進(jìn)行的杜跷。

三傍念、代碼示例

第一步矫夷、引入依賴(lài)

<dependency>

????<groupId>org.mybatis.spring.boot</groupId>

????<artifactId>mybatis-spring-boot-starter</artifactId>

????<version>1.3.2</version>

</dependency>

?第二步、配置application.propertities,指定好好映射文件和實(shí)體類(lèi)的目錄

### mybatis

mybatis.mapperLocations: classpath:mapping/*.xml?

###classpath就是應(yīng)用程序resources的路徑

mybatis.type-aliases-package: com.pingan.yc.demo.model

第三步捂寿、配置代碼生成器,引入配置在pom中添加一下代碼

<build>

<plugins>

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-compiler-plugin</artifactId>

<configuration>

<source>1.8</source>

<target>1.8</target>

</configuration>

</plugin>

<!--<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-resources-plugin</artifactId>

<configuration>

<encoding>${encoding}</encoding>

</configuration>

</plugin>-->

<plugin>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-maven-plugin</artifactId>

</plugin>

<!-- mybatis generator 自動(dòng)生成代碼插件 -->

<plugin>

<groupId>org.mybatis.generator</groupId>

<artifactId>mybatis-generator-maven-plugin</artifactId>

<version>1.3.2</version>

<configuration>

<!-- 自動(dòng)生成代碼的配置文件地址 -->

<configurationFile>${basedir}/src/main/resources/generator/generatorConfig.xml</configurationFile>

<verbose>true</verbose>

<overwrite>true</overwrite>

</configuration>

</plugin>

</plugins>

</build>

在resource文件夾下新建generator.xml文件

? ? ? ? 內(nèi)容如下:需要注意配置數(shù)據(jù)庫(kù)連接驅(qū)動(dòng)和數(shù)據(jù)庫(kù)連接孵运,指定生成實(shí)體類(lèi)和映射文件的路徑秦陋,最后按格式配置要生成的數(shù)據(jù)表

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE generatorConfiguration

? ? ? ? PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"

? ? ? ? "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>

? ? <!-- 數(shù)據(jù)庫(kù)驅(qū)動(dòng):選擇你的本地硬盤(pán)上面的數(shù)據(jù)庫(kù)驅(qū)動(dòng)包-->

? ? <classPathEntry? location="D:\Users\admin\.m2\repository\mysql\mysql-connector-java\5.1.46\mysql-connector-java-5.1.46.jar"/>

? ? <context id="DB2Tables"? targetRuntime="MyBatis3">

? ? ? ? <commentGenerator>

? ? ? ? ? ? <property name="suppressDate" value="true"/>

? ? ? ? ? ? <!-- 是否去除自動(dòng)生成的注釋 true:是 : false:否 -->

? ? ? ? ? ? <property name="suppressAllComments" value="true"/>

? ? ? ? </commentGenerator>

? ? ? ? <!--數(shù)據(jù)庫(kù)鏈接URL,用戶名治笨、密碼 -->

? ? ? ? <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:4433/standard_policy_db" userId="dev" password="dev">

? ? ? ? </jdbcConnection>

? ? ? ? <javaTypeResolver>

? ? ? ? ? ? <property name="forceBigDecimals" value="false"/>

? ? ? ? </javaTypeResolver>

? ? ? ? <!-- 生成模型的包名和位置-->

? ? ? ? <javaModelGenerator targetPackage="com.pingan.yc.policy.model" targetProject="src/main/java">

? ? ? ? ? ? <property name="enableSubPackages" value="true"/>

? ? ? ? ? ? <property name="trimStrings" value="true"/>

? ? ? ? </javaModelGenerator>

? ? ? ? <!-- 生成映射文件的包名和位置-->

? ? ? ? <sqlMapGenerator targetPackage="mapping" targetProject="src/main/resources">

? ? ? ? ? ? <property name="enableSubPackages" value="true"/>

? ? ? ? </sqlMapGenerator>

? ? ? ? <!-- 生成DAO的包名和位置-->

? ? ? ? <javaClientGenerator type="XMLMAPPER" targetPackage="com.pingan.yc.policy.dao" targetProject="src/main/java">

? ? ? ? ? ? <property name="enableSubPackages" value="true"/>

? ? ? ? </javaClientGenerator>

? ? ? ? <!-- 要生成的表 tableName是數(shù)據(jù)庫(kù)中的表名或視圖名 domainObjectName是實(shí)體類(lèi)名-->

? ? ? ? <table tableName="t_user_yc" domainObjectName="User" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false"? />

? ? ? ? <table tableName="t_userinfo_yc" domainObjectName="UserInfo" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false"? selectByExampleQueryId="true"/>

? ? </context>

</generatorConfiguration>

最后:在maven命令窗口或如下界面中執(zhí)行mybatis-generator:generate命令

最后生成如下文件:

第四步驳概、攔截器?

? ? ?(生成對(duì)應(yīng)文件后,mybatis環(huán)境算是集成好了旷赖,可以運(yùn)行一個(gè)測(cè)試類(lèi)試試能否從數(shù)據(jù)庫(kù)讀取數(shù)據(jù)顺又。)定義一個(gè)類(lèi)實(shí)現(xiàn)Mybatis的Interceptor接口,@Component注解必須要添加等孵,不然可能出現(xiàn)攔截器無(wú)效的情況V烧铡!俯萌!

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

@Intercepts({

? ? ? ? @Signature(

? ? ? ? ? ? ? ? type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class

? ? ? ? })

})

public class MySqlInterceptor implements Interceptor {

? ? @Override

? ? public Object intercept(Invocation invocation) throws Throwable {

? ? ? ? // 方法一

? ? ? ? StatementHandler statementHandler = (StatementHandler) invocation.getTarget();

? ? ? ? MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());

? ? ? ? //先攔截到RoutingStatementHandler果录,里面有個(gè)StatementHandler類(lèi)型的delegate變量,其實(shí)現(xiàn)類(lèi)是BaseStatementHandler咐熙,然后就到BaseStatementHandler的成員變量mappedStatement

? ? ? ? MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");

? ? ? ? //id為執(zhí)行的mapper方法的全路徑名弱恒,如com.uv.dao.UserMapper.insertUser

? ? ? ? String id = mappedStatement.getId();

? ? ? ? //sql語(yǔ)句類(lèi)型 select、delete棋恼、insert返弹、update

? ? ? ? String sqlCommandType = mappedStatement.getSqlCommandType().toString();

? ? ? ? BoundSql boundSql = statementHandler.getBoundSql();

? ? ? ? //獲取到原始sql語(yǔ)句

? ? ? ? String sql = boundSql.getSql();

? ? ? ? String mSql = sql;

? ? ? ? //TODO 修改位置

? ? ? ? //注解邏輯判斷? 添加注解了才攔截

? ? ? ? Class<?> classType = Class.forName(mappedStatement.getId().substring(0, mappedStatement.getId().lastIndexOf(".")));

? ? ? ? String mName = mappedStatement.getId().substring(mappedStatement.getId().lastIndexOf(".") + 1, mappedStatement.getId().length());

? ? ? ? for (Method method : classType.getDeclaredMethods()) {

? ? ? ? ? ? if (method.isAnnotationPresent(InterceptAnnotation.class) && mName.equals(method.getName())) {

? ? ? ? ? ? ? ? InterceptAnnotation interceptorAnnotation = method.getAnnotation(InterceptAnnotation.class);

? ? ? ? ? ? ? ? if (interceptorAnnotation.flag()) {

? ? ? ? ? ? ? ? ? ? mSql = sql + " limit 2";

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? //通過(guò)反射修改sql語(yǔ)句

? ? ? ? Field field = boundSql.getClass().getDeclaredField("sql");

? ? ? ? field.setAccessible(true);

? ? ? ? field.set(boundSql, mSql);

? ? ? ? return invocation.proceed();

? ? }

? ? @Override

? ? public Object plugin(Object target) {

? ? ? ? if (target instanceof StatementHandler) {

? ? ? ? ? ? return Plugin.wrap(target, this);

? ? ? ? } else {

? ? ? ? ? ? return target;

? ? ? ? }

? ? }

? ? ? ? @Override

? ? public void setProperties(Properties properties) {

? ? }

}

此外,定義了一個(gè)方法層面的注解爪飘,實(shí)現(xiàn)局部指定攔截

@Target({ElementType.METHOD,ElementType.PARAMETER})

@Retention(RetentionPolicy.RUNTIME)

public @interface InterceptAnnotation {

? ? boolean flag() default? true;

}

最后只需要在指定的方法出添加注解就可以實(shí)現(xiàn)局部攔截

最后運(yùn)行看結(jié)果就好了义起。(按我的邏輯下來(lái)會(huì)只查到2條數(shù)據(jù))

在@Interceptor的注解中也有以下的注解方式,究竟有什么不同和差異师崎,請(qǐng)大家自己研究咯并扇,我就在此拋磚引玉了,請(qǐng)各位大牛指導(dǎo)了抡诞。

@Intercepts(value = {

? ? ? ? @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,

? ? ? ? ? ? ? ? ? ? ? ? CacheKey.class, BoundSql.class}),

? ? ? ? @Signature(type = Executor.class,

? ? ? ? ? ? ? ? method = "query",

? ? ? ? ? ? ? ? args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末穷蛹,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子昼汗,更是在濱河造成了極大的恐慌肴熏,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件顷窒,死亡現(xiàn)場(chǎng)離奇詭異蛙吏,居然都是意外死亡源哩,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén)鸦做,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)励烦,“玉大人,你說(shuō)我怎么就攤上這事泼诱√陈樱” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵治筒,是天一觀的道長(zhǎng)屉栓。 經(jīng)常有香客問(wèn)我,道長(zhǎng)耸袜,這世上最難降的妖魔是什么友多? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮堤框,結(jié)果婚禮上域滥,老公的妹妹穿的比我還像新娘。我一直安慰自己蜈抓,他們只是感情好骗绕,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著资昧,像睡著了一般酬土。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上格带,一...
    開(kāi)封第一講書(shū)人閱讀 51,287評(píng)論 1 301
  • 那天撤缴,我揣著相機(jī)與錄音,去河邊找鬼叽唱。 笑死屈呕,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的棺亭。 我是一名探鬼主播虎眨,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼镶摘!你這毒婦竟也來(lái)了嗽桩?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤凄敢,失蹤者是張志新(化名)和其女友劉穎碌冶,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體涝缝,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡扑庞,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年譬重,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片罐氨。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡臀规,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出栅隐,到底是詐尸還是另有隱情塔嬉,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布约啊,位于F島的核電站邑遏,受9級(jí)特大地震影響佣赖,放射性物質(zhì)發(fā)生泄漏恰矩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一憎蛤、第九天 我趴在偏房一處隱蔽的房頂上張望外傅。 院中可真熱鬧,春花似錦俩檬、人聲如沸萎胰。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)技竟。三九已至,卻和暖如春屈藐,著一層夾襖步出監(jiān)牢的瞬間榔组,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工联逻, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留搓扯,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓包归,卻偏偏與公主長(zhǎng)得像锨推,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子公壤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354