【源码解析】Mybatis执行原理

Mybatis执行原理

MyBatis 是一款优秀的持久层框架,MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。

Mybatis中的mapper接口并没有具体实现,那么他是如何执行SQL的?

mybatis-config.xml配置

xml 复制代码
<!-- mybatis-config.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <environments default="development">
    <environment id="development">
      <!-- 使用jdbc事务管理 -->
      <transactionManager type="JDBC" />
      <!-- 数据库连接池 -->
      <dataSource type="POOLED">
        <property name="driver" value="com.mysql.jdbc.Driver" />
        <property name="url"
          value="jdbc:mysql://127.0.0.1:3306/demo?characterEncoding=utf8"/>
        <property name="username" value="" />
        <property name="password" value="" />
      </dataSource>
    </environment>
  </environments>

  <!-- 加载mapper.xml -->
  <mappers>
    <!-- <package name=""> -->
    <mapper resource="mapper/DemoMapper.xml"></mapper>
  </mappers>
</configuration>

从一个简单的mybatis示例开始源码分析:

java 复制代码
public static void main(String[] args) throws Exception {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    //创建SqlSessionFacory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    //获取Mapper
    DemoMapper mapper = sqlSession.getMapper(DemoMapper.class);
    Map<String,Object> map = new HashMap<>();
    map.put("id","1");
    System.out.println(mapper.selectAll(map));
    sqlSession.close();
}

1.获取SqlSessionFactory

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

new SqlSessionFactoryBuilder().build()方法得到SqlSessionFactory实例,并解析mybatis-config.xml文件configuration节点数据源配置及mapper配置到Configuration对象,Configuration是SqlSessionFactory的一个属性。

java 复制代码
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        if (inputStream != null) {
          inputStream.close();
        }
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
}

public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
}

XMLConfigBuilder解析配置并组装SQL语句:

java 复制代码
public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    // 解析mybatis-config.xml文件configuration节点数据源配置及mapper配置到Configuration对象
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
}

private void parseConfiguration(XNode root) {
    try {
      // issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      loadCustomLogImpl(settings);
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      // 读取mapper配置
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
}

2.创建SqlSession

SqlSession sqlSession = sqlSessionFactory.openSession();

通过SqlSession工厂类创建SqlSession,并增加事务处理、执行器等。这里将SqlSessionFactory中的Environment传递给了SqlSession,Environment中包含DataSource,用于创建数据库连接。

java 复制代码
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level,
      boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

3.创建Mapper、执行SQL

DemoMapper mapper = sqlSession.getMapper(DemoMapper.class);

java 复制代码
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
}

在XMLConfigBuilder.parseConfiguration()中configuration.addMapper(mapperInterface)方法对knownMappers初始化,将所有Mapper的代理类放入knownMappers。

复制代码
knownMappers.put(type, new MapperProxyFactory<>(type));

sqlSession.getMapper()从knownMappers获取代理类对象。

mapper.selectAll(map);

代理类通过MapperProxy.invoke()方法执行,自定义代理类会执行cachedInvoker()方法。

java 复制代码
public class MapperProxy<T> implements InvocationHandler, Serializable {
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      }
      return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }
}

最终通过mapperMethod.execute()方法执行对应的SQL语句。

java 复制代码
private static class PlainMethodInvoker implements MapperMethodInvoker {
    private final MapperMethod mapperMethod;

    public PlainMethodInvoker(MapperMethod mapperMethod) {
        this.mapperMethod = mapperMethod;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
        return mapperMethod.execute(sqlSession, args);
    }
}
相关推荐
小鸡脚来咯2 分钟前
java web后端开发流程
java·开发语言·git
北友舰长4 分钟前
基于Springboot+thymeleaf快递管理系统的设计与实现【Java毕业设计·安装调试·代码讲解】
java·spring boot·mysql·校园管理·快递·快递系统
我爱烤冷面5 分钟前
kotlin项目实现Java doc的方案:使用Dokka
java·开发语言·kotlin·dokka
jian110587 分钟前
android java转kotlin,kotlin转java
android·java·kotlin
长征coder9 分钟前
SpringCloud服务优雅下线LoadBalancer 缓存配置方案
java·后端·spring
历程里程碑10 分钟前
C++ 4:内存管理
java·c语言·开发语言·数据结构·c++·笔记·算法
没有bug.的程序员13 分钟前
微服务的本质:不是拆服务,而是拆复杂度
java·jvm·spring·微服务·云原生·容器·架构
武子康19 分钟前
Java-200 RabbitMQ 架构与 Exchange 路由:fanout/direct/topic/headers
java·架构·消息队列·系统架构·rabbitmq·java-rabbitmq·mq
计算机学姐21 分钟前
基于SSM的社区外来务工人员管理系统【2026最新】
java·vue.js·java-ee·tomcat·maven·intellij-idea·mybatis
好学且牛逼的马22 分钟前
HttpServlet 深度拆解:从设计模式看透其核心原理
java·servlet·设计模式