Mapper接口和Mapper.xml是如何解析的

Spring整合MyBatis原理之Mapper接口和xml文件的解析 目录前言类 `SqlSessionFactoryBean`实现了 `FactoryBean` 接口的 `getObject()``buildSqlSessionFactory()`解析 `mapper` 文件的 `parse()`解析 `mapper` 文件的 `configurationElement(parser.evalNode("/mapper"))`解析节点 `statementParser.parseStatementNode()`绑定 `namespace` 的 `mapper` 的 `bin 阅读详情
/**
 * <pre>
 * 总结 非Spring环境下,配置mybatis-config配置文件
 *     <mappers>
 *         <package name="luck.mybatis.mapper"/>
 *         <mapper class="luck.mybatis.mapper.UsersMapper"/>
 *         <mapper resource="mapper/PermissionsMapper.xml"/>
 *     </mappers>
 *
 *
 *     逻辑1. 当配置为class/package: 则将指定Mapper接口或者指定包名下的Mapper接口进行解析
 *        1.1. 最终会调用到{@link Configuration#addMapper(Class)}注册Mapper
 *          1.1.1. 首先会将Mapper接口中的所有方法进行一一解析,因为Mapper接口中,可以使用注解,也可以使用mapper.xml进行SQL映射
 *                 会通过{@link MapperAnnotationBuilder}来解析Mapper接口,只处理可以书写{@link MapperAnnotationBuilder#statementAnnotationTypes}SQL相关的注解
 *
 *          1.1.2. 再通过{@link MapperAnnotationBuilder#parse()}对Mapper接口开始解析,加载Mapper接口的同时会标记该接口已经解析,防止重复加载,因为下面逻辑又会触发Mapper接口加载
 *                 接着又会同时加载该Mapper接口路径下+.xml的映射文件,如果存在该mapper.xml文件,会通过{@link XMLMapperBuilder}这个MapperXML解析器来解析该Mapper.xml文件
 *                 最终调用{@link XMLMapperBuilder#parse()}解析Mapper.xml,在这个过程中,会获取Mapper.xml中的命名空间进行分析
 *                 如果Mapper.xml中写的命名空间是一个全限定名并且是接口,该接口会当做Mapper接口来处理(又回到了1.1这一步),如果不符合这个条件,则会忽略.
 *                 在解析完.xml之后,会添加一个标识,该xml已经被解析
 *
 *         1.1.3. 这样一来我们可以发现,在注册Mapper的时候,处理Mapper接口的时候,会自动处理Mapper接口路径下的Mapper.xml映射文件
 *                在解析Mapper.xml的时候,又会通过Mapper.xml中的命名空间来注册Mapper接口,完成了相互注册逻辑
 *
 *
 *     逻辑2 当配置的是resource,url: 则将指定的mapper.xml文件进行解析
 *          2.1. 会调用{@link XMLMapperBuilder#parse()}解析Mapper.xml,在这个过程中,会添加一个标识,标识该xml已经被解析,同时会获取Mapper.xml中的命名空间进行分析
 *               如果Mapper.xml中写的命名空间是一个全限定名并且是接口,该接口会当做Mapper接口来处理(调用到{@link Configuration#addMapper(Class)}注册Mapper)
 *               接下来的逻辑就和{@see 1.1}完全一样
 *
 *
 * Spring-SpringBoot环境下
 * 可以通过{@link Demo#sqlSessionFactory}来配置Mapper.xml的路径(如果不配置,那么就是按照Mybatis的固定规则,解析Mapper接口下的Mapper.xml文件,我们只需要扫描Mapper接口就行
 * 我们可以通过PathMatchingResourcePatternResolver来对我们自己设置的mapper的路径进行解析,会解析到所有的Mapper.xml文件
 * 解析到mapper.xml文件之后,就回到了Mybatis原生的XML解析步骤{@see 逻辑2}
 *
 * </pre>
 */
public class Demo {
    @Bean
    public SqlSessionFactoryBean sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        // 通过解析器,解析该路径下的所有资源文件
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        sessionFactory.setMapperLocations(resolver.getResources("classpath*:mapper/*.xml"));
        return sessionFactory
    }
}

// Spring环境
// 保留Mapper和Mapper接口是如何解析的关键步骤
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ContextRefreshedEvent> {
    // mybatis全局配置文件
    private Resource configLocation;
    // 所有mapper.xml的路径
    private Resource[] mapperLocations;

    @Override
    public void afterPropertiesSet() throws Exception {
        // 初始化sqlSessionFactory
        this.sqlSessionFactory = this.buildSqlSessionFactory();
    }

    protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
        // mybatis的核心配置类对象,因为创建Configuration的方式有很多
        final Configuration targetConfiguration;

        XMLConfigBuilder xmlConfigBuilder = null;
        // 设置了自定义的Configuration对象
        if (this.configuration != null) {
            targetConfiguration = this.configuration;
        }
        // 指定了mybatis全局配置文件,通过配置文件生成Configuration对象
        else if (this.configLocation != null) {
            xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
            targetConfiguration = xmlConfigBuilder.getConfiguration();
        }
        // 其他情况,直接创建Configuration对象
        else {
            targetConfiguration = new Configuration();
        }

        // 如果使用的是Mybatis的配置文件创建Configuration对象
        if (xmlConfigBuilder != null) {
            // 解析XML配置文件
            xmlConfigBuilder.parse();
        }
        // 如果指定了mapper.xml的路径
        if (this.mapperLocations != null) {
            // 遍历所有的mapper.xml文件
            for (Resource mapperLocation : this.mapperLocations) {
                if (mapperLocation == null) {
                    continue;
                }
                // 使用Xml的mapper解析器对该xml进行解析
                XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
                xmlMapperBuilder.parse();
            }
            return this.sqlSessionFactoryBuilder.build(targetConfiguration);
        }
    }
}

// Mapper.xml构建器,解析器
public class XMLMapperBuilder extends BaseBuilder {
    // 配置类信息
    public final Configuration configuration;
    // XML解析器
    public final XPathParser parser;
    // Mapper.xml/Mapper接口构造者的辅助类
    public final MapperBuilderAssistant builderAssistant;
    // SQL片段
    public final Map<String, XNode> sqlFragments;
    // 该Mapper的资源文件
    public final String resource;

    // 解析该mapper.xml
    public void parse() {
        // 配置类中没有加载过该资源文件
        if (!configuration.isResourceLoaded(resource)) {
            // 解析xml中的所有标签,在这里会解析mapper文件中的所有<insert,select>等等标签,解析为statement信息
            configurationElement(parser.evalNode("/mapper"));
            // 标识该mapper.xml已经被解析过
            configuration.addLoadedResource(resource);
            // 通过xml中写的命名空间来注册Mapper接口(如果存在的命名空间,并且该命名空间是Mapper接口的情况)
            this.bindMapperForNamespace();
        }
    }

    // 解析mapper文件中的所有标签
    public void configurationElement(XNode context) {
        try {
            // 获取命名空间
            String namespace = context.getStringAttribute("namespace");
            // 不存在抛出异常
            if (namespace == null || namespace.isEmpty()) {
                throw new BuilderException("Mapper's namespace cannot be empty");
            }
            // 设置命名空间
            builderAssistant.setCurrentNamespace(namespace);
            // 解析cacheRef标签
            cacheRefElement(context.evalNode("cache-ref"));
            // 解析cache标签
            cacheElement(context.evalNode("cache"));
            // 解析parameterMap标签
            parameterMapElement(context.evalNodes("/mapper/parameterMap"));
            // 解析resultMap标签
            resultMapElements(context.evalNodes("/mapper/resultMap"));
            // 解析sql标签
            sqlElement(context.evalNodes("/mapper/sql"));
            // 根据<insert,select,delete,update>标签构建对应的statement对象,保存到Configuration对象中
            buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
        } catch (Exception e) {
            throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
        }
    }

    // 根据<insert,select,delete,update>标签构建对应的statement对象,并保存到Configuration类中
    public void buildStatementFromContext(List<XNode> list) {
        // 如果设置了使用指定类型的数据库
        if (configuration.getDatabaseId() != null) {
            buildStatementFromContext(list, configuration.getDatabaseId());
        }
        buildStatementFromContext(list, null);
    }

    // 创建<insert,select,delete,update>元素的映射语句信息对应的Statement对象
    public void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
        // 遍历所有的select|insert|delete|update标签
        for (XNode context : list) {
            final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
            try {
                // 解析该select|insert|delete|update标签标签,并将statement保存到Configuration对象中
                statementParser.parseStatementNode();
            } catch (IncompleteElementException e) {
                // 如果出现异常,先将该标签保存起来,标记该标签未解析完成
                configuration.addIncompleteStatement(statementParser);
            }
        }
    }


    // 通过xml中写的命名空间来注册Mapper接口(如果存在的命名空间,并且该命名空间是Mapper接口的情况)
    public void bindMapperForNamespace() {
        // 获取当前命名空间
        String namespace = builderAssistant.getCurrentNamespace();
        // 如果存在命名空间
        if (namespace != null) {
            Class<?> boundType = null;
            try {
                // 获取命名空间指定的Mapper接口
                boundType = Resources.classForName(namespace);
            } catch (ClassNotFoundException e) {
                // 如果不是Mapper接口,忽略
            }
            // 如果是Mapper接口,并且配置类中没有注册该Mapper
            if (boundType != null && !configuration.hasMapper(boundType)) {
                // 将这个命名空间进行标记,标记为已经加载过了,相同的命名空间不需要重复加载
                configuration.addLoadedResource("namespace:" + namespace);
                /**
                 * 注册该Mapper接口,内部同时也会做一件事,就是按照mybatis的固定规则,使用Mapper接口的类名+.xml来找该Mapper接口的XML映射文件
                 * 如果找到了,则会加载该XML配置文件,将该配置文件中的所有<insert,select>标签解析为Statement
                 * {@link MapperAnnotationBuilder#parse()}
                 * {@link MapperAnnotationBuilder#loadXmlResource()}
                 */
                configuration.addMapper(boundType);
            }
        }
    }

}

public class Configuration {
    // 添加Mapper接口,同时会解析Mappe接口中的注解信息,因为@Select...和<select>...效果一样,都要保存该方法对应的statement
    public <T> void addMapper(Class<T> type) {
        // 只处理接口
        if (type.isInterface()) {
            // 如果已经存在,抛出异常
            if (this.hasMapper(type)) {
                throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
            }
            boolean loadCompleted = false;
            try {
                // 注册mapper以及给定生成该mapper代理对象的工厂
                knownMappers.put(type, new MapperProxyFactory<>(type));
                // 解析该接口的所有方法
                MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
                // 解析mapper接口的所有方法
                parser.parse();
                // 该接口加载完成
                loadCompleted = true;
            } finally {
                // 如果没有加载成功,异常添加的代理对象工厂
                if (!loadCompleted) {
                    knownMappers.remove(type);
                }
            }
        }
    }
}

/**
 * Mapper接口的注解构建器
 * 和{@link XMLStatementBuilder}的作用一样
 * 该类是处理Mapper接口方法注解的形式
 */
public class MapperAnnotationBuilder {

    // 增删查改的注解,类似与xml中的<insert,delete...>标签
    public static final Set<Class<? extends Annotation>> statementAnnotationTypes = Stream.of(Select.class, Update.class, Insert.class, Delete.class, SelectProvider.class, UpdateProvider.class, InsertProvider.class, DeleteProvider.class).collect(Collectors.toSet());
    // 配置类信息
    public final Configuration configuration;
    // Mapper.xml/Mapper接口构造者的辅助类
    public final MapperBuilderAssistant assistant;
    // Mapper接口类型
    public final Class<?> type;

    // 解析mapper接口的所有方法
    public void parse() {
        // 当前mapper
        String resource = type.toString();
        /**
         * 如果当前mapper接口没有被加载过,才需要加载
         * {@link XMLMapperBuilder#parse()}
         * {@link XMLMapperBuilder#bindMapperForNamespace()}
         */
        if (!configuration.isResourceLoaded(resource)) {
            // 加载Mapper.xml,因为注册和xml可以共存
            this.loadXmlResource();
            // 标记该mapper被加载过,防止重复加载
            configuration.addLoadedResource(resource);
            // 遍历所有的方法
            for (Method method : type.getMethods()) {
                // 方法是否可以有SQL语句
                // 不能是桥接方法并且不能是默认方法,才可以写sql
                if (!canHaveStatement(method)) {
                    continue;
                }
                // 解析Statement,映射语句
                parseStatement(method);
            }
        }
    }


    // 加载Mapper.xml
    public void loadXmlResource() {
        // 防止多次加载
        if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
            // 找到Mapper接口文件下的Mapper.xml文件
            String xmlResource = type.getName().replace('.', '/') + ".xml";
            InputStream inputStream = type.getResourceAsStream("/" + xmlResource);
            if (inputStream == null) {
                inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
            }
            if (inputStream != null) {
                // Mapper.xml构建器,解析器
                XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
                // 解析该Mapper.xml
                xmlParser.parse();
            }
        }
    }

    // 解析Statement,映射语句
    public void parseStatement(Method method) {
        // 获取参数类型,如果有参数类型为RowBounds,ResultHandler的话,那么返回ParamMap类型
        final Class<?> parameterTypeClass = getParameterType(method);
        // 获取方法中的@Lang注解的语言驱动信息
        final LanguageDriver languageDriver = getLanguageDriver(method);
        // 获取所有增删查改的注解,类似与xml中的<insert,delete...>标签
        // 就是从该method中,找到注解列表中指定的注解信息,并包装成AnnotationWrapper对象
        Optional<AnnotationWrapper> annotationWrapper = this.getAnnotationWrapper(method, true, statementAnnotationTypes);
        // 如果存在列表中的注解信息,就需要解析该注解
        annotationWrapper.ifPresent(statementAnnotation -> {
            // 构造SQL源,就是通过语言模板对象,解析为指定的SQL源,可以是动态SQl,也可以是静态SQL
            final SqlSource sqlSource = this.buildSqlSource(statementAnnotation.getAnnotation(), parameterTypeClass, languageDriver, method);
            // 获取SQL类型
            final SqlCommandType sqlCommandType = statementAnnotation.getSqlCommandType();
            // 获取方法中Options注解的包装信息,如果不存在返回null
            final Options options = this.getAnnotationWrapper(method, false, Options.class).map(x -> (Options) x.getAnnotation()).orElse(null);
            // 获取该语句的statementID
            final String mappedStatementId = type.getName() + "." + method.getName();
            // 主键生成器
            final KeyGenerator keyGenerator;
            // 保存该映射语句的详细信息
            assistant.addMappedStatement(mappedStatementId, sqlSource, statementType, sqlCommandType, fetchSize, timeout,
                    null, parameterTypeClass, resultMapId, getReturnType(method, type), resultSetType, flushCache, useCache,
                    false, keyGenerator, keyProperty, keyColumn, statementAnnotation.getDatabaseId(), languageDriver,
                    options != null ? nullOrEmpty(options.resultSets()) : null, statementAnnotation.isDirtySelect());
        });
    }
}

// mapper.xml中<insert,select,delete,update>元素的映射语句信息的构造者
public class XMLStatementBuilder extends BaseBuilder {

    // Mapper.xml/Mapper接口构造者的辅助类
    public final MapperBuilderAssistant builderAssistant;
    // 当前<select|insert|delete|update>标签的节点
    public final XNode context;
    // 是否指定了在指定的数据库中执行
    public final String requiredDatabaseId;


    // 解析该select|insert|delete|update标签标签
    public void parseStatementNode() {
        String id = context.getStringAttribute("id");
        String databaseId = context.getStringAttribute("databaseId");
        // 判断该语句是否符合指定数据库的执行条件
        if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
            return;
        }
        // 获取标签名称
        String nodeName = context.getNode().getNodeName();
        // 解析SQL类型
        SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
        // 是否是select标签
        boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
        // 获取标签的属性
        boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
        boolean useCache = context.getBooleanAttribute("useCache", isSelect);
        boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
        // 将解析好的映射语句保存到配置类中
        builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap,
                parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered,
                keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets, dirtySelect);
    }
}

MyBatis中Mapper接口是怎么和XML文件关联起来的 目录1.前言2. `XML` 文件和 `Mapper` 接口的解析2.1. `XML` 文件的解析 1.前言 使用过 MyBatis 的,都知道它有 Mapper 接口和 Mapper.xml 文件,那么它们是如何关联起来的呢?,又是如何一起运作的呢?接下来,我们从源码级别来分析回答这个问题 2. XML 文件和 Mapper 接口的解析 2.1. XML 文件的解析 XMl 文件的解析过程太过复杂,在这里不再过多赘述,详细过程 在这里,我们先从 这里的 2.6 节点谈起,代码如下 private void 阅读详情

相关推荐

MyBatis实现Mapper配置并查询数据

什么是Mapper 在MyBatis工程搭建 中我们主要讲解的是 MyBatis 如何连接数据库,具体执行 SQL 语句使用的是 JDBC 方式 但在实际应用中是不会选择 JDBC 来执行 SQL 的,MyBatis 提供了 Mapper 作为 Java 方法和 SQL 语句之间的桥梁,来帮助我们更好地去使用 SQL Java 接口方法与 SQL 语句以及 mapper 之间的关系如下图所示: 新建Maven项目名为“mybatis-mapper“,设置好Maven版本、配置文件以及Maven仓库 以查询

Await00的博客 973

mapper接口和resource里的mapper.xml文件

在Mapper XML文件中,您可以编写与数据库交互的SQL语句,并使用@Select,@Update等注解为它们提供名称,从而在Mapper接口中调用它们。因此,在使用MyBatis框架时,通常需要配合使用Mapper接口和Mapper XML文件来实现 对数据库进行操作的方法。Mapper接口定义了对数据库进行操作的方法,这些方法通过注解或XML语句来访问数据库。在一个Java项目中使用MyBatis框架进行数据访问时,通常会使用Mapper接口和Mapper XML文件来完成数据的映射。

weixin_52060913的博客 3745

MyBatis中接口和mapper.xml文件的存放位置、以及名称相同问题,spring整合mybatis时mapper文件放哪,以及解决mapper文件放入resources下访问不到

mybatis、mapper、spring、SSM整合

zhangfuping123456789的博客 1万+

3.Mapper.xml 详解

文章目录Mapper.xml 详解1. parameterType2. resultType3. 级联查询3.1 一对多3.2 多对多 Mapper.xml 详解 MyBatis 主要有两个的配置文件:config.xml 和 Mapper.xml,这两个配置文件可以自定义文件名。 config.xml 是全局配置文件,主要配置 MyBatis 的数据源(DataSource),事务管理(TransactionManager)以及打印 SQL 语句,开启二级缓存,注册 Mapper.xml 等。 Map

qq_41775769的博客 1万+

mybatis源码分析(二):Mapper解析

Mapper解析 从上一篇文章我们分析到,Mapper的解析主要分为两类: 解析Mapper接口上的注解 解析xml配置文件 解析Mapper接口 如果想要通过解析Mapper接口上的注解来注册Mapper的话,有两种方法: 在mappers标签下面使用package子标签来指定扫描的Mapper类所在包的位置 在mappers标签下面使用mapper子标签,在该子标签中使用属性class属性来执行Mapper类的全路径名 不管使用上面哪种方法,最终都会将Mapper类交给MapperAnnotati

lxlneversettle的博客 564

Mybaits 源码解析 (三)----- Mapper接口底层原理(为什么Mapper不用写实现类就能访问到数据库?)

上一篇我们讲解到mapperElement方法用来解析mapper,我们这篇文章具体来 看看mapper.xml的解析过程 mappers配置方式 mappers 标签下有许多 mapper 标签,每一个 mapper 标签中配置的都是一个独立的映射配置文件的路径,配置方式有以下几种。 接口信息进行配置 <mappers> <mapper class="or...

微赚开发者技术分享博客 967

MyBatis3源码深度解析(十五)SqlSession的创建与执行(二)Mapper接口和XML配置文件的注册与获取

SqlSession对象创建后,接下来是执行Mapper。执行Mapper的过程可以拆解为三步:注册Mapper接口与XML配置文件;注册MappedStatement对象;调用Mapper方法。

挑灯日记 记录自己 1180

AT91SAM9260搭建LINUX操作系统

一、 编写目的 2 二、 系统搭建 2 1. 下载相关软件包 2 2. Bootstrap编译 3 3. U-BOOT编译 5 4. LINUX编译 7 5. 通过U-BOOT的TFTP命令从TFTP服务器下载系统镜像 9 6. 文件系统制作 10 7. 挂载NFS作为根文件系统 10 三、 总结 14 一、编写目的 本文档用于记录AT91SAM9260 LINUX学

lshiceman的专栏 7410

Mybatis中mapper是怎么和XMl关联起来的

Mybatis中mapper是怎么和XMl关联起来的 从源码来分析,通过Mybatis的都知道,必须指定nameSpace为Mapper的全限定类名。这样就能关联起来。Mapper的实现肯定是动态dialing,在InvocationHandler中做增强。这里就来分析分析具体是怎么做的? 在下面分析的时候,源码看起来比较枯燥,并且涉及到的东西很多。分析的时候设计的东西多,容易走偏。我尽量回归主题。 1. XML文件解析 这里的xml解析比较繁琐,如果逐行来分析的话,很多很多,这里就挑主线来分析了。之后会分

daliucheng的博客 5839

手把手教你用Qt和DSP28335实现串口Bootloader,附赠源码和避坑指南

本文详细介绍了如何使用Qt和DSP28335实现串口Bootloader,包括开发环境搭建、通信协议设计、Qt上位机开发、DSP端Bootloader核心实现以及应用程序工程的特殊配置。通过实战案例和源码分享,帮助开发者快速掌握嵌入式固件升级技术,提升工业设备远程升级的效率和可靠性。

weixin_29176179的博客 133

UHPC的疲劳计算——兼论ModelCode2010的适用性

ModelCode2010的混凝土疲劳计算方法是适用于UHPC的。

George的博客 1322

Mybatis源码解析之Mapper与xml的加载和关联

Mybatis源码解析之Mapper与xml的加载和关联 什么是 MyBatis? 我们去Mybatis的官方文档看一下Mybatis的介绍 MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。 我们进入实战查看M

weixin_43465227的博客 1201

STM32 HAL库ADC+DMA配置详解:从CubeMX到实战调试

在嵌入式系统开发中,模数转换器(ADC)是实现传感器数据采集、信号处理等功能的基石。其工作原理是将连续的模拟信号转换为离散的数字量,为微控制器(MCU)提供可处理的数字信息。为了提升系统效率,直接存储器访问(DMA)技术被引入,它作为独立于CPU的数据搬运工,能在不占用CPU资源的情况下,自动完成外设与内存间的数据传输,从而解放CPU以执行更复杂的控制算法或通信任务。这种ADC与DMA的协同工作模式,对于实现高效、实时的数据流处理具有重要价值,广泛应用于电机控制、环境监测、音频处理等场景。本文聚焦于STM3

weixin_30698297的博客 407

Mybatis源码分析(三):mapper.xml的解析及namespace与Mapper接口的映射

概述 由上一篇文章分析可知,在调用SqlSessionFactoryBuilder的build方法创建SqlSessionFactory对象实例时,会首先调用builder包的xml子包的XMLConfigBuilder解析mybatisConfig.xml文件并创建和保存配置信息到Configuration对象,然后使用该Configuration对象作为参数场景SqlSessionFacto...

源码之下,了无秘密 5761

乐鑫科技 EchoEar 喵伴修改自定义唤醒词的方法 EchoEar喵伴、唤醒词修改、ESP32、AI萌宠、智能音箱、开发者指南

标签:乐鑫科技、EchoEar喵伴、唤醒词修改、ESP32、AI萌宠、智能音箱、开发者指南大家好,我是一名前端开发工程师,最近在玩乐鑫科技推出的时,发现它不仅萌趣十足,还能进行全双工语音交互、多模态识别,甚至能识别你的情绪和与你互动。EchoEar 喵伴是由乐鑫科技和火山引擎扣子大模型团队合作开发的,它使用乐鑫的芯片方案(ESP32 系列)+ 火山引擎扣子大模型,支持 WiFi 连接和本地处理,长记忆能力可以让它记住用户的偏好与习惯。

代码简单说 Vue、JAVA、PHP、Node.js 熟练运用,接口、架构、性能全搞定。 1876

ONVIF协议简介与协议测试工具使用详解

Onvif协议及协议测试工具使用详解

dvlinker的技术专栏 3万+

为啥mybatis的mapper只有接口没有实现类,但它却能工作?

说起mybatis,大伙应该都用过,有些人甚至底层源码都看过了。在mybatis中,mapper接口是没有实现类的,取而代之的是一个xml文件。也就是说我们调用mapper接口,其实是使用了mapper.xml中定义sql完成数据操作。 大家有没想过,为什么mapper没有实现类,它是如何和xml关联起来的? 一个简单的例子 ok,别急,现在我们已经抛出问题,现在我们从demo...

凡是过往,皆为序章 8905

YOLOv8 模型进化全解析:百种创新模块集成方法与实战部署指南(附资料链接)

动态网络 | DeformableConv/DynamicHead| +2.8% || 轻量化卷积 | GhostConv/MobileConv | +1.2% || 多尺度融合 | AFPN/ASFF | +3.5% || 注意力机制 | CA/EMA/SimAM | +1.8-4.2% |

FJN110的博客 110

动态加载子节点_MyBatis初始化加载 Mapper 接口与XML文件

在MyBatis初始化过程中,大致会有以下几个步骤:拉勾IT课小编为大家分解创建Configuration全局配置对象,会往TypeAliasRegistry别名注册中心添加Mybatis需要用到的相关类,并设置默认的语言驱动类为XMLLanguageDriver加载mybatis-config.xml配置文件、Mapper接口中的注解信息和XML映射文件,解析后的配置信息会形成相应的对象并保存到...

weixin_32258691的博客 374

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

mybatis mapper 接口注册流程源码解析

01、前言 在正式介绍 mapper 接口注册源码分析之前, 首先来介绍什么是 mapper, 再其次说明下 mapper 与 sql 关联的两种方式 可能比较多的同学使用的是 xml 的方式, 没有接触过注解定义; 注解定义SQL语句本质上是一种轻量级的配置 02、mapper 接口 2.1 什么是 mapper 接口 mapper 接口是用于执行自定义SQL语句相关的方法 可以在 mapper 接口定义方法上添加SQL方法注解或者使 mapper 接口绑定 xml 文件 2.2 使用注解定义 sql .

Long-Tai 732

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

[Mybatis]-[基础支持层]-mappers标签解析-mapper-sql解析

该系列文章针对 Mybatis 3.5.1 版本 Mybatis 中 <mappers> 标签解析,主要是为了得到两大部分数据 1、Mapper.class 接口 2、SQL 执行语句,结果集映射关系等数据 在上一章中提到了 Mapper.class 文件的解析,Mapper.class 是接口文件,该接口是发起SQl执行调用的 API 入口,所有相关的操作都在其代理类 MapperProxy 中完成。 在上一章中忽略了 Mapper.class 文件中 @Select 等 SQL.

保持敏感!保持学习! 600

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

MyBatis 如何将 Mapper 接口与其 XML 映射文件关联:深入原理与实现

MyBatis 是一个简单、灵活的持久层框架,它通过 SQL 语句将 Java 对象与数据库进行映射。MyBatis 支持基于 XML 和注解的配置方式。在实际开发中,XML 映射文件与 Mapper 接口的关联是 MyBatis 的核心功能之一。通过这种关联,开发者可以在 Mapper 接口中定义方法,并在 XML 文件中编写 SQL 语句,从而实现数据操作。MyBatis 中 Mapper 接口与 XML 文件的关联过程,主要通过namespace和id来匹配。

2301_79858914的博客 6111

Mapper解析

本文解析了MyBatis中Mapper接口与SQL绑定的实现机制。通过MapperAnnotationBuilder和XMLMapperBuilder两种解析器,分别处理注解方式和XML方式的SQL配置,最终统一收敛为MappedStatement。文章详细说明了不同扫描方式(包路径/资源路径)的解析流程差异,并解释了接口与SQL的关联原理。同时指出Mapper接口不支持方法重载,因为MyBatis通过方法名作为SQL标识匹配的关键。这些设计体现了MyBatis为兼容多种使用方式所做的职责划分。

会编程的林俊杰的博客 704

mybatis系列之获取mapper.xml配置文件中的sql

hello~各位读者好,我是鸭血粉丝(大家可以称呼我为「阿粉」)。今天,阿粉带着大家来了解一下获取 mapper.xml 配置文件中的sql上期回顾首先,我们还是回顾一下上篇文件的内容。...

Java极客技术 8001

MyBatis 的 DAO 接口跟 XML 文件里面的 SQL 是如何建立关系的?

MyBatis 是一款优秀的持久层框架,它通过 DAO 接口和 XML 文件来完成对象与数据库的映射关系。DAO 接口定义了各种数据访问方法,而 XML 文件则配置了具体的 SQL 语句。这两者之间如何建立关系和调用关系,对于使用和理解 MyBatis 非常重要。本文将从 MyBatis 工作原理出发,详细说明 DAO 接口和 XML 文件之间的对应关系。同时给出代码示例,直观展示它们在实际操作中的调用过程。

资料免费分享,点击名片 406
上一篇: Mybatis中插件的实现原理
下一篇: Mybatis最全学习文档
安缇吖
博客等级 码龄9年 411粉丝 135原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值