MyBatis拦截器和MyBatis Plus的拦截器在概念上是一致的,都是通过拦截器机制对MyBatis的SQL执行过程进行扩展和控制,但是在实现细节和功能上有所差异。MyBatis Plus的拦截器是建立在MyBatis拦截器基础之上,通过封装简化了开发流程,同时也针对MyBatis Plus的特性和功能进行了强化和扩展。在MyBatis Plus项目中使用拦截器时,既可以利用MyBatis Plus内置的拦截器,也能自定义符合自己业务需求的拦截器。
-
MyBatis拦截器:
- MyBatis自带的拦截器机制允许开发者自定义拦截器,实现
org.apache.ibatis.plugin.Interceptor
接口,并通过@Intercepts
注解来标识要拦截的方法签名。这些拦截器可以在Executor、ParameterHandler、ResultSetHandler和StatementHandler四个核心接口的方法上进行拦截,从而在SQL执行的不同阶段插入自定义逻辑,如日志记录、性能分析、权限控制等。
- MyBatis自带的拦截器机制允许开发者自定义拦截器,实现
-
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
执行器是 SimpleExecutor
。SimpleExecutor
是最基础的执行器,它的特点是每次执行 SQL 都会创建一个新的 PreparedStatement
对象,然后执行 SQL 并关闭 PreparedStatement
。
以下是 Executor
接口在 MyBatis 中扮演的关键角色:
-
SQL 执行:
Executor
接口提供了执行 SQL 语句的方法,这些方法接收已解析的MappedStatement
对象作为参数,后者封装了 SQL 语句以及相关的参数映射和结果映射信息。 -
事务管理:
Executor
通常与事务管理紧密结合,确保在执行 SQL 时能够参与到当前的数据库事务中,并根据配置执行提交或回滚。 -
缓存控制:
Executor
也参与 MyBatis 的二级缓存机制,负责缓存数据的读取和刷新。 -
执行策略: MyBatis 提供了多种
Executor
的实现,如SimpleExecutor
、ReuseExecutor
和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 之间。
-
SQL 解析与动态 SQL 结合: 根据 Mapper 映射文件中的 SQL 语句以及参数对象,解析得到最终的可执行 SQL 语句。对于动态 SQL(如
<if>
、<foreach>
等标签),StatementHandler
会负责将这些标签解析为实际的 SQL 片段。 -
参数设置: 将用户传入的方法参数,按照一定的规则绑定到 JDBC Statement 对象上,比如设置 PreparedStatement 的参数值。
-
结果集映射: 根据 Mapper 映射文件中的 resultMap 配置,将 JDBC ResultSet 映射为 Java 对象,便于上层应用处理。
-
执行 SQL 语句: 最终执行 JDBC Statement,包括执行查询、更新等操作。
StatementHandler负责将最终的 SQL 语句和参数传递给 JDBC PreparedStatement
或 Statement。上年龄的老程序员上学的时候肯定都写过那种调用原生
JDBC API 操作数据库的代码,自己写sql,提交或者回滚,关闭数据库连接之类的,相比现在繁琐了很多。
-
Statement:
- Statement 是最基本的用于执行静态 SQL 语句的对象,可以发送简单的 SQL 查询到数据库。
- 编写 SQL 语句时直接包含变量值,例如
String sql = "SELECT * FROM users WHERE id = 1";
- 如果同一段 SQL 语句需要反复执行多次且每次参数不同,使用 Statement 会导致 SQL 注入的风险增加。
- 不支持预编译,对于每次执行,数据库都需要解析和优化 SQL 语句,效率相对较低。
-
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 对象。具体功能如下:
-
结果集解析: 当 SQL 查询被执行并返回一个 ResultSet 时,
ResultSetHandler
会遍历 ResultSet 中的每一行数据。 -
对象映射: 根据 MyBatis 的映射配置(如 resultMap 或通过注解标注的映射关系),将每一行的列数据映射到对应的 Java 对象的属性上。
-
类型转换: 在映射过程中,
ResultSetHandler
会与 MyBatis 的 TypeHandler 一起工作,将数据库中的原始数据类型转换为 Java 类型。 -
集合构建: 如果查询结果需要转换为一个集合(如 List、Set 等),
ResultSetHandler
会将一个个映射好的 Java 对象添加到集合中。 -
嵌套结果映射: 对于复杂的查询结果,
ResultSetHandler
还能处理嵌套结果映射,即将多个表关联查询的结果映射到嵌套的 Java 对象结构中。
4.PameterHandler : 作用是对参数进行赋值。它是 MyBatis 框架中的一个重要组件,它的主要作用是处理 SQL 执行时的参数绑定。在 MyBatis 中,当我们调用 Mapper 方法执行 SQL 时,传递给该方法的参数需要经过 ParameterHandler
处理后才能正确绑定到预编译的 SQL 语句中。
具体来说,ParameterHandler
的功能包括:
-
类型转换: 将方法参数从 Java 类型转换为 JDBC 可识别的类型。例如,将 Java 日期类型转换为符合数据库日期格式的
java.sql.Date
或java.sql.Timestamp
类型。 -
参数设置: 根据 SQL 语句中占位符(#{...} 或 ${...})的位置和数量,将转换后的参数值设置到 PreparedStatement 中。
-
动态 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 主要应用于两个场合。
-
设置参数: 当 MyBatis 向预编译的 SQL 语句(PreparedStatement)设置参数时,会根据参数的 Java 类型查找对应的 TypeHandler,由 TypeHandler 负责将 Java 类型转换为 JDBC 可识别的数据类型,并将其设置到 PreparedStatement 中。
-
获取结果: 当从 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 中进行了进一步的增强和简化,具体过程如下:
-
初始化阶段:
- 当 MyBatis Plus 初始化 SqlSessionFactory 时,会读取配置文件,其中包含了对 Mapper 接口所在包的扫描信息,通过
MapperScan
注解或 XML 配置来实现自动扫描。 - MyBatis Plus 会自动识别那些继承自
BaseMapper
或使用特定注解的 Mapper 接口,并为其创建相应的代理对象。
- 当 MyBatis Plus 初始化 SqlSessionFactory 时,会读取配置文件,其中包含了对 Mapper 接口所在包的扫描信息,通过
-
SqlSession 使用阶段:
- 当应用通过 SqlSession 调用
getMapper(Class<T> type)
方法时,由于已经预先扫描和注册了 Mapper 接口,所以能立即获得对应的代理对象。
- 当应用通过 SqlSession 调用
-
执行方法调用阶段:
- 开发者调用 MyBatis Plus 的 Mapper 接口(如继承自
BaseMapper
的UserMapper
接口)中的方法,这些方法包含了基本的 CRUD 操作以及可能的自定义扩展方法。 - 代理对象接收到方法调用后,MyBatis Plus 会根据方法签名和已注册的元数据信息动态生成 SQL 语句,并利用 MyBatis 的执行链(Executor、ParameterHandler、ResultSetHandler 等)来执行 SQL 操作。
- MyBatis Plus 通过强大的元数据处理能力,可以根据实体类的属性自动生成 SQL 语句,避免了手动编写大量的 SQL 或 XML 映射文件。
- 开发者调用 MyBatis Plus 的 Mapper 接口(如继承自
最后想说的是其实这些感觉也不需要死记硬背,做一个普通程序员大致有个了解就行了,希望如上啰啰嗦嗦的废话能给你的工作带来一些帮助。