MyBatis底层基础和拦截器 - 第一部分

本文详细解读了MyBatis的基础概念、内部实现机制以及拦截器的作用,包括配置文件、数据源、执行器、缓存、SQL源、映射规则等关键组件。通过实际代码示例,展示了如何在MyBatis中添加拦截器以增强其功能,以及如何利用MyBatis执行SQL查询并处理结果。

MyBatis底层基础和拦截器 - 第一部分

第一部分包含了下面代码的基本讲解和下面代码与XML配置关系和作用的讲解。

这一部分是了解后续拦截器和SqlSource的重要基础。

本视频不仅对深入学习MyBatis有用,对于一般的MyBatis使用也能加深理解。

第一部分完整视频+源文件+PPT下载地址:http://pan.baidu.com/s/1mgzZnx2

在第一部分视频后,还有一些和MyBatis相关问答的一些视频,这些视频可以从群共享下载。

另外想看第三遍讲解的各位还有看直播的机会,具体时间会在群内公布。

如果有意愿捐赠,可以从 http://mybatis.tk 下方扫描微信二维码或支付宝二维码进行捐赠。

下面是整篇讲解用到的代码:

package tk.mybatis.sample1;

import org.apache.ibatis.builder.StaticSqlSource;
import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.cache.decorators.*;
import org.apache.ibatis.cache.impl.PerpetualCache;
import org.apache.ibatis.datasource.unpooled.UnpooledDataSource;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.mapping.*;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.defaults.DefaultSqlSession;
import org.apache.ibatis.transaction.Transaction;
import org.apache.ibatis.transaction.jdbc.JdbcTransaction;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.apache.log4j.Logger;
import org.junit.Test;

import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * 通过本段代码了解MyBatis内部的一些实现
 *
 * @author liuzh
 * @since 2015-11-24 23:04
 */
public class SimpleMyBatis {

    public final Logger logger = Logger.getLogger(SimpleMyBatis.class);

    @Test
    public void test() throws IOException, SQLException {
        //创建配置文件
        final Configuration config = new Configuration();
        config.setCacheEnabled(true);
        config.setLazyLoadingEnabled(false);
        config.setAggressiveLazyLoading(true);

        //为了后续说明拦截器,这里添加两个简单例子
        config.addInterceptor(new SampleExecutorInterceptor());
        config.addInterceptor(new SampleResultSetHandlerInterceptor());

        //创建DataSource
        Properties props = Resources.getResourceAsProperties("jdbc.properties");
        UnpooledDataSource dataSource = new UnpooledDataSource();
        dataSource.setDriver(props.getProperty("driver"));
        dataSource.setUrl(props.getProperty("url"));
        dataSource.setUsername(props.getProperty("username"));
        dataSource.setPassword(props.getProperty("password"));

        //创建Executor
        //<transactionManager type="JDBC"/>
        Transaction transaction = new JdbcTransaction(dataSource, null, false);

        //config.newExecutor会将符合条件的拦截器添加到Executor代理链上
        final Executor executor = config.newExecutor(transaction);

        //cache是一个多层代理【装饰模式】的缓存对象,通过一级一级代理使得一个简单的缓存拥有了复杂的功能
        //<cache/>
        final Cache countryCache =
                new SynchronizedCache(//同步缓存
                        new SerializedCache(//序列化缓存
                                new LoggingCache(//日志缓存
                                        new LruCache(//最少使用缓存
                                                new PerpetualCache("country_cache")//持久缓存
                                        ))));


        //类型处理注册器
        //自己写TypeHandler的时候可以参考该注册器中已经存在的大量实现
        final TypeHandlerRegistry registry = config.getTypeHandlerRegistry();

        //================== 下面的步骤相当于解析XML或者解析接口注解方法生成ms =====================

        //创建静态sqlSource
        //最简单的,相当于从xml或接口注解获取SQL,创建合适的sqlSource对象
        StaticSqlSource sqlSource = new StaticSqlSource(config, "SELECT * FROM country WHERE id = ?");

        //由于上面的SQL有个参数id,这里需要提供ParameterMapping(参数映射)
        List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();
        //通过ParameterMapping.Builder创建ParameterMapping
        parameterMappings.add(new ParameterMapping.Builder(config, "id", registry.getTypeHandler(int.class)).build());
        //通过ParameterMap.Builder创建ParameterMap
        ParameterMap.Builder paramBuilder = new ParameterMap.Builder(config, "defaultParameterMap", Country.class, parameterMappings);

        //创建ms
        MappedStatement.Builder msBuilder = new MappedStatement.Builder(config, "tk.mybatis.selectCountry", sqlSource, SqlCommandType.SELECT);
        msBuilder.parameterMap(paramBuilder.build());

        //<resultMap>
        final ResultMap resultMap = new ResultMap.Builder(config, "defaultResultMap", Country.class,
                new ArrayList<ResultMapping>() {
                    {
                        add(new ResultMapping.Builder(config, "id", "id", int.class).build());
                        add(new ResultMapping.Builder(config, "countryname", "countryname", String.class).build());
                        add(new ResultMapping.Builder(config, "countrycode", "countrycode", registry.getTypeHandler(String.class)
                        ).build());
                    }
                }).build();

        //2:不设置具体的映射,只是用类型,相当于只配置resultType="tk.mybatis.sample1.Country"
        //final ResultMap resultMap = new ResultMap.Builder(config, "defaultResultMap", Country.class, new ArrayList<ResultMapping>()).build();

        List<ResultMap> resultMaps = new ArrayList<ResultMap>();
        resultMaps.add(resultMap);
        //设置返回值的resultMap和resultType
        msBuilder.resultMaps(resultMaps);
        //设置缓存
        msBuilder.cache(countryCache);

        //创建ms
        MappedStatement ms = msBuilder.build();

        //第一种使用executor执行
        List<Country> countries = executor.query(ms, 1, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);

        for (Country country : countries) {
            logger.info(country);
        }

        //第二种
        //首先添加ms到config
        config.addMappedStatement(ms);
        //创建sqlSession
        SqlSession sqlSession = new DefaultSqlSession(config, executor, false);
        //查询
        Country country = sqlSession.selectOne("selectCountry", 35);
        logger.info(country);
        //关闭
        sqlSession.close();
    }
}
<script type="text/javascript"> $(function () { $('pre.prettyprint code').each(function () { var lines = $(this).text().split('\n').length; var $numbering = $('<ul/>').addClass('pre-numbering').hide(); $(this).addClass('has-numbering').parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($('<li/>').text(i)); }; $numbering.fadeIn(1700); }); }); </script>
<think>我们正在解决MyBatis Log Free插件在MyBatis-Flex中不生效的问题。根据引用[2]中提到的InterceptorChain机制,MyBatis的插件(拦截器)需要被正确注册才能生效。MyBatis-Flex作为MyBatis的增强框架,同样基于MyBatis拦截器机制,因此我们可以从拦截器注册的角度来排查。 可能的原因及解决方案如下: 1. **插件未正确配置**:确保在MyBatis-Flex的配置文件中正确配置了日志插件(如MyBatis Log Free)。在MyBatis中,我们通常在mybatis-config.xml中配置插件。在MyBatis-Flex中,可以通过Spring Boot的配置类或者直接在application.yml中配置。 2. **拦截器注册顺序**:MyBatis拦截器链是按添加顺序执行的,如果其他拦截器修改了SQL语句,可能会影响日志插件的输出。确保日志插件被正确添加到拦截器链中。 3. **日志级别设置**:检查日志框架(如Logback、Log4j2)的配置,确保对应Mapper接口的日志级别设置为DEBUG或TRACE。因为MyBatis Log Free插件通常需要依赖底层的日志框架输出SQL。 4. **MyBatis-Flex的特殊性**:MyBatis-Flex可能有自己的日志打印机制,或者对拦截器的处理方式有所不同。需要确认MyBatis Log Free插件是否兼容MyBatis-Flex。 5. **插件冲突**:项目中可能存在多个日志打印插件(如MyBatis原生的日志插件),导致冲突。尝试移除其他日志插件,只保留MyBatis Log Free。 具体步骤: **步骤1:检查插件配置** 在Spring Boot项目中,我们可以通过配置类来添加拦截器。例如: ```java @Configuration public class MyBatisConfig { @Bean public ConfigurationCustomizer configurationCustomizer() { return configuration -> { // 添加MyBatis Log Free插件 configuration.addInterceptor(new MyBatisLogFreeInterceptor()); }; } } ``` 注意:`MyBatisLogFreeInterceptor`需要是MyBatis Log Free插件提供的拦截器类。请确认你引入的依赖中是否有此类。 **步骤2:检查日志框架配置** 在application.properties或application.yml中设置日志级别: ```properties # 设置MyBatis相关包的日志级别为DEBUG logging.level.org.mybatis=DEBUG logging.level.com.yourmapper.package=DEBUG ``` **步骤3:确认插件兼容性** 查阅MyBatis-Flex文档,确认其是否支持第三方拦截器。通常MyBatis-Flex完全兼容MyBatis拦截器机制。如果MyBatis Log Free插件未生效,可能需要查看MyBatis-Flex是否有特殊的拦截器注册方式。 **步骤4:排除冲突插件** 检查pom.xml中是否引入了其他可能冲突的插件,比如MyBatis自带的日志插件(如log4jdbc)。如果有,尝试移除。 **步骤5:调试拦截器链** 可以编写一个简单的拦截器,打印拦截器链中的所有拦截器,以确认MyBatis Log Free插件是否被添加。 ```java @Bean public Interceptor interceptor() { return (Invocation invocation) -> { System.out.println("Interceptor added: " + invocation); return invocation.proceed(); }; } ``` 如果以上步骤都不能解决问题,建议查阅MyBatis Log Free插件的文档,查看是否有针对MyBatis-Flex的特殊说明,或者尝试联系插件作者。 另外,引用[3]中提到代码生成可能会影响,但这里我们主要关注日志插件的拦截器注册问题,因此代码生成的影响在此问题中可能性较小。 希望以上步骤能帮助你解决问题。 §§相关问题§§ 1. MyBatis-Flex中如何自定义拦截器? 2. MyBatis Log Free插件的原理是什么? 3. 如何配置MyBatis-Flex的日志输出? </think>### MyBatis Log Free 在 MyBatis-Flex 中不生效的解决方案 根据引用[2]提到的 MyBatis 拦截器机制插件注册原理,结合 MyBatis-Flex 框架特性,排查步骤如下: #### 1. **检查拦截器注册(核心步骤)** MyBatis Log Free 本质是 MyBatis 拦截器,需通过 `InterceptorChain` 注册(引用[2])。在 MyBatis-Flex 中需显式配置: ```java @Configuration public class MyBatisFlexConfig { @Bean public MybatisLogFreeInterceptor logInterceptor() { return new MybatisLogFreeInterceptor(); // 实例化日志拦截器 } } ``` - **关键点**:确保拦截器被添加到 `interceptors` 集合(引用[2]中的 `InterceptorChain.addInterceptor()`) #### 2. **验证插件加载顺序** MyBatis-Flex 的自带插件(如分页)可能影响拦截器链顺序: - 尝试调整日志拦截器的加载顺序(优先加载) - 在配置类中添加 `@Order(Ordered.HIGHEST_PRECEDENCE)` 注解 #### 3. **检查依赖冲突** ```xml <!-- pom.xml 需同时包含 --> <dependency> <groupId>com.mybatis-flex</groupId> <artifactId>mybatis-flex-core</artifactId> <version>1.2.8</version> </dependency> <dependency> <groupId>com.github.zhuguangtao</groupId> <artifactId>mybatis-log-free</artifactId> <version>1.0.4</version> </dependency> ``` - 执行 `mvn dependency:tree` 检查版本冲突 #### 4. **启用 MyBatis 原生日志(辅助诊断)** 在 `application.yml` 中激活 SQL 日志: ```yaml mybatis-flex: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl ``` 若原生日志生效但插件无效,说明框架拦截器链异常(引用[2]) #### 5. **排查 MyBatis-Flex 特性影响** - 禁用 MyBatis-Flex 的 **APT 增强功能**(可能跳过拦截器- 检查是否使用 `@Table("tb_account")` 注解实体类(APT 要求) #### 替代方案(若仍不生效) 使用 MyBatis-Flex 内置日志工具: ```java FlexGlobalConfig config = FlexGlobalConfig.getDefaultConfig(); config.setSqlPrinter(sql -> { System.out.println("Executed SQL: " + sql); // 自定义日志输出 }); ``` > **底层原理**:MyBatis 插件通过动态代理拦截 `Executor`(引用[2])。MyBatis-Flex 可能重写了 `Executor` 实现,需确保日志拦截器在代理链中正确织入[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值