mybatis拦截器和mybatis plus的拦截器

MyBatis拦截器和MyBatis Plus的拦截器在概念上是一致的,都是通过拦截器机制对MyBatis的SQL执行过程进行扩展和控制,但是在实现细节和功能上有所差异。MyBatis Plus的拦截器是建立在MyBatis拦截器基础之上,通过封装简化了开发流程,同时也针对MyBatis Plus的特性和功能进行了强化和扩展。在MyBatis Plus项目中使用拦截器时,既可以利用MyBatis Plus内置的拦截器,也能自定义符合自己业务需求的拦截器。

  1. MyBatis拦截器

    • MyBatis自带的拦截器机制允许开发者自定义拦截器,实现org.apache.ibatis.plugin.Interceptor接口,并通过@Intercepts注解来标识要拦截的方法签名。这些拦截器可以在Executor、ParameterHandler、ResultSetHandler和StatementHandler四个核心接口的方法上进行拦截,从而在SQL执行的不同阶段插入自定义逻辑,如日志记录、性能分析、权限控制等。
  2. MyBatis Plus拦截器

    • MyBatis Plus作为MyBatis的增强工具,也提供了拦截器功能,但它的拦截器通常是基于MyBatis拦截器机制封装的,并针对MyBatis Plus自身的特性进行了扩展和优化。例如,MyBatis Plus内置了一些特定功能的拦截器,如分页插件、性能分析插件等,这些都是针对MyBatis Plus的使用场景设计的。
    • MyBatis Plus的拦截器使用起来相对更为便捷,因为它提供了一些开箱即用的拦截器,同时用户也可以自定义拦截器以满足个性化需求,这部分的实现仍然是基于MyBatis拦截器体系,只是MyBatis Plus对这一部分进行了良好的封装和简化。

MyBatis Inteceptor是使用JDK的动态代理来实现的,所以它只能对接口进行拦截
里面两个很重要的注解是:@Intercepts、@Signature
@Intercepts : 标记要拦截的方法签名
@Signature : 方法签名,唯一的标记一个接口的方法 

MyBatis所有的代理拦截都是通过 InterceptorChain.pluginAll(Object target) 来实现的,会对如下四个对象进行拦截:

1.Executor : 作用是执行SQL语句(所有的sql),并且对事务、缓存等提供统一接口,它负责处理 SQL 语句的编译、参数设置以及结果集的映射等工作。(在这一层上做拦截的权限会更大),所有与数据库的实际交互操作都是通过 Executor 接口及其实现类来完成的。在 MyBatis 中,默认的 Executor 执行器是 SimpleExecutorSimpleExecutor 是最基础的执行器,它的特点是每次执行 SQL 都会创建一个新的 PreparedStatement 对象,然后执行 SQL 并关闭 PreparedStatement

以下是 Executor 接口在 MyBatis 中扮演的关键角色:

  1. SQL 执行: Executor 接口提供了执行 SQL 语句的方法,这些方法接收已解析的 MappedStatement 对象作为参数,后者封装了 SQL 语句以及相关的参数映射和结果映射信息。

  2. 事务管理: Executor 通常与事务管理紧密结合,确保在执行 SQL 时能够参与到当前的数据库事务中,并根据配置执行提交或回滚。

  3. 缓存控制: Executor 也参与 MyBatis 的二级缓存机制,负责缓存数据的读取和刷新。

  4. 执行策略: MyBatis 提供了多种 Executor 的实现,如 SimpleExecutorReuseExecutor 和 BatchExecutor 等,每种执行器都有其特定的行为特点:

    • SimpleExecutor:每次执行 SQL 语句都会打开一个新的 Statement。
    • ReuseExecutor:复用已存在的 Statement 对象,以减少创建 Statement 的开销。
    • BatchExecutor:针对批量操作优化,允许在一次连接中执行一组 SQL 语句,并行发出多个更新操作。

在 MyBatis 的配置文件中,可以通过 <settings> 标签下的 defaultExecutorType 属性来设置默认的执行器类型。同时,也可以在具体的 Mapper 配置中覆盖这一默认设置。

下面是Executor接口的定义:

public interface Executor {
    ResultHandler NO_RESULT_HANDLER = null;

    int update(MappedStatement var1, Object var2) throws SQLException;

    <E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4, CacheKey var5, BoundSql var6) throws SQLException;

    <E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4) throws SQLException;

    <E> Cursor<E> queryCursor(MappedStatement var1, Object var2, RowBounds var3) throws SQLException;

    List<BatchResult> flushStatements() throws SQLException;

    void commit(boolean var1) throws SQLException;

    void rollback(boolean var1) throws SQLException;

    CacheKey createCacheKey(MappedStatement var1, Object var2, RowBounds var3, BoundSql var4);

    boolean isCached(MappedStatement var1, CacheKey var2);

    void clearLocalCache();

    void deferLoad(MappedStatement var1, MetaObject var2, String var3, CacheKey var4, Class<?> var5);

    Transaction getTransaction();

    void close(boolean var1);

    boolean isClosed();

    void setExecutorWrapper(Executor var1);
}

2.StatementHandler : 作用是对 statement 进行预处理,并且提供统一的原子的增、删、改、查接口。(如果要在SQL执行前进行拦截的话,拦截这里就可以了),在 MyBatis 的执行链路中,StatementHandler 起到了承上启下的作用,它介于 SqlSession 和 JDBC Statement 之间。

  1. SQL 解析与动态 SQL 结合: 根据 Mapper 映射文件中的 SQL 语句以及参数对象,解析得到最终的可执行 SQL 语句。对于动态 SQL(如 <if><foreach> 等标签),StatementHandler 会负责将这些标签解析为实际的 SQL 片段。

  2. 参数设置: 将用户传入的方法参数,按照一定的规则绑定到 JDBC Statement 对象上,比如设置 PreparedStatement 的参数值。

  3. 结果集映射: 根据 Mapper 映射文件中的 resultMap 配置,将 JDBC ResultSet 映射为 Java 对象,便于上层应用处理。

  4. 执行 SQL 语句: 最终执行 JDBC Statement,包括执行查询、更新等操作。

StatementHandler负责将最终的 SQL 语句和参数传递给 JDBC PreparedStatement 或 Statement。上年龄的老程序员上学的时候肯定都写过那种调用原生 JDBC API 操作数据库的代码,自己写sql,提交或者回滚,关闭数据库连接之类的,相比现在繁琐了很多。

  1. Statement:

    • Statement 是最基本的用于执行静态 SQL 语句的对象,可以发送简单的 SQL 查询到数据库。
    • 编写 SQL 语句时直接包含变量值,例如 String sql = "SELECT * FROM users WHERE id = 1";
    • 如果同一段 SQL 语句需要反复执行多次且每次参数不同,使用 Statement 会导致 SQL 注入的风险增加。
    • 不支持预编译,对于每次执行,数据库都需要解析和优化 SQL 语句,效率相对较低。
  2. PreparedStatement:

    • PreparedStatement 对象能够存储并执行预编译的 SQL 语句,它可以接受参数化输入。
    • 参数化的 SQL 语句带有占位符(通常以问号 '?' 表示),在执行时传入具体的值,例如 String sql = "SELECT * FROM users WHERE id = ?";
    • 预编译 SQL 可以提高性能,特别是当 SQL 语句被重复执行时,因为数据库只需要解析一次 SQL,后续只需替换参数即可执行,减少了解析和编译的时间。
    • PreparedStatement 可有效防止 SQL 注入攻击,因为它会确保传入的数据被正确转义并作为单独的数据项处理,而不是当作 SQL 代码的一部分。
    • 对于批量操作,PreparedStatement 更加灵活和高效,可以通过循环设置参数然后一次性执行多个插入、更新等操作。

工作后用了hibernate或者是mybatis感觉喷喷香,因为这些框架都帮我们做了。MyBatis 默认情况下使用的是 PreparedStatement。在 MyBatis 中,当你在映射文件中使用参数占位符(如 #{})时,MyBatis 会自动帮你创建并使用 PreparedStatement 来执行 SQL 语句。通过这种方式,MyBatis 能够防止 SQL 注入攻击,并且充分利用 PreparedStatement 的预编译特性,提高 SQL 语句的执行效率。

当然MyBatis 也允许你选择使用 Statement,但这通常不建议,尤其是在处理含有用户输入的 SQL 查询时,以避免安全风险。如果确实需要使用非预编译的 SQL,可以通过 MyBatis 的 ${} 占位符来引用变量,但这将不会享受到预编译带来的安全性和性能优势。不过请注意,最佳实践是始终使用预编译语句以确保安全性。

3.ResultSetHandler : 作用是对返回结果ResultSet进行处理。通常不会直接实现 ResultSetHandler 接口,因为 MyBatis 已经提供了一个默认的实现类 DefaultResultSetHandler,并且在大多数情况下,我们可以通过编写 resultMap 或者直接使用注解来完成结果集到 Java 对象的映射。它主要负责将从数据库查询得到的结果集(ResultSet)转换成 Java 对象。具体功能如下:

  1. 结果集解析: 当 SQL 查询被执行并返回一个 ResultSet 时,ResultSetHandler 会遍历 ResultSet 中的每一行数据。

  2. 对象映射: 根据 MyBatis 的映射配置(如 resultMap 或通过注解标注的映射关系),将每一行的列数据映射到对应的 Java 对象的属性上。

  3. 类型转换: 在映射过程中,ResultSetHandler 会与 MyBatis 的 TypeHandler 一起工作,将数据库中的原始数据类型转换为 Java 类型。

  4. 集合构建: 如果查询结果需要转换为一个集合(如 List、Set 等),ResultSetHandler 会将一个个映射好的 Java 对象添加到集合中。

  5. 嵌套结果映射: 对于复杂的查询结果,ResultSetHandler 还能处理嵌套结果映射,即将多个表关联查询的结果映射到嵌套的 Java 对象结构中。


4.PameterHandler : 作用是对参数进行赋值。它是 MyBatis 框架中的一个重要组件,它的主要作用是处理 SQL 执行时的参数绑定。在 MyBatis 中,当我们调用 Mapper 方法执行 SQL 时,传递给该方法的参数需要经过 ParameterHandler 处理后才能正确绑定到预编译的 SQL 语句中。

具体来说,ParameterHandler 的功能包括:

  1. 类型转换: 将方法参数从 Java 类型转换为 JDBC 可识别的类型。例如,将 Java 日期类型转换为符合数据库日期格式的 java.sql.Date 或 java.sql.Timestamp 类型。

  2. 参数设置: 根据 SQL 语句中占位符(#{...} 或 ${...})的位置和数量,将转换后的参数值设置到 PreparedStatement 中。

  3. 动态 SQL 处理: 对于包含动态 SQL 片段的语句,ParameterHandler 负责解析条件表达式,并根据表达式的值决定是否将参数值插入到 SQL 语句中。

那这四大金刚彼此之间有关系吗?肯定是有的,Executor负责实际执行 SQL 语句并管理事务,Executor 接口中定义了一系列的方法,这些方法用来执行 MappedStatement 中的 SQL 以及相关操作,包括 CRUD(增删改查)以及其他数据库操作。StatementHandler负责将最终的 SQL 语句和参数传递给 JDBC PreparedStatement 或 Statement,并在结果返回时进行处理。StatementHandler 在执行 SQL 之前会对 SQL 语句进行动态处理(如动态 SQL),之后将参数通过 ParameterHandler 绑定到 SQL 语句中,并在执行后通过 ResultSetHandler 处理结果集。

大致了解了这些概念,再看Interceptor接口:

package org.apache.ibatis.plugin;

import java.util.Properties;

public interface Interceptor {
    Object intercept(Invocation var1) throws Throwable;
    //自Java 8起,接口中允许包含具有具体实现的方法,这些方法被称为“默认方法”。在接口定义中使用default关键字修饰的方法不需要实现类去实现它,但实现类可以选择覆盖(override)这个方法。这样做的目的是在不破坏现有实现类的情况下向接口添加新的方法,提高接口的扩展性。
    default Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    default void setProperties(Properties properties) {
    }
}

我们看一下mybatis为我们提供的Interceptor实现类。

比如PaginationInterceptor,它就是基于Interceptor实现的一个强大的分页插件,可以看出它对statement 进行预处理,作用于prepare阶段。

@Intercepts({@Signature(
    type = StatementHandler.class,
    method = "prepare",
    args = {Connection.class, Integer.class}
)})
public class PaginationInterceptor extends AbstractSqlParserHandler implements Interceptor 

如下是StatementHandler能做的操作:

public interface StatementHandler {
    Statement prepare(Connection var1, Integer var2) throws SQLException;

    void parameterize(Statement var1) throws SQLException;

    void batch(Statement var1) throws SQLException;

    int update(Statement var1) throws SQLException;

    <E> List<E> query(Statement var1, ResultHandler var2) throws SQLException;

    <E> Cursor<E> queryCursor(Statement var1) throws SQLException;

    BoundSql getBoundSql();

    ParameterHandler getParameterHandler();
}

在prepare阶段setFetchSize设置提取的数量。

    public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
        ErrorContext.instance().sql(this.boundSql.getSql());
        Statement statement = null;

        try {
            statement = this.instantiateStatement(connection);
            this.setStatementTimeout(statement, transactionTimeout);
            this.setFetchSize(statement);
            return statement;
        } catch (SQLException var5) {
            this.closeStatement(statement);
            throw var5;
        } catch (Exception var6) {
            this.closeStatement(statement);
            throw new ExecutorException("Error preparing statement.  Cause: " + var6, var6);
        }
    }

    protected void setFetchSize(Statement stmt) throws SQLException {
        Integer fetchSize = this.mappedStatement.getFetchSize();
        if (fetchSize != null) {
            stmt.setFetchSize(fetchSize);
        } else {
            Integer defaultFetchSize = this.configuration.getDefaultFetchSize();
            if (defaultFetchSize != null) {
                stmt.setFetchSize(defaultFetchSize);
            }

        }
    }

我们可以在mybatis-config.xml配置这个插件。

<plugins>
    <plugin interceptor="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor">
        <!-- 设置每页显示条数,默认值是5 -->
        <property name="pageSizeZero" value="true"/>
        <!-- 开启 count 的 join 优化,只针对 left join -->
        <property name="countSqlParserEnabled" value="true"/>
    </plugin>
</plugins>

使用分页查询时 在 Java 代码中,使用 MyBatis-Plus 提供的 Page 类来进行分页查询。 

Page<User> page = new Page<>(currentPage, pageSize);
List<User> users = userMapper.selectPage(page, wrapper); // wrapper 是一个条件构造器
Long total = page.getTotal(); // 获取总记录数
List<User> records = page.getRecords(); // 获取当前页的记录列表

我们自己也可以对Interceptor进行实现:

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
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 java.util.Properties;

//@Intercepts({
//    @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
    // 可以添加更多需要拦截的方法
//})
@Component
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class})})
public class CustomExecutorInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 1.在执行update方法前的操作
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
        // 打印 SQL 语句
        String sql = boundSql.getSql();
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        System.out.println("Executing SQL: [" + mappedStatement.getId() + "] => " + sql);


        // 2.调用原来的方法
        Object result = invocation.proceed(); 
        // 3.在执行update方法后的操作

        return result;
    }

    @Override
    public Object plugin(Object target) {
        // 返回一个代理对象,使得目标对象的方法调用可以被拦截器拦截
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        // 设置拦截器的属性
    }
}

注册拦截器的三种方法:

1.如果你的应用是基于 Spring Boot 的,并且使用了 MyBatis-Spring-Boot-Starter,可以创建一个配置类来注册自定义的 MyBatis 拦截器。以下是一个示例:

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableTransactionManagement
public class MybatisConfig extends MybatisAutoConfiguration {

    @Bean
    public CustomExecutorInterceptor mybatisInterceptor() {
        return new CustomExecutorInterceptor(); // 假设你有一个名为 CustomExecutorInterceptor 的自定义拦截器类
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(ResourcePatternResolver resolver) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource()); // dataSource() 是你的数据源 Bean
        factoryBean.setPlugins(new Interceptor[]{mybatisInterceptor()});
        // 其他配置,如设置mapperLocations等
        factoryBean.setMapperLocations(resolver.getResources("classpath:mappers/*.xml"));
        return factoryBean.getObject();
    }
    
    // 其他配置...
}

2.使用 MyBatis 的 Configuration 配置文件注册拦截器。

<configuration>
    <!-- ... -->
    <plugins>
        <plugin interceptor="com.example.CustomExecutorInterceptor" />
    </plugins>
</configuration>

3.注解式注册(适用于Spring环境)在 Spring 环境下,也可以使用 @Intercepts 注解来定义拦截器,并配合 @Component 注解将其注册为 Spring Bean,就如同上面的CustomExecutorInterceptor 实现类,下面是大致格式:

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 java.lang.reflect.Method;

@Component
@Intercepts({
    @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
    // 其他需要拦截的方法签名
})
public class YourInterceptorClass implements Interceptor {
    // 实现 Interceptor 接口的方法
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 拦截逻辑
        return invocation.proceed();
    }

    // 其他方法...
}

那我们知道了这些拦截器和这四大接口有什么用呢?如果你想比初级程序员牛逼那么一点点,知道一些总没坏处,说不定被裁员了下次面试就问到你这些了。比如我们可以说,我要实现自定义数据库入库字段进行加密,除了笨方法每次入库的时候对字段加密一下。我们还可以写点初级程序员看了一蒙圈的代码装下B,这样用拦截器优雅的进行加密操作:

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
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 java.sql.PreparedStatement;
import java.util.Properties;

@Intercepts({@Signature(type = StatementHandler.class, method = "setParameters", args = {PreparedStatement.class})})
public class EncryptionInterceptor implements Interceptor {

    private static final String ENCRYPTION_KEY = "your_encryption_key"; // 加密密钥

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        BoundSql boundSql = statementHandler.getBoundSql();
        Object parameterObject = boundSql.getParameterObject();

        if (parameterObject != null) {
            // 这里只是一个简单的示例,实际项目中应根据业务需求进行适当的加密算法封装
            // 对参数对象的各个属性进行加密处理
            encrypt(parameterObject);
        }

        return invocation.proceed();
    }

    private void encrypt(Object parameterObject) {
        // 实现你的加密逻辑,这里仅为示例
        // 例如,假设参数对象是个 Map
        if (parameterObject instanceof Map) {
            Map<String, Object> params = (Map<String, Object>) parameterObject;
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                entry.setValue(encryptValue(entry.getValue()));
            }
        }
        // 其他类型的参数对象处理...
    }

    private Object encryptValue(Object value) {
        // 实现加密方法,此处仅为示意
        return "encrypted_" + value.toString(); 
    }

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

    @Override
    public void setProperties(Properties properties) {
        // 设置拦截器的属性,例如密钥等
    }
}

这样,每当 MyBatis 准备执行 SQL 时,就会触发 EncryptionInterceptor 的 intercept 方法,从而对参数进行加密。当然,为了能在查询结果中解密数据,你还需要在结果映射的地方进行相应的处理,例如在 ResultMap 中使用自定义的 TypeHandler 进行解密。

TypeHandler接口。在 MyBatis 中,TypeHandler 是一个接口,它负责在 Java 类型和 JDBC 类型之间进行转换。TypeHandler 主要应用于两个场合。

  1. 设置参数: 当 MyBatis 向预编译的 SQL 语句(PreparedStatement)设置参数时,会根据参数的 Java 类型查找对应的 TypeHandler,由 TypeHandler 负责将 Java 类型转换为 JDBC 可识别的数据类型,并将其设置到 PreparedStatement 中。

  2. 获取结果: 当从 ResultSet 中获取结果时,同样会根据目标 Java 类型查找对应的 TypeHandler,由 TypeHandler 负责将从 ResultSet 中读取的 JDBC 数据类型转换成对应的 Java 类型。

通过自定义 TypeHandler,我们可以实现一些特殊类型的转换,例如:

  • 将日期字符串转换为 Java Date 类型或 LocalDate 类型。
  • 将数据库中的整数或字符串转换为枚举类型。
  • 对敏感数据(如密码)进行加密/解密处理。

大致例子:

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;

public class EncryptedStringTypeHandler extends BaseTypeHandler<String> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        // 在这里实现加密逻辑并将加密后的字符串设置到 PreparedStatement
        String encryptedValue = encrypt(parameter);
        ps.setString(i, encryptedValue);
    }

    @Override
    public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
        // 从 ResultSet 获取加密过的字符串并解密
        String encryptedValue = rs.getString(columnName);
        return decrypt(encryptedValue);
    }

    @Override
    public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        // 同上,根据列索引获取并解密
        String encryptedValue = rs.getString(columnIndex);
        return decrypt(encryptedValue);
    }

    @Override
    public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        // 从 CallableStatement 获取并解密
        String encryptedValue = cs.getString(columnIndex);
        return decrypt(encryptedValue);
    }

    // 假设encrypt和decrypt是加密解密的方法
    private String encrypt(String value) {...}
    private String decrypt(String encryptedValue) {...}
}

然后在 MyBatis 配置文件中注册这个自定义的 TypeHandler,并在映射文件中引用它,以便在设置参数和获取结果时进行自动的加密解密处理。

<configuration>
  <!-- ...其它配置... -->
  <typeHandlers>
    <typeHandler handler="com.example.security.DecryptTypeHandler"/>
  </typeHandlers>
</configuration>

或者是用注解来注入这个Handler。

import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;

@MappedJdbcTypes(JdbcType.VARCHAR)
@MappedTypes(MyCustomType.class)
public class MyCustomTypeHandler implements TypeHandler<MyCustomType> {
    // ...
}

最后,在你的 ResultMap 中引用这个自定义的 TypeHandler:

<resultMap id="UserResultMap" type="com.example.entity.User">
  <!-- ...其它字段... -->
  <result column="encrypted_field" property="plainTextField" jdbcType="VARCHAR" typeHandler="com.example.security.DecryptTypeHandler"/>
</resultMap>

<select id="selectUser" resultMap="UserResultMap">
  SELECT * FROM users
</select>

再比如我们如果用Mybatis Plus的时候,经常用BaseMapper里的方法对数据库进行增删改查的操作很方便,它也跟这四大对象有关系,MyBatis Plus 的 Mapper 接口起作用的时机与标准 MyBatis 类似,但在 MyBatis Plus 中进行了进一步的增强和简化,具体过程如下:

  1. 初始化阶段

    • 当 MyBatis Plus 初始化 SqlSessionFactory 时,会读取配置文件,其中包含了对 Mapper 接口所在包的扫描信息,通过 MapperScan 注解或 XML 配置来实现自动扫描。
    • MyBatis Plus 会自动识别那些继承自 BaseMapper 或使用特定注解的 Mapper 接口,并为其创建相应的代理对象。
  2. SqlSession 使用阶段

    • 当应用通过 SqlSession 调用 getMapper(Class<T> type) 方法时,由于已经预先扫描和注册了 Mapper 接口,所以能立即获得对应的代理对象。
  3. 执行方法调用阶段

    • 开发者调用 MyBatis Plus 的 Mapper 接口(如继承自 BaseMapper 的 UserMapper 接口)中的方法,这些方法包含了基本的 CRUD 操作以及可能的自定义扩展方法。
    • 代理对象接收到方法调用后,MyBatis Plus 会根据方法签名和已注册的元数据信息动态生成 SQL 语句,并利用 MyBatis 的执行链(Executor、ParameterHandler、ResultSetHandler 等)来执行 SQL 操作。
    • MyBatis Plus 通过强大的元数据处理能力,可以根据实体类的属性自动生成 SQL 语句,避免了手动编写大量的 SQL 或 XML 映射文件。

最后想说的是其实这些感觉也不需要死记硬背,做一个普通程序员大致有个了解就行了,希望如上啰啰嗦嗦的废话能给你的工作带来一些帮助。

  • 32
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值