溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Java?mybatis怎么開發(fā)自定義插件

發(fā)布時間:2022-08-17 16:46:50 來源:億速云 閱讀:169 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要介紹“Java mybatis怎么開發(fā)自定義插件”,在日常操作中,相信很多人在Java mybatis怎么開發(fā)自定義插件問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Java mybatis怎么開發(fā)自定義插件”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

介紹

MyBatis 允許你在映射語句執(zhí)行過程中的某一點進(jìn)行攔截調(diào)用。比如執(zhí)行前、執(zhí)行后或者對SQL結(jié)果集處理、sql入?yún)⑻幚淼?,這樣就可以在不修改mybatis源碼的情況下對sql執(zhí)行的過程或結(jié)果進(jìn)行修改,實現(xiàn)了解耦。mybatis 是在動態(tài)代理的基礎(chǔ)上實現(xiàn)的。

使用場景

如果業(yè)務(wù)中需要設(shè)置一些通用數(shù)據(jù)庫操作,比如創(chuàng)建時間、創(chuàng)建人等通用字段又或者是分頁操作等,這類都可以使用插件開發(fā)方式,PageHelper就是基于Interceptor的一個mybatis插件。

Interceptor攔截器

public interface Interceptor {

  /**
   * 子類攔截器必須要實現(xiàn)的方法,
   * 在該方法對內(nèi)自定義攔截邏輯
   * @param invocation
   * @return
   * @throws Throwable
   */
  Object intercept(Invocation invocation) throws Throwable;

  /**
   生成目標(biāo)類的代理對象
   * 也可以根據(jù)需求不返回代理對象,這種情況下這個攔截器將不起作用
   * 無特殊情況使用默認(rèn)的即可
   * @param target
   * @return
   */
  default Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }

  /**
   * 設(shè)置變量
   * 在注冊攔截器的時候設(shè)置變量,在這里可以獲取到
   * @param properties
   */
  default void setProperties(Properties properties) {
    // NOP
  }

}

InterceptorChain攔截器鏈

在org.apache.ibatis.plugin包下有個InterceptorChain類,該類有個interceptors屬性,所有實現(xiàn)了Interceptor接口的攔截器都會被存儲到interceptors中。

源碼如下:

public class InterceptorChain {

  private final List<Interceptor> interceptors = new ArrayList<>();
  /**
   *  讓目標(biāo)類在所有的攔截器中生成代理對象,并返回代理對象
   * @param target
   * @return
   */
  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }

  /**
   * 添加過濾器
   * @param interceptor
   */
  public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
  }

  public List<Interceptor> getInterceptors() {
    return Collections.unmodifiableList(interceptors);
  }

}

攔截方法

默認(rèn)情況下,MyBatis 允許使用插件來攔截Executor 、ParameterHandler 、ResultSetHandler 、StatementHandler 接口下面的方法。如果系統(tǒng)中有配置自定義插件,默認(rèn)情況下,系統(tǒng)會把上面四個類的默認(rèn)子類都作為目標(biāo)類來讓所有的攔截器進(jìn)行攔截, 以保證所有的攔截器都能對Executor 、ParameterHandler 、ResultSetHandler 、StatementHandler子類進(jìn)行攔截。

源碼如下: 在org.apache.ibatis.session.Configuration類中

public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
    // 使用攔截器進(jìn)行攔截
    parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
    return parameterHandler;
  }

  public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
      ResultHandler resultHandler, BoundSql boundSql) {
    ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
    // 使用攔截器進(jìn)行攔截
    resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
    return resultSetHandler;
  }

  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    // 使用攔截器進(jìn)行攔截
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }

  public Executor newExecutor(Transaction transaction) {
    return newExecutor(transaction, defaultExecutorType);
  }

  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    // 使用攔截器進(jìn)行攔截
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

注解

Intercepts

Intercepts的作用是攔截Signature注解數(shù)組中指定的類的方法。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Intercepts {
  /**
   * Returns method signatures to intercept.
   * Signature注解列表
   * @return method signatures
   */
  Signature[] value();
}

Signature

Signature注解作用是攔截指定類的方法。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Signature {
  /**
   * Returns the java type.
   * 要攔截的類
   * @return the java type
   */
  Class<?> type();

  /**
   * Returns the method name.
   * 要攔截的類的方法
   * @return the method name
   */
  String method();

  /**
   * Returns java types for method argument.
   * 要攔截的類的方法的參數(shù)列表
   * @return java types for method argument
   */
  Class<?>[] args();
}

示例

步驟

  • 1、實現(xiàn)org.apache.ibatis.plugin.Interceptor接口

  • 2、添加Intercepts和Signature注解

  • 3、根據(jù)需求實現(xiàn)Interceptor方法邏輯

入門使用

這里會寫兩個使用示例,一個是動態(tài)給屬性賦值,一個是打印SQL。

表結(jié)構(gòu):

CREATE TABLE `users` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `gender` varchar(20) DEFAULT NULL,
  `userName` text NOT NULL,
  `create_date` datetime DEFAULT NULL COMMENT '創(chuàng)建日期',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

實體類:

public class UserInfo {
    private Long id;

    private String gender;
    private String userName;
    private Date createDate;
    // 省略get、set方法
}

動態(tài)給屬性賦值

在創(chuàng)建表時,有些是每個表都有的參數(shù),比如創(chuàng)建時間、修改時間等,這類參數(shù)如果在每個類進(jìn)行保存或修改的時候都進(jìn)行設(shè)值的話就有點重復(fù)操作了,所以可以通過mybatis插件進(jìn)行處理。

1、Interceptor 實現(xiàn)類InsertInterceptor:

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

    private Properties properties;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        final Object[] args = invocation.getArgs();

        MappedStatement mappedStatement= (MappedStatement) args[0];
        Object parameter = args[1];

        Executor executor = (Executor) invocation.getTarget();
        final Class<?> parameterClass = parameter.getClass();

        final String createDate = properties.getProperty("createDate");

        //獲取createDate 屬性描述器
        final PropertyDescriptor propertyDescriptor = new PropertyDescriptor(createDate , parameterClass);
        //獲取createDate 寫方法
        final Method writeMethod = propertyDescriptor.getWriteMethod();
        //調(diào)用createDate 寫方法
        writeMethod.invoke(parameter , new Date());

        return executor.update(mappedStatement, parameter);
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target , this);
    }

    /**
     * 設(shè)置變量
     *
     * @param properties
     */
    @Override
    public void setProperties(Properties properties) {
        this.properties = properties;
    }
}

2、mybatis配置文件中注冊InsertInterceptor

<plugins>
    <plugin interceptor="plugin.PrintSqlPlugin"/>
    <plugin interceptor="plugin.InsertInterceptor">
      <property name="createDate" value="createDate"/>
    </plugin>
  </plugins>

3、測試

public class UserTest {

  private final static SqlSessionFactory sqlSessionFactory;
  static {
    String resource = "mybatis-config.xml";
    Reader reader = null;
    try {
      reader = Resources.getResourceAsReader(resource);
    } catch (IOException e) {
      System.out.println(e.getMessage());
    }
    sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
  }

  @Test
  public void insert(){
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserInfo userInfo = new UserInfo();
    userInfo.setUserName("test1");
    userInfo.setGender("male");
    UserInfoMapper mapper = sqlSession.getMapper(UserInfoMapper.class);
    mapper.insertUser(userInfo);
    sqlSession.commit();
    sqlSession.close();
  }

}

查看數(shù)據(jù)庫,可以看到在沒有給createDate屬性收到賦值的情況下,通過攔截器進(jìn)行賦值,最后是保存到數(shù)據(jù)庫中了。

Java?mybatis怎么開發(fā)自定義插件

打印SQL

Interceptor 實現(xiàn)類PrintSqlPlugin:

@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}),
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class PrintSqlPlugin implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //被代理對象
        Object target = invocation.getTarget();
        //代理方法
        Method method = invocation.getMethod();
        //方法參數(shù)
        Object[] args = invocation.getArgs();

        MappedStatement mappedStatement= (MappedStatement) args[0];
        Object parameter = args[1];

        final BoundSql mappedStatementBoundSql = mappedStatement.getBoundSql(parameter);
        System.err.println("BoundSql="+mappedStatementBoundSql.getSql());

        final Configuration configuration = mappedStatement.getConfiguration();

        final String showSql = showSql(configuration, mappedStatementBoundSql);
        System.err.println("sql="+showSql);

        //方法執(zhí)行
        final Object returnValue = invocation.proceed();
        return returnValue;
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }

    /**
     * 獲取參數(shù)
     * @param obj
     * @return
     */
    private static String getParameterValue(Object obj) {
        String value = null;
        if (obj instanceof String) {
            value = "'" + obj.toString() + "'";
            value = value.replaceAll("\\\\", "\\\\\\\\");
            value = value.replaceAll("\\$", "\\\\\\$");
        } else if (obj instanceof Date) {
            DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
            value = "'" + formatter.format(obj) + "'";
        } else {
            if (obj != null) {
                value = obj.toString();
            } else {
                value = "";
            }

        }
        return value;
    }

    /**
     * 打印SQL
     * @param configuration
     * @param boundSql
     * @return
     */
    public static String showSql(Configuration configuration, BoundSql boundSql) {
        Object parameterObject = boundSql.getParameterObject();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
        if (parameterMappings.size() > 0 && parameterObject != null) {
            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
            if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                sql = sql.replaceFirst("\\?", getParameterValue(parameterObject));

            } else {
                MetaObject metaObject = configuration.newMetaObject(parameterObject);
                for (ParameterMapping parameterMapping : parameterMappings) {
                    String propertyName = parameterMapping.getProperty();
                    if (metaObject.hasGetter(propertyName)) {
                        Object obj = metaObject.getValue(propertyName);
                        sql = sql.replaceFirst("\\?", getParameterValue(obj));
                    } else if (boundSql.hasAdditionalParameter(propertyName)) {
                        Object obj = boundSql.getAdditionalParameter(propertyName);
                        sql = sql.replaceFirst("\\?", getParameterValue(obj));
                    }
                }
            }
        }
        return sql;
    }
}

使用同樣的方法進(jìn)行測試,查看控制臺打印結(jié)果:

BoundSql=insert into users (gender,  userName ,create_date) values(? , ?, ?)
sql=insert into users (gender, userName ,create_date) values('male' , 'test2', '2022-1-14 18:40:08')

到此,關(guān)于“Java mybatis怎么開發(fā)自定義插件”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI