首頁>技術>

本文主要內容:

大多數框架都支援外掛,使用者可透過編寫外掛來自行擴充套件功能,Mybatis也不例外。

在Mybatis中最出名的就是PageHelper 分頁外掛,下面我們先來使用一下這個分頁外掛。

如何整合分頁外掛

Spring-Boot+Mybatis+PageHelper 。

引入pom依賴

<dependency>   <groupId>com.github.pagehelper</groupId>   <artifactId>pagehelper-spring-boot-starter</artifactId>   <version>1.2.3</version></dependency>

配置分頁外掛配置項

pagehelper:  helperDialect: mysql  reasonable: true  supportMethodsArguments: true  params: count=countSql

service介面程式碼中

PageInfo selectUsersByName(int pageIndex, int pageSize);

service實現類程式碼中

@Overridepublic PageInfo selectUsersByName(int pageIndex, int pageSize) {    PageHelper.startPage(pageIndex, pageSize);    List<User> users = userMapper.selectUsersByName(null);    return new PageInfo(users);}

Mapper程式碼程式碼

<select id="selectUsersByName" resultMap="User">    select * from m_user    <where>        <if test="userName != null and userName != ''">            `name` = #{userName}        </if>    </where></select>
List<User> selectUsersByName(@Param("userName") String userName);

controller中程式碼

@GetMapping("/user/name")public PageInfo selectUsersByName(int pageIndex, int pageSize) {    return userService.selectUsersByName(pageIndex, pageSize);}

然後我們訪問

http://localhost:9002/user/name?pageIndex=1&pageSize=10

輸出結果:

輸出重要項說明:

pageNum:當前頁碼。pageSize:每頁數。list:就是我們返回的業務資料。total:總資料。hasNextPage:是否存在下一頁。

我們在看看輸出SQL:

發現其實執行了兩條SQL:count和limit。

猜測分頁外掛實現

1.這個分頁外掛無非就是在我們的查詢條件上拼接了個limit和做了一個count查詢。

2.我們這裡使用的是Mysql作為資料庫,如果是Oracle的話那就不是limit了,所以這裡有多種資料庫對應的方案。

3.在沒有此外掛的前面攔截並做了sql和相關處理。

根據官網快速入門外掛

下面是來自官網的一段話:

MyBatis 允許你在對映語句執行過程中的某一點進行攔截呼叫。預設情況下,MyBatis 允許使用外掛來攔截的方法呼叫包括:

Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)

ParameterHandler (getParameterObject, setParameters)

ResultSetHandler (handleResultSets, handleOutputParameters)

StatementHandler (prepare, parameterize, batch, update, query)

這些類中方法的細節可以透過檢視每個方法的簽名來發現,或者直接檢視 MyBatis 發行包中的原始碼。如果你想做的不僅僅是監控方法的呼叫,那麼你最好相當瞭解要重寫的方法的行為。因為在試圖修改或重寫已有方法的行為時,很可能會破壞 MyBatis 的核心模組。這些都是更底層的類和方法,所以使用外掛的時候要特別當心。

透過 MyBatis 提供的強大機制,使用外掛是非常簡單的,只需實現 Interceptor 介面,並指定想要攔截的方法簽名即可。

那我們就嘗試著按照官方來寫一個外掛。

自定義外掛
@Intercepts({@Signature(        type= Executor.class,        method = "update",        args = {MappedStatement.class,Object.class})})public class TianPlugin implements Interceptor {    private Properties properties = new Properties();    @Override    public Object intercept(Invocation invocation) throws Throwable {        System.out.println("老田寫的一個Mybatis外掛--start");        Object returnObject = invocation.proceed();        System.out.println("老田寫的一個Mybatis外掛---end");        return returnObject;    }}

然後把外掛類注入到容器中。

這裡的自定義完全是官網給出的案例。從自定義的外掛類中看到有個update,我們猜測肯定是需要執行update才會被攔截到。

訪問前面的程式碼:http://localhost:9002/updateUser

成功了。

這時大家肯定會聯想到我們剛剛開始學動態代理的時候,不就是在要呼叫的方法的前面和後面做點小東東嗎?

Mybatis的外掛確實就是這樣的。

我們來分析一下官方的那段話和我們自定義的外掛。

分析

首先,我們自定義的外掛必須是針對下面這四個類以及方法。

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

其次,我們必須實現Mybatis的Interceptor。

Interceptor中三個方法的作用:

intercept():執行攔截內容的地方,比如:在呼叫某類方法前後做一些自己的處理,簡單就是列印日誌。plugin():決定是否觸發intercept()方法。setProperties():給自定義的攔截器傳遞我們配置的屬性引數(這個可以暫時不管他,後面我們寫一個相對完整點的外掛,你就明白是幹啥的了)。plugin方法
default Object plugin(Object target) {    return Plugin.wrap(target, this);  }

預設實現方法,裡面呼叫了Plugin.wrap()方法。

public class Plugin implements InvocationHandler {  private Object target;  private Interceptor interceptor;  private Map<Class<?>, Set<Method>> signatureMap;  private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {    this.target = target;    this.interceptor = interceptor;    this.signatureMap = signatureMap;  }  public static Object wrap(Object target, Interceptor interceptor) {    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);    Class<?> type = target.getClass();    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);    if (interfaces.length > 0) {      // 建立JDK動態代理物件      return Proxy.newProxyInstance(          type.getClassLoader(),          interfaces,          new Plugin(target, interceptor, signatureMap));    }    return target;  }  @Override  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {    try {      Set<Method> methods = signatureMap.get(method.getDeclaringClass());      // 判斷是否是需要攔截的方法(很重要)      if (methods != null && methods.contains(method)) {        // 回撥intercept()方法        return interceptor.intercept(new Invocation(target, method, args));      }      return method.invoke(target, args);    } catch (Exception e) {      throw ExceptionUtil.unwrapThrowable(e);    }  }//...省略其他不相關程式碼}

這不就是一個JDK動態代理嗎?

Map<Class<?>, Set> signatureMap:快取需攔截物件的反射結果,避免多次反射,即target的反射結果。

所以,我們不要動不動就說反射效能很差,那是因為你沒有像Mybatis一樣去快取一個物件的反射結果。

判斷是否是需要攔截的方法,這句註釋很重要,一旦忽略了,都不知道Mybatis是怎麼判斷是否執行攔截內容的,要記住。

Plugin.wrap(target, this)是幹什麼的?

使用JDK的動態代理,給target物件建立一個delegate代理物件,以此來實現方法攔截和增強功能,它會回撥intercept()方法。

為什麼要寫註解?註解都是什麼含義?

在我們自定義的外掛上有一堆註解,別害怕。

Mybatis規定外掛必須編寫Annotation註解,是必須,而不是可選。

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

@Intercepts註解:裝載一個@Signature列表,一個@Signature其實就是一個需要攔截的方法封裝。那麼,一個攔截器要攔截多個方法,自然就是一個@Signature列表。

type= Executor.class, method = "update",args = {MappedStatement.class,Object.class}

解釋:要攔截Executor介面內的query()方法,引數型別為args列表。

那如果想攔截多個方法呢?

@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface Intercepts {  Signature[] value();}

這就簡單了吧,我們在@Intercepts註解中可以存放多個@Signature註解。

比如說前面分頁外掛中就是攔截多個方法的。

為什麼攔截兩個都是query方法呢?因為在Executor中有兩個query方法。

總結下:

Mybatis規定必須使用@Intercepts註解。

@Intercepts註解內可以新增多個類多個方法,注意方法名和引數型別個數一定要對應起來。

21
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • Cell | 單羧酸轉運複合物的質子偶聯轉運機制等