Jadual Kandungan
介绍
使用场景
Interceptor拦截器
InterceptorChain拦截器链
拦截方法
注解
Intercepts
Signature
示例
步骤
入门使用
动态给属性赋值
打印SQL
Rumah Java javaTutorial Bagaimana untuk membangunkan pemalam tersuai dalam Java Mybatis

Bagaimana untuk membangunkan pemalam tersuai dalam Java Mybatis

May 09, 2023 pm 03:10 PM
java mybatis

这篇文章主要介绍“Java mybatis怎么开发自定义插件”,在日常操作中,相信很多人在Java mybatis怎么开发自定义插件问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java mybatis怎么开发自定义插件”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

介绍

MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。比如执行前、执行后或者对SQL结果集处理、sql入参处理等,这样就可以在不修改mybatis源码的情况下对sql执行的过程或结果进行修改,实现了解耦。mybatis 是在动态代理的基础上实现的。

使用场景

如果业务中需要设置一些通用数据库操作,比如创建时间、创建人等通用字段又或者是分页操作等,这类都可以使用插件开发方式,PageHelper就是基于Interceptor的一个mybatis插件。

Interceptor拦截器

public interface Interceptor {

  /**
   * 子类拦截器必须要实现的方法,
   * 在该方法对内自定义拦截逻辑
   * @param invocation
   * @return
   * @throws Throwable
   */
  Object intercept(Invocation invocation) throws Throwable;

  /**
   生成目标类的代理对象
   * 也可以根据需求不返回代理对象,这种情况下这个拦截器将不起作用
   * 无特殊情况使用默认的即可
   * @param target
   * @return
   */
  default Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }

  /**
   * 设置变量
   * 在注册拦截器的时候设置变量,在这里可以获取到
   * @param properties
   */
  default void setProperties(Properties properties) {
    // NOP
  }

}
Salin selepas log masuk

InterceptorChain拦截器链

在org.apache.ibatis.plugin包下有个InterceptorChain类,该类有个interceptors属性,所有实现了Interceptor接口的拦截器都会被存储到interceptors中。

源码如下:

public class InterceptorChain {

  private final List<Interceptor> interceptors = new ArrayList<>();
  /**
   *  让目标类在所有的拦截器中生成代理对象,并返回代理对象
   * @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);
  }

}
Salin selepas log masuk

拦截方法

默认情况下,MyBatis 允许使用插件来拦截Executor 、ParameterHandler 、ResultSetHandler 、StatementHandler 接口下面的方法。如果系统中有配置自定义插件,默认情况下,系统会把上面四个类的默认子类都作为目标类来让所有的拦截器进行拦截, 以保证所有的拦截器都能对Executor 、ParameterHandler 、ResultSetHandler 、StatementHandler子类进行拦截。

源码如下: 在org.apache.ibatis.session.Configuration类中

public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
    // 使用拦截器进行拦截
    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);
    // 使用拦截器进行拦截
    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);
    // 使用拦截器进行拦截
    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);
    }
    // 使用拦截器进行拦截
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }
Salin selepas log masuk

注解

Intercepts

Intercepts的作用是拦截Signature注解数组中指定的类的方法。

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

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.
   * 要拦截的类的方法的参数列表
   * @return java types for method argument
   */
  Class<?>[] args();
}
Salin selepas log masuk

示例

步骤

  • 1、实现org.apache.ibatis.plugin.Interceptor接口

  • 2、添加Intercepts和Signature注解

  • 3、根据需求实现Interceptor方法逻辑

入门使用

这里会写两个使用示例,一个是动态给属性赋值,一个是打印SQL。

表结构:

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 &#39;创建日期&#39;,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
Salin selepas log masuk

实体类:

public class UserInfo {
    private Long id;

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

动态给属性赋值

在创建表时,有些是每个表都有的参数,比如创建时间、修改时间等,这类参数如果在每个类进行保存或修改的时候都进行设值的话就有点重复操作了,所以可以通过mybatis插件进行处理。

1、Interceptor 实现类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();
        //调用createDate 写方法
        writeMethod.invoke(parameter , new Date());

        return executor.update(mappedStatement, parameter);
    }

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

    /**
     * 设置变量
     *
     * @param properties
     */
    @Override
    public void setProperties(Properties properties) {
        this.properties = properties;
    }
}
Salin selepas log masuk

2、mybatis配置文件中注册InsertInterceptor

<plugins>
    <plugin interceptor="plugin.PrintSqlPlugin"/>
    <plugin interceptor="plugin.InsertInterceptor">
      <property name="createDate" value="createDate"/>
    </plugin>
  </plugins>
Salin selepas log masuk

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();
  }

}
Salin selepas log masuk

查看数据库,可以看到在没有给createDate属性收到赋值的情况下,通过拦截器进行赋值,最后是保存到数据库中了。

Bagaimana untuk membangunkan pemalam tersuai dalam Java Mybatis

打印SQL

Interceptor 实现类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();
        //方法参数
        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);

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

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

    @Override
    public void setProperties(Properties properties) {

    }

    /**
     * 获取参数
     * @param obj
     * @return
     */
    private static String getParameterValue(Object obj) {
        String value = null;
        if (obj instanceof String) {
            value = "&#39;" + obj.toString() + "&#39;";
            value = value.replaceAll("\\\\", "\\\\\\\\");
            value = value.replaceAll("\\$", "\\\\\\$");
        } else if (obj instanceof Date) {
            DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
            value = "&#39;" + formatter.format(obj) + "&#39;";
        } 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;
    }
}
Salin selepas log masuk

使用同样的方法进行测试,查看控制台打印结果:

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')

Atas ialah kandungan terperinci Bagaimana untuk membangunkan pemalam tersuai dalam Java Mybatis. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn

Tag artikel panas

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Akar Kuasa Dua di Jawa Akar Kuasa Dua di Jawa Aug 30, 2024 pm 04:26 PM

Akar Kuasa Dua di Jawa

Nombor Sempurna di Jawa Nombor Sempurna di Jawa Aug 30, 2024 pm 04:28 PM

Nombor Sempurna di Jawa

Penjana Nombor Rawak di Jawa Penjana Nombor Rawak di Jawa Aug 30, 2024 pm 04:27 PM

Penjana Nombor Rawak di Jawa

Nombor Armstrong di Jawa Nombor Armstrong di Jawa Aug 30, 2024 pm 04:26 PM

Nombor Armstrong di Jawa

Weka di Jawa Weka di Jawa Aug 30, 2024 pm 04:28 PM

Weka di Jawa

Nombor Smith di Jawa Nombor Smith di Jawa Aug 30, 2024 pm 04:28 PM

Nombor Smith di Jawa

Soalan Temuduga Java Spring Soalan Temuduga Java Spring Aug 30, 2024 pm 04:29 PM

Soalan Temuduga Java Spring

Cuti atau kembali dari Java 8 Stream Foreach? Cuti atau kembali dari Java 8 Stream Foreach? Feb 07, 2025 pm 12:09 PM

Cuti atau kembali dari Java 8 Stream Foreach?

See all articles