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 {@Overridepublic 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;}@Overridepublic Object plugin(Object target) {// 返回一个代理对象,使得目标对象的方法调用可以被拦截器拦截return Plugin.wrap(target, this);}@Overridepublic 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 {@Beanpublic CustomExecutorInterceptor mybatisInterceptor() {return new CustomExecutorInterceptor(); // 假设你有一个名为 CustomExecutorInterceptor 的自定义拦截器类}@Beanpublic SqlSessionFactory sqlSessionFactory(ResourcePatternResolver resolver) throws Exception {SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();factoryBean.setDataSource(dataSource()); // dataSource() 是你的数据源 BeanfactoryBean.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 接口的方法@Overridepublic 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"; // 加密密钥@Overridepublic 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) {// 实现你的加密逻辑,这里仅为示例// 例如,假设参数对象是个 Mapif (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(); }@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic 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> {@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {// 在这里实现加密逻辑并将加密后的字符串设置到 PreparedStatementString encryptedValue = encrypt(parameter);ps.setString(i, encryptedValue);}@Overridepublic String getNullableResult(ResultSet rs, String columnName) throws SQLException {// 从 ResultSet 获取加密过的字符串并解密String encryptedValue = rs.getString(columnName);return decrypt(encryptedValue);}@Overridepublic String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {// 同上,根据列索引获取并解密String encryptedValue = rs.getString(columnIndex);return decrypt(encryptedValue);}@Overridepublic 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 映射文件。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/645268.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

武汉星起航:亚马逊全球资源赋能中国卖家,跨境电商助力品牌国际化

亚马逊全球开店业务于2015年正式进驻中国&#xff0c;为中国卖家打开了通往全球市场的便捷之门。这一举措不仅为中国卖家提供了与全球消费者直接交流的机会&#xff0c;更借助亚马逊的丰富资源和先进技术&#xff0c;帮助卖家将优质的中国商品推向世界舞台。亚马逊平台以其高效…

8.4.1 实验1:创建 VLAN 和划分端口

1、实验目的 通过本实验可以掌握&#xff1a; VLAN的概念。创建VLAN的方法。把交换机端口划分到VLAN中的方法。 2、实验拓扑 创建 VLAN 和划分端口的实验拓扑如下图所示。 图8-5 创建 VLAN 和划分端口的实验拓扑 3、实验步骤 &#xff08;1&#xff09;实验准备 S1#eras…

javaScript中的作用域和作用域链

作用域&#xff08;Scope&#xff09; 什么是作用域 作用域是在运行时代码中的某些特定部分中变量、对象和函数的可访问性。 换句话说&#xff0c;作用域决定了代码区块中变量和其他资源的可见性。 示例&#xff1a; function outFun2() {var inVariable "内层变量2…

请介绍如何在PostgreSQL中使用物化视图提升查询性能,并描述其更新机制?

文章目录 解决方案1. 创建物化视图2. 刷新物化视图3. 在查询中使用物化视图 物化视图的更新机制总结 物化视图&#xff08;Materialized View&#xff09;是数据库中的一种对象&#xff0c;它存储了查询的结果集&#xff0c;即预先计算和存储了查询的结果。通过物化视图&#x…

Java 设计模式(上)

目录 一、单一职责原则 二、开闭原则 三、里氏替换原则 四、迪米特法则 五、接口隔离原则 六、依赖倒置原则 七、工厂方法 八、抽象工厂 九、建造者模式 十、原型模式 十一、单例模式 十二、适配器模式 一、单一职责原则 单一职责原则又称单一功能原则&#xff0c;…

HTTP协议的总结

参考 https://www.runoob.com/http/http-tutorial.html 1.简介 HTTP&#xff08;超文本传输协议&#xff0c;Hypertext Transfer Protocol&#xff09;是一种用于从网络传输超文本到本地浏览器的传输协议。它定义了客户端与服务器之间请求和响应的格式。HTTP 工作在 TCP/IP 模…

为底图发愁? 这里有一份清爽又百搭的底图绘制方法!

图纸不够清爽美观&#xff1f; 图纸表达混乱&#xff0c;重点不够醒目&#xff1f; 图纸的颜色太难调了&#xff0c;怎么调都不满意&#xff1f; ...... 俗话说&#xff0c;好的底图是图纸成功的关键&#xff01; 绝大部分的图纸问题&#xff0c;都和底图有关&#xff01; …

权威解析Spring框架九大核心功能(续篇):专业深度,不容错过

作者介绍&#xff1a;✌️大厂全栈码农|毕设实战开发&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。 推荐订阅精彩专栏 &#x1f447;&#x1f3fb; 避免错过下次更新 Springboot项目精选实战案例 更多项目&#xff1a;CSDN主页YAML墨韵 学如逆水行舟&#xff0c…

【无标题】w

import requests , sys , edge _ tts , os , asyncio from pydub import AudioSegment , playback url http://localhost:8080/v1/chat/ completions ’ def send _ message ( message ): headers {" Content - Type “:” application / json "} data { " mode…

数据聚类:Mean-Shift和EM算法

目录 1. 高斯混合分布2. Mean-Shift算法3. EM算法4. 数据聚类5. 源码地址 1. 高斯混合分布 在高斯混合分布中&#xff0c;我们假设数据是由多个高斯分布组合而成的。每个高斯分布被称为一个“成分”&#xff08;component&#xff09;&#xff0c;这些成分通过加权和的方式来构…

Cesium分屏对比功能实现,完整版代码案例

使用cesium开发的小伙伴们,分屏对比功能是视图功能中比较常见的一个需求。 这篇文章我们来教会大家如何实现这个功能。 首先我们要准备一左一右2个div容器,用来挂在两个cesium实例。 其实分屏对比的关键就在于左右两个视图如何联动起来。 那么我们需要借助相机之间的参数…

JavaScript精粹:26个关键字深度解析,编写高质量代码的秘诀!

JavaScript关键字是一种特殊的标识符&#xff0c;它们在语言中有固定的含义&#xff0c;不能用作变量名或函数名。这些关键字是JavaScript的基础&#xff0c;理解它们是掌握JavaScript的关键。 今天&#xff0c;我们将一起探索JavaScript中的26个关键字&#xff0c;了解这些关…