" />

欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Mybatis攔截器實(shí)現(xiàn)自定義需求

 更新時(shí)間:2023年05月11日 10:42:39   作者:寧在春  
本文主要介紹了Mybatis攔截器實(shí)現(xiàn)自定義需求,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

前言

最近在參加金石計(jì)劃,在考慮寫(xiě)什么的時(shí),想到自己在項(xiàng)目中使用過(guò)的mybatis的插件,就想趁這個(gè)機(jī)會(huì)聊一聊我們接觸頻繁的Mybatis.

如果是使用過(guò)Mybatis的小伙伴,那么我們接觸過(guò)的第一個(gè)Mybatis的插件自然就是分頁(yè)插件(Mybatis-PageHelper)啦。

你有了解過(guò)它是如何實(shí)現(xiàn)的嗎?你有沒(méi)有自己編寫(xiě) Mybatis 插件去實(shí)現(xiàn)一些自定義需求呢?

插件是一種常見(jiàn)的擴(kuò)展方式,大多數(shù)開(kāi)源框架也都支持用戶(hù)通過(guò)添加自定義插件的方式來(lái)擴(kuò)展或改變框架原有的功能。

Mybatis 中也提供了插件的功能,雖然叫插件,但是實(shí)際上是通過(guò)攔截器( Interceptor )實(shí)現(xiàn)的,通過(guò)攔截某些方法的調(diào)用,在執(zhí)行目標(biāo)邏輯之前插入我們自己的邏輯實(shí)現(xiàn)。另外在 MyBatis 的插件模塊中還涉及責(zé)任鏈模式和 JDK 動(dòng)態(tài)代理~

文章大綱:

image.png

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

  • 一些字段的自動(dòng)填充
  • SQL語(yǔ)句監(jiān)控、打印、數(shù)據(jù)權(quán)限等
  • 數(shù)據(jù)加解密操作、數(shù)據(jù)脫敏操作
  • 分頁(yè)插件
  • 參數(shù)、結(jié)果集的類(lèi)型轉(zhuǎn)換

這些都是一些可以使用Mybatis插件實(shí)現(xiàn)的場(chǎng)景,當(dāng)然也可以使用其他的方式來(lái)實(shí)現(xiàn),只不過(guò)攔截的地方不一樣罷了,有早有晚。

二、Mybatis實(shí)現(xiàn)自定義攔截器

我們用自定義攔截器實(shí)現(xiàn)一個(gè)相對(duì)簡(jiǎn)單的需求,在大多數(shù)表設(shè)計(jì)中,都會(huì)有create_time和update_time等字段,在創(chuàng)建或更新時(shí)需要更新相關(guān)字段。

如果是使用過(guò)MybatisPlus的小伙伴,可能知道在MybatisPlus中有一個(gè)自動(dòng)填充功能,通過(guò)實(shí)現(xiàn)MetaObjectHandler接口中的方法來(lái)進(jìn)行實(shí)現(xiàn)(主要的實(shí)現(xiàn)代碼在com.baomidou.mybatisplus.core.MybatisParameterHandler中).

但使用Mybatis,并沒(méi)有相關(guān)的方法或 API 可以直接來(lái)實(shí)現(xiàn)。所以我們這次就用以此處作為切入點(diǎn),自定義攔截器來(lái)實(shí)現(xiàn)類(lèi)似的自動(dòng)填充功能。

編寫(xiě)步驟

  • 編寫(xiě)一個(gè)攔截器類(lèi)實(shí)現(xiàn) Interceptor 接口
  • 添加攔截注解 @Intercepts
  • 在xml文件中配置攔截器或者添加到Configuration中

基礎(chǔ)的環(huán)境我就不再貼出來(lái)啦哈,直接上三個(gè)步驟的代碼

2.1、編寫(xiě)攔截器

package com.nzc.interceptor;
?
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
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.Signature;
import org.springframework.beans.factory.annotation.Value;
?
import java.lang.reflect.Field;
import java.util.*;
?
/**
 * @author 寧在春
 * @version 1.0
 * @description: 通過(guò)實(shí)現(xiàn)攔截器來(lái)實(shí)現(xiàn)部分字段的自動(dòng)填充功能
 * @date 2023/4/6 21:49
 */
@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
@Slf4j
public class MybatisMetaInterceptor implements Interceptor {
?
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        String sqlId = mappedStatement.getId();
        log.info("------sqlId------" + sqlId);
        SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
        Object parameter = invocation.getArgs()[1];
        log.info("------sqlCommandType------" + sqlCommandType);
        log.info("攔截查詢(xún)請(qǐng)求 Executor#update 方法" + invocation.getMethod());
        if (parameter == null) {
            return invocation.proceed();
        }
        if (SqlCommandType.INSERT == sqlCommandType) {
?
            Field[] fields = getAllFields(parameter);
            for (Field field : fields) {
                log.info("------field.name------" + field.getName());
                try {
                    // 注入創(chuàng)建時(shí)間
                    if ("createTime".equals(field.getName())) {
                        field.setAccessible(true);
                        Object local_createDate = field.get(parameter);
                        field.setAccessible(false);
                        if (local_createDate == null || local_createDate.equals("")) {
                            field.setAccessible(true);
                            field.set(parameter, new Date());
                            field.setAccessible(false);
                        }
                    }
                } catch (Exception e) {
                }
            }
        }
        if (SqlCommandType.UPDATE == sqlCommandType) {
            Field[] fields = getAllFields(parameter);
            for (Field field : fields) {
                log.info("------field.name------" + field.getName());
                try {
                    if ("updateTime".equals(field.getName())) {
                        field.setAccessible(true);
                        field.set(parameter, new Date());
                        field.setAccessible(false);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return invocation.proceed();
    }
?
    @Override
    public Object plugin(Object target) {
        return Interceptor.super.plugin(target);
    }
    // 稍后會(huì)展開(kāi)說(shuō)的
    @Override
    public void setProperties(Properties properties) {
        System.out.println("=======begin");
        System.out.println(properties.getProperty("param1"));
        System.out.println(properties.getProperty("param2"));
        Interceptor.super.setProperties(properties);
        System.out.println("=======end");
    }
?
    /**
     * 獲取類(lèi)的所有屬性,包括父類(lèi)
     *
     * @param object
     * @return
     */
    public static Field[] getAllFields(Object object) {
        Class<?> clazz = object.getClass();
        List<Field> fieldList = new ArrayList<>();
        while (clazz != null) {
            fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
            clazz = clazz.getSuperclass();
        }
        Field[] fields = new Field[fieldList.size()];
        fieldList.toArray(fields);
        return fields;
    }
}
?

2.2、添加到Mybatis配置

我這里使用的JavaConfig的方式

package com.nzc.config;
?
import com.nzc.interceptor.*;
import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
?
@Configuration
public class MyBatisConfig {
?
    @Bean
    public ConfigurationCustomizer configurationCustomizer() {
        return new ConfigurationCustomizer() {
            @Override
            public void customize(org.apache.ibatis.session.Configuration configuration) {
                // 開(kāi)啟駝峰命名映射
                configuration.setMapUnderscoreToCamelCase(true);
                MybatisMetaInterceptor mybatisMetaInterceptor = new MybatisMetaInterceptor();
                Properties properties = new Properties();
                properties.setProperty("param1","javaconfig-value1");
                properties.setProperty("param2","javaconfig-value2");
                mybatisMetaInterceptor.setProperties(properties);
                configuration.addInterceptor(mybatisMetaInterceptor);
            }
        };
    }
}

如果是xml配置的話(huà),則是如下: property 是設(shè)置 攔截器中需要用到的參數(shù)

<configuration>
    <plugins>
        <plugin interceptor="com.nzc.interceptor.MybatisMetaInterceptor"> 
            <property name="param1" value="value1"/>
            <property name="param2" value="value2"/>
        </plugin>
    </plugins>
</configuration>    

2.3、測(cè)試

測(cè)試代碼:實(shí)現(xiàn)了一個(gè)SysMapper的增刪改查

package com.nzc.mapper;
??
import com.nzc.entity.SysUser;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
?
import java.util.List;
?
/**
 * @author 寧在春
 * @description 針對(duì)表【sys_user】的數(shù)據(jù)庫(kù)操作Mapper
 */
@Mapper
public interface SysUserMapper {
?
    @Select("SELECT * FROM tb_sys_user")
    List<SysUser> list();
?
    @Insert("insert into tb_sys_user(id,username,realname,create_time,update_time) values (#{id}, #{username}, #{realname}, #{createTime}, #{updateTime})")
    Boolean insert(SysUser sysUser);
?
    @Update("update tb_sys_user set  username=#{username} , realname=#{realname},update_time=#{updateTime}  where id=#{id}")
    boolean update(SysUser sysUser);
}
/**
 * @author 寧在春
 * @version 1.0
 * @description: TODO
 * @date 2023/4/6 21:38
 */
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class SysUserMapperTest {
?
    @Autowired
    private SysUserMapper sysUserMapper;
?
?
    @Test
    public void test1(){
        System.out.println(sysUserMapper.list());
    }
?
    @Test
    public void test2(){
        SysUser sysUser = new SysUser();
        sysUser.setId("1235");
        sysUser.setUsername("nzc5");
        sysUser.setRealname("nzc5");
        System.out.println(sysUserMapper.insert(sysUser));
    }
?
    @Test
    public void test3(){
        SysUser sysUser = new SysUser();
        sysUser.setId("1235");
        sysUser.setUsername("nzc7");
        sysUser.setRealname("nzc5");
        System.out.println(sysUserMapper.update(sysUser));
    }
}
?

當(dāng)然重點(diǎn)不在這里,而是在我們打印的日志上,一起來(lái)看看效果吧

image.png

此處相關(guān)日志對(duì)應(yīng)Interceptor中的日志打印,想要了解的更為詳細(xì)的可以debug查看一番。

2.4、小結(jié)

通過(guò)這個(gè)小小的案例,我想大伙對(duì)于Mybatis中的攔截器應(yīng)當(dāng)是沒(méi)有那般陌生了吧,接下來(lái)再來(lái)仔細(xì)聊聊吧

如果你使用過(guò)MybatisPlus的話(huà),在讀完這篇博文后,可以思考思考下面這個(gè)問(wèn)題,或去看一看源碼,將知識(shí)串聯(lián)起來(lái),如果可以的話(huà),記得把答案貼到評(píng)論區(qū)啦~~~

思考:還記得這一小節(jié)開(kāi)始我們聊到的MybatisPlus實(shí)現(xiàn)的自動(dòng)填充功能嗎?它是怎么實(shí)現(xiàn)的呢?

三、攔截器接口介紹

MyBatis 插件可以用來(lái)實(shí)現(xiàn)攔截器接口 Interceptor ,在實(shí)現(xiàn)類(lèi)中對(duì)攔截對(duì)象和方法進(jìn)行處理

public interface Interceptor {
  // 執(zhí)行攔截邏輯的方法
  Object intercept(Invocation invocation) throws Throwable;
?
    //這個(gè)方法的參數(shù) target 就是攔截器要攔截的對(duì)象,該方法會(huì)在創(chuàng)建被攔截的接口實(shí)現(xiàn)類(lèi)時(shí)被調(diào)用。
    //該方法的實(shí)現(xiàn)很簡(jiǎn)單 ,只需要調(diào)用 MyBatis 提供的 Plug 類(lèi)的 wrap 靜態(tài)方法就可以通過(guò) Java 動(dòng)態(tài)代理攔截目標(biāo)對(duì)象。
  default Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }
?
  //這個(gè)方法用來(lái)傳遞插件的參數(shù),可以通過(guò)參數(shù)來(lái)改變插件的行為
  default void setProperties(Properties properties) {
    // NOP
  }
}

有點(diǎn)懵沒(méi)啥事,一個(gè)一個(gè)展開(kāi)說(shuō):

intercept 方法

Object intercept(Invocation invocation) throws Throwable;

簡(jiǎn)單說(shuō)就是執(zhí)行攔截邏輯的方法,但不得不說(shuō)這句話(huà)是個(gè)高度概括~

首先我們要明白參數(shù)Invocation是個(gè)什么東東:

public class Invocation {
?
  private final Object target; // 攔截的對(duì)象信息
  private final Method method; // 攔截的方法信息
  private final Object[] args; // 攔截的對(duì)象方法中的參數(shù)
?
  public Invocation(Object target, Method method, Object[] args) {
    this.target = target;
    this.method = method;
    this.args = args;
  }
?
 // get...
 // 利用反射來(lái)執(zhí)行攔截對(duì)象的方法
  public Object proceed() throws InvocationTargetException, IllegalAccessException {
    return method.invoke(target, args);
  }
}

聯(lián)系我們之前實(shí)現(xiàn)的自定義攔截器上的注解:

@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
  • target對(duì)應(yīng)我們攔截的Executor對(duì)象
  • method對(duì)應(yīng)Executor#update方法
  • args對(duì)應(yīng)Executor#update#args參數(shù)

plugin方法

這個(gè)方法其實(shí)也很好說(shuō):

那就是Mybatis在創(chuàng)建攔截器代理時(shí)候會(huì)判斷一次,當(dāng)前這個(gè)類(lèi) Interceptor 到底需不需要生成一個(gè)代理進(jìn)行攔截,如果需要攔截,就生成一個(gè)代理對(duì)象,這個(gè)代理就是一個(gè) {@link Plugin},它實(shí)現(xiàn)了jdk的動(dòng)態(tài)代理接口 {@link InvocationHandler},如果不需要代理,則直接返回目標(biāo)對(duì)象本身 加載時(shí)機(jī):該方法在 mybatis 加載核心配置文件時(shí)被調(diào)用

  default Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }
public class Plugin implements InvocationHandler {
?
?
    //  利用反射,獲取這個(gè)攔截器 MyInterceptor 的注解 Intercepts和Signature,然后解析里面的值,
    //  1 先是判斷要攔截的對(duì)象是哪一個(gè)
    //  2 然后根據(jù)方法名稱(chēng)和參數(shù)判斷要對(duì)哪一個(gè)方法進(jìn)行攔截
    //  3 根據(jù)結(jié)果做出決定,是返回一個(gè)對(duì)象呢還是代理對(duì)象
    public static Object wrap(Object target, Interceptor interceptor) {
        Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
        Class<?> type = target.getClass();
        // 這邊就是判斷當(dāng)前的interceptor是否包含在
        Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
        if (interfaces.length > 0) {
            return Proxy.newProxyInstance(
                type.getClassLoader(),
                interfaces,
                new Plugin(target, interceptor, signatureMap));
        }
        //如果不需要代理,則直接返回目標(biāo)對(duì)象本身
        return target;
    }
?
    //....
?
}

setProperties方法

在攔截器中可能需要使用到一些變量參數(shù),并且這個(gè)參數(shù)是可配置的,這個(gè)時(shí)候我們就可以使用這個(gè)方法啦,加載時(shí)機(jī):該方法在 mybatis 加載核心配置文件時(shí)被調(diào)用

  default void setProperties(Properties properties) {
    // NOP
  }

關(guān)于如何使用:

javaConfig方式設(shè)置:

@Bean
public ConfigurationCustomizer configurationCustomizer() {
    return new ConfigurationCustomizer() {
        @Override
        public void customize(org.apache.ibatis.session.Configuration configuration) {
            // 開(kāi)啟駝峰命名映射
            configuration.setMapUnderscoreToCamelCase(true);
            MybatisMetaInterceptor mybatisMetaInterceptor = new MybatisMetaInterceptor();
            Properties properties = new Properties();
            properties.setProperty("param1","javaconfig-value1");
            properties.setProperty("param2","javaconfig-value2");
            mybatisMetaInterceptor.setProperties(properties);
            configuration.addInterceptor(mybatisMetaInterceptor);
        }
    };
}

通過(guò)mybatis-config.xml文件進(jìn)行配置

<configuration>
    <plugins>
        <plugin interceptor="com.nzc.interceptor.MybatisMetaInterceptor">
            <property name="param1" value="value1"/>
            <property name="param2" value="value2"/>
        </plugin>
    </plugins>
</configuration>    

測(cè)試效果就是測(cè)試案例上那般,通過(guò)了解攔截器接口的信息,對(duì)于之前的案例不再是那般模糊啦

接下來(lái)再接著聊一聊攔截器上面那一坨注解信息是用來(lái)干嘛的吧,

注意

當(dāng)配置多個(gè)攔截器時(shí), MyBatis 會(huì)遍歷所有攔截器,按順序執(zhí)行攔截器的 plugin 口方法, 被攔截的對(duì)象就會(huì)被層層代理。

在執(zhí)行攔截對(duì)象的方法時(shí),會(huì)一層層地調(diào)用攔截器,攔截器通 invocation proceed()調(diào)用下層的方法,直到真正的方法被執(zhí)行。

方法執(zhí)行的結(jié)果 從最里面開(kāi)始向外 層層返回,所以如果存在按順序配置的三個(gè)簽名相同的攔截器, MyBaits 會(huì)按照 C>B>A>target.proceed()>A>B>C 的順序執(zhí)行。如果簽名不同, 就會(huì)按照 MyBatis 攔截對(duì)象的邏輯執(zhí)行.

這也是我們最開(kāi)始談到的Mybatis插件模塊所使用的設(shè)計(jì)模式-責(zé)任鏈模式。

四、攔截器注解介紹

上一個(gè)章節(jié),我們只說(shuō)明如何實(shí)現(xiàn)Interceptor接口來(lái)實(shí)現(xiàn)攔截,卻沒(méi)有說(shuō)明要攔截的對(duì)象是誰(shuí),在什么時(shí)候進(jìn)行攔截.就關(guān)系到我們之前編寫(xiě)的注解信息啦.

@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})

這兩個(gè)注解用來(lái)配置攔截器要攔截的接口的方法。

@Intercepts({})注解中是一個(gè)@Signature()數(shù)組,可以在一個(gè)攔截器中同時(shí)攔截不同的接口和方法。

MyBatis 允許在己映射語(yǔ)句執(zhí)行過(guò)程中的某一點(diǎn)進(jìn)行攔截調(diào)用。默認(rèn)情況下, MyBatis 允許使用插件來(lái)攔截的接口包括以下幾個(gè)。

  • Executor
  • ParameterHandler
  • ResultSetHandler
  • StatementHandler

@Signature 注解包含以下三個(gè)屬性。

  • type 設(shè)置攔截接口,可選值是前面提到的4個(gè)接口
  • method 設(shè)置攔截接口中的方法名 可選值是前面4個(gè)接口中所對(duì)應(yīng)的方法,需要和接口匹配
  • args 設(shè)置攔截方法的參數(shù)類(lèi)型數(shù)組 通過(guò)方法名和參數(shù)類(lèi)型可以確定唯一一個(gè)方法

Executor 接口

下面就是Executor接口的類(lèi)信息

public interface Executor {
?
  int update(MappedStatement ms, Object parameter) throws SQLException;
?
  <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
?
  <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
?
  <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
?
  List<BatchResult> flushStatements() throws SQLException;
?
  void commit(boolean required) throws SQLException;
?
  void rollback(boolean required) throws SQLException;
?
  CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
?
  boolean isCached(MappedStatement ms, CacheKey key);
?
  void clearLocalCache();
?
  void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
?
  Transaction getTransaction();
?
  void close(boolean forceRollback);
?
  boolean isClosed();
?
  void setExecutorWrapper(Executor executor);
?
}

我只會(huì)簡(jiǎn)單說(shuō)一些最常用的~

1、update

int update(MappedStatement ms, Object parameter) throws SQLException;

該方法會(huì)在所有的 INSERT、UPDATE、DELETE 執(zhí)行時(shí)被調(diào)用,因此如果想要攔截這類(lèi)操作,可以攔截該方法。接口方法對(duì)應(yīng)的簽名如下。

@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})

2、query

<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
?
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;

該方法會(huì)在所有 SELECT 查詢(xún)方法執(zhí)行時(shí)被調(diào)用 通過(guò)這個(gè)接口參數(shù)可以獲取很多有用的信息,這也是最常被攔截的方法。

@Intercepts({@Signature(
    type = Executor.class,
    method = "query",
    args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
), @Signature(
    type = Executor.class,
    method = "query",
    args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}
)})

3、queryCursor:

  <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;

該方法只有在查詢(xún) 的返回值類(lèi)型為 Cursor 時(shí)被調(diào)用 。接口方法對(duì)應(yīng)的簽名類(lèi)似于之前的。

//該方法只在通過(guò) SqlSession 方法調(diào)用 commit 方法時(shí)才被調(diào)用  
void commit(boolean required) throws SQLException; 
//該方法只在通過(guò) SqlSessio口方法調(diào)用 rollback 方法時(shí)才被調(diào)用
void rollback(boolean required) throws SQLException;
//該方法只在通過(guò) SqlSession 方法獲取數(shù)據(jù)庫(kù)連接時(shí)才被調(diào)用,
Transaction getTransaction();
//該方法只在延遲加載獲取新的 Executor 后才會(huì)被執(zhí)行
void close(boolean forceRollback);
//該方法只在延遲加載執(zhí)行查詢(xún)方法前被執(zhí)行
boolean isClosed();

注解的編寫(xiě)方法都是類(lèi)似的。

ParameterHandler 接口

public interface ParameterHandler {
?
    //該方法只在執(zhí)行存儲(chǔ)過(guò)程處理出參的時(shí)候被調(diào)用
    Object getParameterObject();
    //該方法在所有數(shù)據(jù)庫(kù)方法設(shè)置 SQL 參數(shù)時(shí)被調(diào)用。
    void setParameters(PreparedStatement ps) throws SQLException;
}

我都寫(xiě)一塊啦,如果要攔截某一個(gè)的話(huà)只寫(xiě)一個(gè)即可

@Intercepts({
        @Signature(type = ParameterHandler.class, method = "getParameterObject", args = {}),
        @Signature(type = ParameterHandler.class, method = "setParameters", args = {PreparedStatement.class})
})

ResultSetHandler 接口

public interface ResultSetHandler {
    //該方法會(huì)在除存儲(chǔ)過(guò)程及返回值類(lèi)型為 Cursor 以外的查詢(xún)方法中被調(diào)用。
    <E> List<E> handleResultSets(Statement stmt) throws SQLException;
    //只會(huì)在返回值類(lèi)型為 ursor 查詢(xún)方法中被調(diào)用  
    <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
    //只在使用存儲(chǔ)過(guò)程處理出參時(shí)被調(diào)用 ,
    void handleOutputParameters(CallableStatement cs) throws SQLException;
}
@Intercepts({
        @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class}),
        @Signature(type = ResultSetHandler.class, method = "handleCursorResultSets", args = {Statement.class}),
        @Signature(type = ResultSetHandler.class, method = "handleOutputParameters", args = {CallableStatement.class})
})

StatementHandler 接口

public interface StatementHandler {
    //該方法會(huì)在數(shù)據(jù)庫(kù)執(zhí)行前被調(diào)用 優(yōu)先于當(dāng)前接口中的其他方法而被執(zhí)行
    Statement prepare(Connection connection, Integer transactionTimeout)
        throws SQLException;
    //該方法在 prepare 方法之后執(zhí)行,用于處理參數(shù)信息 
    void parameterize(Statement statement)
        throws SQLException;
    //在全局設(shè)置配置 defaultExecutorType BATCH 時(shí),執(zhí)行數(shù)據(jù)操作才會(huì)調(diào)用該方法
    void batch(Statement statement)
        throws SQLException;
    //執(zhí)行UPDATE、DELETE、INSERT方法時(shí)執(zhí)行
    int update(Statement statement)
        throws SQLException;
    //執(zhí)行 SELECT 方法時(shí)調(diào)用,接口方法對(duì)應(yīng)的簽名如下。
    <E> List<E> query(Statement statement, ResultHandler resultHandler)
        throws SQLException;
?
    <E> Cursor<E> queryCursor(Statement statement)
        throws SQLException;
?
    //獲取實(shí)際的SQL字符串
    BoundSql getBoundSql();
?
    ParameterHandler getParameterHandler();
?
}
@Intercepts({
        @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class,Integer.class}),
        @Signature(type = StatementHandler.class, method = "parameterize", args = {Statement.class}),
        @Signature(type = StatementHandler.class, method = "batch", args = {Statement.class}),
        @Signature(type = StatementHandler.class, method = "update", args = {Statement.class}),
        @Signature(type = StatementHandler.class, method = "query", args = {Statement.class,ResultHandler.class}),
        @Signature(type = StatementHandler.class, method = "queryCursor", args = {Statement.class}),
        @Signature(type = StatementHandler.class, method = "getBoundSql", args = {}),
        @Signature(type = StatementHandler.class, method = "getParameterHandler", args = {})
}

如果有時(shí)間的話(huà),我會(huì)更加建議看了的小伙伴,自己去實(shí)現(xiàn)接口做個(gè)測(cè)試,驗(yàn)證一番,也能了解的更徹底些??磿?huì)了,很多時(shí)候知識(shí)的記憶還是淺的。

五、進(jìn)一步思考

看完這篇文章后,不知道你有沒(méi)有什么收獲。

再次看看這張文章大綱的圖吧

image.png

試著思考思考下面幾個(gè)問(wèn)題:

Mybatis插件適用于哪些場(chǎng)景?回憶一下你做過(guò)的項(xiàng)目,是否有可以使用Mybatis插件來(lái)實(shí)現(xiàn)的呢?你可以編寫(xiě)一個(gè)Mybatis插件了嗎?感興趣的話(huà),你可以試著去了解一下Mybatis分頁(yè)插件的實(shí)現(xiàn)方式。

最后留下一個(gè)遇到的問(wèn)題,也是下一篇文章可能會(huì)寫(xiě)的吧,同時(shí)也使用到了今天所談到了的攔截器。

在項(xiàng)目中,你們都是如何針對(duì)表中某些字段進(jìn)行加解密的呢?

到此這篇關(guān)于Mybatis攔截器實(shí)現(xiàn)自定義需求的文章就介紹到這了,更多相關(guān)Mybatis自定義攔截器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java從控制臺(tái)接收一個(gè)數(shù)字的實(shí)例詳解

    java從控制臺(tái)接收一個(gè)數(shù)字的實(shí)例詳解

    這篇文章主要介紹了java從控制臺(tái)接收一個(gè)數(shù)字的實(shí)例詳解的相關(guān)資料,這里提供實(shí)例代碼,注釋說(shuō)明清晰,需要的朋友可以參考下
    2017-07-07
  • 一篇文章告訴你JAVA Mybatis框架的核心原理到底有多重要

    一篇文章告訴你JAVA Mybatis框架的核心原理到底有多重要

    yBatis的底層操作封裝了JDBC的API,MyBatis的工作原理以及核心流程與JDBC的使用步驟一脈相承,MyBatis的核心對(duì)象(SqlSession,Executor)與JDBC的核心對(duì)象(Connection,Statement)相互對(duì)應(yīng)
    2021-06-06
  • Java?@Scheduled定時(shí)任務(wù)不執(zhí)行解決辦法

    Java?@Scheduled定時(shí)任務(wù)不執(zhí)行解決辦法

    這篇文章主要給大家介紹了關(guān)于Java?@Scheduled定時(shí)任務(wù)不執(zhí)行解決的相關(guān)資料,當(dāng)@Scheduled定時(shí)任務(wù)不執(zhí)行時(shí)可以根據(jù)以下步驟進(jìn)行排查和解決,需要的朋友可以參考下
    2023-10-10
  • java打印指定年月的日歷

    java打印指定年月的日歷

    這篇文章主要為大家詳細(xì)介紹了Java如何打印指定年月的日歷,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-07-07
  • java web實(shí)現(xiàn)自動(dòng)登錄功能

    java web實(shí)現(xiàn)自動(dòng)登錄功能

    這篇文章主要為大家詳細(xì)介紹了java web實(shí)現(xiàn)自動(dòng)登錄功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-10-10
  • JDK動(dòng)態(tài)代理過(guò)程原理及手寫(xiě)實(shí)現(xiàn)詳解

    JDK動(dòng)態(tài)代理過(guò)程原理及手寫(xiě)實(shí)現(xiàn)詳解

    這篇文章主要為大家介紹了JDK動(dòng)態(tài)代理過(guò)程原理及手寫(xiě)實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09
  • Java中關(guān)于char類(lèi)型變量能夠輸出中文的問(wèn)題

    Java中關(guān)于char類(lèi)型變量能夠輸出中文的問(wèn)題

    這篇文章主要介紹了Java中關(guān)于char類(lèi)型變量能夠輸出中文的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • Java全面解析IO流相關(guān)知識(shí)

    Java全面解析IO流相關(guān)知識(shí)

    這篇文章主要介紹了IO流相關(guān)知識(shí),包括File,字節(jié)流,字符流,特殊操作流(標(biāo)準(zhǔn)輸入流,標(biāo)準(zhǔn)輸出流,對(duì)象序列化與反序列化,properties與IO流結(jié)合)相關(guān)知識(shí)的總結(jié)
    2021-08-08
  • Java中防止數(shù)據(jù)重復(fù)提交超簡(jiǎn)單的6種方法

    Java中防止數(shù)據(jù)重復(fù)提交超簡(jiǎn)單的6種方法

    在平時(shí)開(kāi)發(fā)中,如果網(wǎng)速比較慢的情況下,用戶(hù)提交表單后,發(fā)現(xiàn)服務(wù)器半天都沒(méi)有響應(yīng),那么用戶(hù)可能會(huì)以為是自己沒(méi)有提交表單,就會(huì)再點(diǎn)擊提交按鈕重復(fù)提交表單,這篇文章主要給大家介紹了關(guān)于Java中防止數(shù)據(jù)重復(fù)提交超簡(jiǎn)單的6種方法,需要的朋友可以參考下
    2021-11-11
  • SpringBoot Shiro授權(quán)實(shí)現(xiàn)過(guò)程解析

    SpringBoot Shiro授權(quán)實(shí)現(xiàn)過(guò)程解析

    這篇文章主要介紹了SpringBoot Shiro授權(quán)實(shí)現(xiàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11

最新評(píng)論