Mybatis中Dao接口的工作原理

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: Mybatis中Dao接口的工作原理

解释:


Dao接口,就是人们常说的Mapper接口,接口的全限名,就是映射文件中的namespace的值,接口的方法名,就是映射文件中MappedStatement的id值,接口方法内的参数,就是传递给sql的参数。Mapper接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为key值,可唯一定位一个MappedStatement,举例:com.mybatis3.mappers.StudentDao.findStudentById,可以唯一找到namespace为com.mybatis3.mappers.StudentDao下面id = findStudentById的MappedStatement。在Mybatis中,每一个<select>、<insert>、<update>、<delete>标签,都会被解析为一个MappedStatement对象。


下面来看这个问题:

如何通过全限名+方法名来定位一个mapper的?


初始化

首先mybatis会把各种mapper映射进行初始化

对于mybatis与spring结合的项目,最开始是从SqlSessionFactoryBean开始

**SqlSessionFactoryBean类**


```java

protected SqlSessionFactory buildSqlSessionFactory() throws IOException {

   //其他省略...

   if (xmlConfigBuilder != null) {

     try {

         //初始化mybatis config配置文件

       xmlConfigBuilder.parse();

       LOGGER.debug(() -> "Parsed configuration file: '" + this.configLocation + "'");

     } catch (Exception ex) {

       throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);

     } finally {

       ErrorContext.instance().reset();

     }

   }

      //其他省略...

       try {

         XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),

             targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());

         //格式化mapper xml文件

         xmlMapperBuilder.parse();

       } catch (Exception e) {

         throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);

       } finally {

         ErrorContext.instance().reset();

       }

       LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");

   //其他省略...

   return this.sqlSessionFactoryBuilder.build(targetConfiguration);

 }

```




**XMLMapperBuilder类**


```java

/**

  * 解析mapper映射配置文件

  */

 public void parse() {

   //判断是否已经加载过该映射文件

   if (!configuration.isResourceLoaded(resource)) {

     configurationElement(parser.evalNode("/mapper"));

     configuration.addLoadedResource(resource);

     //注册 Mapper 接 口

     bindMapperForNamespace();

   }

   //处理 configurationElement ()方法中解析失败的<resultMap>节点

   parsePendingResultMaps();

   //处理 configurationElement ()方法中 解析失败的< cache-ref>节点

   parsePendingCacheRefs();

   //处理 configurationElement ()方法中 解析失败的 SQL 语句节点

   parsePendingStatements();

 }

```


**bindMapperForNamespace()方法**


```java

//绑定Mapper接口

 private void bindMapperForNamespace() {

   //获取当前namespace名称

   String namespace = builderAssistant.getCurrentNamespace();

   if (namespace != null) {

     Class<?> boundType = null;

     try {

       boundType = Resources.classForName(namespace);

     } catch (ClassNotFoundException e) {

       //ignore, bound type is not required

     }

     if (boundType != null) {

       //如果还没有加载

       if (!configuration.hasMapper(boundType)) {

         // Spring may not know the real resource name so we set a flag

         // to prevent loading again this resource from the mapper interface

         // look at MapperAnnotationBuilder#loadXmlResource

         configuration.addLoadedResource("namespace:" + namespace);

         configuration.addMapper(boundType);

       }

     }

   }

 }

```


你从configuration.addMapper(boundType)进入,到最后你会发现,会以类全限定名为key,mapper代理作为value放入knownMappers 中


**MapperRegistry类**


```java


public <T> void addMapper(Class<T> type) {

     //....

     try {

       //放入knownMappers中

       knownMappers.put(type, new MapperProxyFactory<T>(type));

       // It's important that the type is added before the parser is run

       // otherwise the binding may automatically be attempted by the

       // mapper parser. If the type is already known, it won't try.

       MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);

       parser.parse();

       loadCompleted = true;

     } finally {

       if (!loadCompleted) {

         knownMappers.remove(type);

       }

     }

   }

 }

```


另外还有一个地方也会初始化,在初始化mybatis config配置文件的时候,可以看**XMLConfigBuilder.java**中mapperElement方法


### 定位


测试用例


```java

 @Test

 public void shouldSelectBlogWithPostsUsingSubSelect() throws Exception {

   SqlSession session = sqlSessionFactory.openSession();

   try {

     //getMapper返回一个MapperProxy对象

     BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);

     Blog b = mapper.selectBlogWithPostsUsingSubSelect(1);

     assertEquals(1, b.getId());

     session.close();

     assertNotNull(b.getAuthor());

     assertEquals(101, b.getAuthor().getId());

     assertEquals("jim", b.getAuthor().getUsername());

     assertEquals("********", b.getAuthor().getPassword());

     assertEquals(2, b.getPosts().size());

   } finally {

     session.close();

   }

 }

```


MapperProxy类实现了InvocationHandler接口,代理类调用的时候会执行invoke方法

**MapperProxy类**


```java

@Override

 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

   try {

     //如果目标方法继承自Object,则直接调用目标方法

     if (Object.class.equals(method.getDeclaringClass())) {

       return method.invoke(this, args);

     } else if (isDefaultMethod(method)) {

       //对jdk7以上版本,动态语言的支持

       return invokeDefaultMethod(proxy, method, args);

     }

   } catch (Throwable t) {

     throw ExceptionUtil.unwrapThrowable(t);

   }

   //从缓存中获取 MapperMethod对象,如果缓存中没有,则创建新的 MapperMethod对象并添加到缓存中

   final MapperMethod mapperMethod = cachedMapperMethod(method);

   //调用 MapperMethod.execute ()方法执行 SQL 语 句

   return mapperMethod.execute(sqlSession, args);

 }

```


看 cachedMapperMethod(method)方法


```java

private MapperMethod cachedMapperMethod(Method method) {

   MapperMethod mapperMethod = methodCache.get(method);

   if (mapperMethod == null) {

     创建 MapperMethod 对象 , 并添加到 methodCache 集合 中缓存

     mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());

     methodCache.put(method, mapperMethod);

   }

   return mapperMethod;

 }

```


MapperMethod 中封装了 Mapper接口中对应方法的信息,以及对应 SQL 语句的信息


```java

 public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {

   //记录了 SQL语句的名称和类型

   this.command = new SqlCommand(config, mapperInterface, method);

   //Mapper 接 口中对应方法的相关信息

   this.method = new MethodSignature(config, mapperInterface, method);

 }

```


看SqlCommand -->resolveMappedStatement你会发现,sql语句的名称是由:Mapper 接口的名称与对应的方法名称组成的

相关文章
|
11天前
|
SQL Java 数据库连接
Mybatis架构原理和机制,图文详解版,超详细!
MyBatis 是 Java 生态中非常著名的一款 ORM 框架,在一线互联网大厂中应用广泛,Mybatis已经成为了一个必会框架。本文详细解析了MyBatis的架构原理与机制,帮助读者全面提升对MyBatis的理解和应用能力。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
Mybatis架构原理和机制,图文详解版,超详细!
|
1月前
|
SQL Java 数据库连接
mybatis使用四:dao接口参数与mapper 接口中SQL的对应和对应方式的总结,MyBatis的parameterType传入参数类型
这篇文章是关于MyBatis中DAO接口参数与Mapper接口中SQL的对应关系,以及如何使用parameterType传入参数类型的详细总结。
37 10
|
1月前
|
SQL XML Java
Mybatis的原理和MybaitsPlus
这篇文章对比分析了Mybatis和Mybatis Plus的特点与底层实现机制,探讨了两者之间的差异及各自的优势。
65 0
|
3月前
|
XML Java 数据库连接
MyBatis中的接口代理机制及其使用
【8月更文挑战第5天】MyBatis的接口代理机制是其核心功能之一,允许通过定义接口并在运行时生成代理对象来操作数据库。开发者声明一个带有`@Mapper`注解的接口,MyBatis则依据接口方法、映射配置(XML或注解)及数据库信息动态生成代理类。此机制分为四步:创建接口、配置映射文件或使用注解、最后在业务逻辑中注入并使用代理对象。这种方式简化了数据库操作,提高了代码的可读性和可维护性。例如,在电商系统中可通过`OrderMapper`处理订单数据,在社交应用中利用`MessageMapper`管理消息,实现高效且清晰的数据库交互。
|
4月前
|
SQL Java 数据库连接
springboot~mybatis-pagehelper原理与使用
【7月更文挑战第15天】MyBatis-PageHelper是用于MyBatis的分页插件,基于MyBatis的拦截器机制实现。它通过在SQL执行前动态修改SQL语句添加LIMIT子句以支持分页。使用时需在`pom.xml`添加依赖并配置方言等参数。示例代码: PageHelper.startPage(2, 10); List&lt;User&gt; users = userMapper.getAllUsers(); PageInfo&lt;User&gt; pageInfo = new PageInfo&lt;&gt;(users); 这使得分页查询变得简单且能获取总记录数等信息。
111 2
|
4月前
|
SQL Java 数据库连接
Java面试题:简述ORM框架(如Hibernate、MyBatis)的工作原理及其优缺点。
Java面试题:简述ORM框架(如Hibernate、MyBatis)的工作原理及其优缺点。
78 0
MybatisPlus--IService接口基本用法,MP提供了Service接口,save(T) 这里的意思是新增了一个T, saveBatch 是批量新增的意思,saveOrUpdate是增或改
MybatisPlus--IService接口基本用法,MP提供了Service接口,save(T) 这里的意思是新增了一个T, saveBatch 是批量新增的意思,saveOrUpdate是增或改
|
1月前
|
Java 数据库连接 Maven
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和MyBatis Generator,使用逆向工程来自动生成Java代码,包括实体类、Mapper文件和Example文件,以提高开发效率。
118 2
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。
|
1月前
|
SQL JSON Java
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和PageHelper进行分页操作,并且集成Swagger2来生成API文档,同时定义了统一的数据返回格式和请求模块。
56 1
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
|
1月前
|
前端开发 Java Apache
Springboot整合shiro,带你学会shiro,入门级别教程,由浅入深,完整代码案例,各位项目想加这个模块的人也可以看这个,又或者不会mybatis-plus的也可以看这个
本文详细讲解了如何整合Apache Shiro与Spring Boot项目,包括数据库准备、项目配置、实体类、Mapper、Service、Controller的创建和配置,以及Shiro的配置和使用。
353 1
Springboot整合shiro,带你学会shiro,入门级别教程,由浅入深,完整代码案例,各位项目想加这个模块的人也可以看这个,又或者不会mybatis-plus的也可以看这个
下一篇
无影云桌面