【Mybatis】Mybatis初识-通过源码学习执行流程

news2024/12/23 16:57:56

文章目录

  • 1.Mybatis核心组件
    • 1.1 SqlSession
    • 1.2 SqlSessionFactory
    • 1.3 Mapper
    • 1.4 MappedStatement
    • 1.5 Executor
  • 2. Mybatis各组件之间关系
  • 3. 构建SqlSessionFactory
    • 3.1 从XML文件中构建
    • 3.2 不使用XML构建SqlSessionFactory
  • 4. 如何从SqlSessionFactory获取SqlSession
  • 5.获取Mapper
    • 缓一下!缓一下! 买杯咖啡好嘛!
    • 5.1 解析environments
    • 5.2 解析mapper
      • 5.2.1 解析配置parameterMap
      • 5.2.2 ResultMap
      • 5.2.3 SQL
      • 5.2.4 select|insert|update|delete
  • 6. 总结

Mybatis作为一个优秀的持久层框架,免除了几乎所有的JDBC代码已经设置参数和结果获取的工作。那Mybatis是如何做到的呢?本文主要介绍Mybatis中的一些重要概念。

1.Mybatis核心组件

1.1 SqlSession

类似于JDBC中的Connection,表示和数据库交互的会话。SqlSession提供了一系列的操作数据库的API,包括查询、插入和删除数据等操作。

1.2 SqlSessionFactory

SqlSessionFactory是mybatis的核心组件之一,可以依据配置文件以及JAVA API的方式生成SqlSession对象。
SqlSessionFactory是SqlSession的工厂类,采用工厂模式设计,封装对象创建的过程。

1.3 Mapper

Mapper是Mybatis中的一个抽象概念,表示一类DAO类的接口。每个Mapper接口中定义了对应的SQL操作方法。每个 Mapper 接口中定义了对应 SQL 操作的方法。Mapper 接口中的方法会被 MyBatis 解析成 MappedStatement 对象,与该 SQL 语句对应。

1.4 MappedStatement

MappedStatement 是 MyBatis 用于存储 SQL 语句、入参、出参等相关信息的核心组件。在 MyBatis 中,Mapper 接口中的每个方法都会被解析成一个 MappedStatement 对象。MappedStatement 对象是一个有状态(stateful)对象,包含了 SQL 语句的语法、入参映射、结果映射等相关信息。

1.5 Executor

Executor 是 MyBatis 中的核心组件之一,它主要负责查询语句的执行和结果的返回。Executor 的实现类有三种:SimpleExecutor、ReuseExecutor、BatchExecutor,分别对应于简单执行器、重复执行器和批处理执行器。Executor 提供了追踪和缓存查询结果的功能,能够提高执行效率。

2. Mybatis各组件之间关系

请添加图片描述

3. 构建SqlSessionFactory

从mybatis官网的入门示例中,可以清楚的看到两种不同的SqlSessionFactory创建方式:

  • 从XML中构建SqlSessionFactory
  • 不使用XML构建

3.1 从XML文件中构建

既然是从XML文件中构建,那就一定会有xml文件,官网有给出的示例文件,这里我们参照示例,适当修改如下(放在src/test/resource下):

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/bookstore?serverTimezone=UTC"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
      </dataSource>
    </environment>
  </environments>
  <mappers>
    <mapper resource="UserMapper.xml"/>
  </mappers>
</configuration>

还是使用之前一篇文章中的数据,创建一个测试用例,来测试sqlSession创建成功,并能查询到数据库的数据。这里还需要这样一个UserMapper.xml文件(也放在src/test/resource下)

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0// EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.example.ssm.mapper.UserMapper">
        <select id="findUserByName">
            select * from user
        </select>
</mapper>

这样就可以编写测试用例了:

@org.junit.jupiter.api.Test
public void testSqlSession() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    User tom = sqlSession.getMapper(UserMapper.class).findByName("tom");
    sqlSession.close();
    assertEquals(23, tom.getAge());
}

测试通过!通过XML配置文件构建SqlSessionFactory成功。

3.2 不使用XML构建SqlSessionFactory

当然了,配置的内容不仅可以从xml中读取,也可以从JAVA代码中获取。

@org.junit.jupiter.api.Test
    public void testSqlSessionWithoutXml() throws SQLException {
        DataSource dataSource = new SimpleDriverDataSource(new Driver(), "jdbc:mysql://localhost:3306/bookstore?serverTimezone=UTC", "root", "Yuanyao@123");
        JdbcTransactionFactory factory = new JdbcTransactionFactory();
        Environment environment = new Environment("test", factory, dataSource);
        Configuration configuration = new Configuration(environment);
        configuration.addMapper(UserMapper.class);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        User tom = sqlSession.getMapper(UserMapper.class).findByName("tom");
        sqlSession.close();
        assertEquals(23, tom.getAge());
    }

测试用例也还是一次通过!!

4. 如何从SqlSessionFactory获取SqlSession

在创建完SqlSessionFactory后,通过opensession方法就获取到了sqlSession。那么这个方法里到底包含了哪些内容?

@Override
public SqlSession openSession() {
  return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}

调用了openSessionFromDataSource方法,还传了3个参数:

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();
    }
  }

三个参数的含义分别是Executor的类别,事务的隔离级别已经是否自动提交事务。很明显,这里的事务隔离级别是null,不自动提交事务。而ExecutorType则是从Configuration中获取DefaultExecutorType值SIMPLE。

在这个方法中,既然已经有了事务隔离级别的存在,那么事务必然少不了。这里是通过TransactionFactory来获取,而TransactionFactory又是根据environment配置来的:

private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
    if (environment == null || environment.getTransactionFactory() == null) {
      return new ManagedTransactionFactory();
    }
    return environment.getTransactionFactory();
  }

当然了,如果没有配置,那就new一个默认的喽。
现在,有了transaction和execType,那就可以从Configuration中构建出一个Executor了,有了Configuration和Executor,并且也知道了如何处理transaction,那此时就可以构建一个DefaultSqlSession对象了。

5.获取Mapper

然后从SqlSession中获取mapper,这里SqlSession的接口,定义了方法

<T> T getMapper(Class<T> type);

其实现类包括DefaultSqlSession,这里使用的正是这个:

@Override
  public <T> T getMapper(Class<T> type) {
    return configuration.getMapper(type, this);
  }

而Configuration中的getMapper方法是:

@Override
  public <T> T getMapper(Class<T> type) {
    return configuration.getMapper(type, this);
  }

可以看到这里是由configuration的getMapper方法根据mapper的类来获取的。但是这个configuration是什么时候被初始化的呢?
从代码中看,是通过构造函数初始化的:

public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
    this.configuration = configuration;
    this.executor = executor;
    this.dirty = false;
    this.autoCommit = autoCommit;
  }

而构造函数又是什么时候被调用的?
在构建SqlSessionFactory的时候,调用了构造器构建的时候!

public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
}

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.
      }
    }
  }

而在通过构造器构建XMLConfigBuilder的时候

public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
    this(Configuration.class, inputStream, environment, props);
}

public XMLConfigBuilder(Class<? extends Configuration> configClass, InputStream inputStream, String environment,
      Properties props) {
    this(configClass, new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}

private XMLConfigBuilder(Class<? extends Configuration> configClass, XPathParser parser, String environment,
      Properties props) {
    super(newConfig(configClass));
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
}

在调用XMLConfigBuilder的时候,传入了一个XPathParser类型的参数。这个参数也是通过构造器传入的:

new XPathParser(inputStream, true, props, new XMLMapperEntityResolver())

public XPathParser(Reader reader, boolean validation, Properties variables, EntityResolver entityResolver) {
    commonConstructor(validation, variables, entityResolver);
    this.document = createDocument(new InputSource(reader));
}

private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
    this.validation = validation;
    this.entityResolver = entityResolver;
    this.variables = variables;
    XPathFactory factory = XPathFactory.newInstance();
    this.xpath = factory.newXPath();
 }

这里的构造器设置了一些基本信息:

  • validation为true,需要校验数据
  • entityResolver就是一个XMLMapperEntityResolver,用来解析这个配置的xml文件
  • variables为null,没有设置
  • 通过工厂模式,获取一个Xpath实例

然后调用createDocument解析配置的xml配置文件:

private Document createDocument(InputSource inputSource) {
    // important: this must only be called AFTER common constructor
    try {
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
      factory.setValidating(validation);

      factory.setNamespaceAware(false);
      factory.setIgnoringComments(true);
      factory.setIgnoringElementContentWhitespace(false);
      factory.setCoalescing(false);
      factory.setExpandEntityReferences(true);

      DocumentBuilder builder = factory.newDocumentBuilder();
      builder.setEntityResolver(entityResolver);
      builder.setErrorHandler(new ErrorHandler() {
        @Override
        public void error(SAXParseException exception) throws SAXException {
          throw exception;
        }

        @Override
        public void fatalError(SAXParseException exception) throws SAXException {
          throw exception;
        }

        @Override
        public void warning(SAXParseException exception) throws SAXException {
          // NOP
        }
      });
      return builder.parse(inputSource);
    } catch (Exception e) {
      throw new BuilderException("Error creating document instance.  Cause: " + e, e);
    }
  }

这里主要是读取并解析mybatis-config.xml文件为一个Document(解析xml文件过程暂时不作深入探讨,如有必要,另开一篇详细探讨)。
XMLConfigBuilder的父类构造器,super(new Config(configuration)),这里new了一个Config对象,传入的参数是一个配置类,这个类的就是org.apache.ibatis.session.Configuration。而这个类里的MapperRegistry参数是这样初始化的

protected final MapperRegistry mapperRegistry = new MapperRegistry(this);

而newConfig(configClass)方法是个静态方法:

private static Configuration newConfig(Class<? extends Configuration> configClass) {
    try {
      return configClass.getDeclaredConstructor().newInstance();
    } catch (Exception ex) {
      throw new BuilderException("Failed to create a new Configuration instance.", ex);
    }
  }

只是将Configuration类实例化了。
在super(newconfig(configuration))方法中只是初始化:

public BaseBuilder(Configuration configuration) {
    this.configuration = configuration;
    this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
    this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
}

到这里,总算把XMLConfigBuilder构建出来啦!

缓一下!缓一下!
买杯咖啡好嘛!

如果我填坑了,给我也买一杯好嘛!
喝完咖啡继续!

饶了很远,但是目标不能忘,构建SqlSessionFactory!接着看build(parser.parse())方法:
首先看XMLConfigBuilder的pares方法:

public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
 }

其中

parser.evalNode(“/configuration”)

就是读取Document中configuration节点的内容,然后这个XNode被parseConfiguration用来解析其中的配置:

private void parseConfiguration(XNode root) {
    try {
      // issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfsImpl(settings);
      loadCustomLogImpl(settings);
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginsElement(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"));
      typeHandlersElement(root.evalNode("typeHandlers"));
      mappersElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

从这里可以看到,可以配置的内容很多,包括properties、settings、plugins等等。我这里暂时只配置了environments和mappers节点。

5.1 解析environments

environments节点中包含了连接数据所需要的基本信息,主要包括两部分内容,一部分就是transactionManager,一部分是dataSource

private void environmentsElement(XNode context) throws Exception {
    if (context == null) {
      return;
    }
    if (environment == null) {
      environment = context.getStringAttribute("default");
    }
    for (XNode child : context.getChildren()) {
      String id = child.getStringAttribute("id");
      if (isSpecifiedEnvironment(id)) {
        TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
        DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
        DataSource dataSource = dsFactory.getDataSource();
        Environment.Builder environmentBuilder = new Environment.Builder(id).transactionFactory(txFactory)
            .dataSource(dataSource);
        configuration.setEnvironment(environmentBuilder.build());
        break;
      }
    }
  }

我这里配置了transactionManager为JDBC类型,在dataSource下配置了连接数据的地址,用户名和密码。

5.2 解析mapper

private void mappersElement(XNode context) throws Exception {
    if (context == null) {
      return;
    }
    for (XNode child : context.getChildren()) {
      if ("package".equals(child.getName())) {
        String mapperPackage = child.getStringAttribute("name");
        configuration.addMappers(mapperPackage);
      } else {
        String resource = child.getStringAttribute("resource");
        String url = child.getStringAttribute("url");
        String mapperClass = child.getStringAttribute("class");
        if (resource != null && url == null && mapperClass == null) {
          ErrorContext.instance().resource(resource);
          try (InputStream inputStream = Resources.getResourceAsStream(resource)) {
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource,
                configuration.getSqlFragments());
            mapperParser.parse();
          }
        } else if (resource == null && url != null && mapperClass == null) {
          ErrorContext.instance().resource(url);
          try (InputStream inputStream = Resources.getUrlAsStream(url)) {
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url,
                configuration.getSqlFragments());
            mapperParser.parse();
          }
        } else if (resource == null && url == null && mapperClass != null) {
          Class<?> mapperInterface = Resources.classForName(mapperClass);
          configuration.addMapper(mapperInterface);
        } else {
          throw new BuilderException(
              "A mapper element may only specify a url, resource or class, but not more than one.");
        }
      }
    }
  }

可以看到,通过mapper标签配置mybatis至少有2种配置方式:

  • 1.配置package,并添加属性name:

    <package name=“org.example.ssm.mapper”/>

  • 2.配置 resource, url, class,三个其中一个

    <mappers>
    <mapper resource=“UserMapper.xml”/>
    <mapper class=“org.example.ssm.mapper.UserMapper.class”/>
    <mapper url=“http://userMapper.xml”/>
    </mappers>
    这样都可以读取,以resource方式读取为例:

ErrorContext.instance().resource(resource);
try (InputStream inputStream = Resources.getResourceAsStream(resource)) {
  XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource,
      configuration.getSqlFragments());
  mapperParser.parse();
}

这里首先通过构造函数创建了一个XMLMapperBuilder,同样,也会创建一个XPathParser去解析xml文件。
由XMLMapperBuilder.parse()方法来构建mapper:

public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
      bindMapperForNamespace();
    }
    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
  }

private 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);
      cacheRefElement(context.evalNode("cache-ref"));
      cacheElement(context.evalNode("cache"));
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      sqlElement(context.evalNodes("/mapper/sql"));
      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);
    }
  }

可以看到,还会去解析mapper这个element下的各个元素:

  • cache-ref
  • cache
  • parameterMap
  • resultMap
  • sql
  • select|insert|update|delete

5.2.1 解析配置parameterMap

ParameterMap 是用来定义 SQL 语句中的参数映射关系的。通过 ParameterMap,可以将 Java 对象中的属性映射到 SQL 语句中的参数,从而实现参数的传递和绑定。

ParameterMap 的作用包括:

    1. 简化 SQL 语句中的参数设置:通过 ParameterMap 可以将 Java 对象中的属性直接映射到 SQL 语句中的参数,避免了在 SQL 语句中重复设置参数。
    1. 提高代码的可维护性:将 SQL 语句中的参数映射关系集中在 ParameterMap 中管理,便于统一维护和修改。
    1. 提高代码的重用性:可以在多个 SQL 语句中重复使用同一个 ParameterMap,减少重复的设置参数的工作。
    1. 支持更复杂的参数映射关系:ParameterMap 可以定义更复杂的参数映射关系,例如多个参数的组合、嵌套对象等。
private void parameterMapElement(List<XNode> list) {
    for (XNode parameterMapNode : list) {
      String id = parameterMapNode.getStringAttribute("id");
      String type = parameterMapNode.getStringAttribute("type");
      Class<?> parameterClass = resolveClass(type);
      List<XNode> parameterNodes = parameterMapNode.evalNodes("parameter");
      List<ParameterMapping> parameterMappings = new ArrayList<>();
      for (XNode parameterNode : parameterNodes) {
        String property = parameterNode.getStringAttribute("property");
        String javaType = parameterNode.getStringAttribute("javaType");
        String jdbcType = parameterNode.getStringAttribute("jdbcType");
        String resultMap = parameterNode.getStringAttribute("resultMap");
        String mode = parameterNode.getStringAttribute("mode");
        String typeHandler = parameterNode.getStringAttribute("typeHandler");
        Integer numericScale = parameterNode.getIntAttribute("numericScale");
        ParameterMode modeEnum = resolveParameterMode(mode);
        Class<?> javaTypeClass = resolveClass(javaType);
        JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
        Class<? extends TypeHandler<?>> typeHandlerClass = resolveClass(typeHandler);
        ParameterMapping parameterMapping = builderAssistant.buildParameterMapping(parameterClass, property,
            javaTypeClass, jdbcTypeEnum, resultMap, modeEnum, typeHandlerClass, numericScale);
        parameterMappings.add(parameterMapping);
      }
      builderAssistant.addParameterMap(id, parameterClass, parameterMappings);
    }
  }

可以看到,这里可以配置的属性有很多,有property,javaType,jdbcType等等。
不过从官网得知,这个元素已经被废弃,后序不再使用了。

5.2.2 ResultMap

resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来,并在一些情形下允许你进行一些 JDBC 不支持的操作。实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份 resultMap 能够代替实现同等功能的数千行代码。

ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。

private void resultMapElements(List<XNode> list) {
    for (XNode resultMapNode : list) {
      try {
        resultMapElement(resultMapNode);
      } catch (IncompleteElementException e) {
        // ignore, it will be retried
      }
    }
  }


private ResultMap resultMapElement(XNode resultMapNode) {
    return resultMapElement(resultMapNode, Collections.emptyList(), null);
  }

  private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings,
      Class<?> enclosingType) {
    ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
    String type = resultMapNode.getStringAttribute("type", resultMapNode.getStringAttribute("ofType",
        resultMapNode.getStringAttribute("resultType", resultMapNode.getStringAttribute("javaType"))));
    Class<?> typeClass = resolveClass(type);
    if (typeClass == null) {
      typeClass = inheritEnclosingType(resultMapNode, enclosingType);
    }
    Discriminator discriminator = null;
    List<ResultMapping> resultMappings = new ArrayList<>(additionalResultMappings);
    List<XNode> resultChildren = resultMapNode.getChildren();
    for (XNode resultChild : resultChildren) {
      if ("constructor".equals(resultChild.getName())) {
        processConstructorElement(resultChild, typeClass, resultMappings);
      } else if ("discriminator".equals(resultChild.getName())) {
        discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
      } else {
        List<ResultFlag> flags = new ArrayList<>();
        if ("id".equals(resultChild.getName())) {
          flags.add(ResultFlag.ID);
        }
        resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
      }
    }
    String id = resultMapNode.getStringAttribute("id", resultMapNode.getValueBasedIdentifier());
    String extend = resultMapNode.getStringAttribute("extends");
    Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
    ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator,
        resultMappings, autoMapping);
    try {
      return resultMapResolver.resolve();
    } catch (IncompleteElementException e) {
      configuration.addIncompleteResultMap(resultMapResolver);
      throw e;
    }
  }

官网的解释很到位,这里我不做无用摘抄,直接上链接!mybatis官网

5.2.3 SQL

这里也就是解析配置的内容,挨个按顺序解析:

private void sqlElement(List<XNode> list) {
    if (configuration.getDatabaseId() != null) {
      sqlElement(list, configuration.getDatabaseId());
    }
    sqlElement(list, null);
  }

  private void sqlElement(List<XNode> list, String requiredDatabaseId) {
    for (XNode context : list) {
      String databaseId = context.getStringAttribute("databaseId");
      String id = context.getStringAttribute("id");
      id = builderAssistant.applyCurrentNamespace(id, false);
      if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) {
        sqlFragments.put(id, context);
      }
    }
  }

5.2.4 select|insert|update|delete

这个是去构建statement,以便可以具体execute语句,可以对数据库进行增删改查。通过statement或者实现类preparedStatement操作已经是JDBC链接操作数据库的基本操作了。

private void buildStatementFromContext(List<XNode> list) {
    if (configuration.getDatabaseId() != null) {
      buildStatementFromContext(list, configuration.getDatabaseId());
    }
    buildStatementFromContext(list, null);
  }

  private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    for (XNode context : list) {
      final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context,
          requiredDatabaseId);
      try {
        statementParser.parseStatementNode();
      } catch (IncompleteElementException e) {
        configuration.addIncompleteStatement(statementParser);
      }
    }
  }

至此,我们似乎已经把需要的东西全部准备好了:

  • 连接数据库的基本信息,包括数据库地址、用户名、密码、transactionManager
  • mapper,定义用什么数据作参数,获得什么样的数据,并如何转化为什么形式。

可别忘了,咱这是在干嘛,是在创建SqlSessionFactory!有了配置文件,就直接调用DefaultSqlSessionFactory的构造函数创建就好了。

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

创建好了SqlSessionFactory,那就可以获取SqlSession了,然后就终于可以获取Mapper了!

接着看SqlSession的getMapper方法:

@Override
  public <T> T getMapper(Class<T> type) {
    return configuration.getMapper(type, this);
}

跟到Configuration类中的实现方法:

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
}

而MapperRegistry的getMapper方法:

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);
    }
  }

从knownMappers中根据type获取MapperProxyFactory,然后创建其实例,即UserMapper。

protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

最终获取到这个mapper,然后调用mapper的findByName方法。

6. 总结

mybatis本身看起来并不复杂,学习难度与大名鼎鼎的hibernate相比,要小很多。整体流程非常清晰,其核心就是SqlSession。围绕SqlSession的创建,以及通过mapper定义Sql语句,通过SqlSession创建statement对数据库进行操作,然后对返回数据作映射。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STM32CubeMx的学习记录系列(1) - 软件的下载与点灯

目录 因为最近要学STM32的嵌入式AI开发&#xff0c;但它于是基于STM32CubeMX开发的&#xff0c;就顺便把这个学了。 直接百度STM32CubeMX&#xff0c;到意法的官网去下载。下载过程就看这篇博客 https://blog.csdn.net/as480133937/article/details/98885316 点灯 选择芯片&…

PG备份与恢复

一、开启WAL归档 1、创建归档目录 我们除了存储数据目录pgdata之外&#xff0c;还要创建backups&#xff0c;scripts&#xff0c;archive_wals文件 mkdir -p /home/mydba/pgdata/arch mkdir -p /home/mydba/pgdata/scripts mkdir -p /home/mydba/backups chown -R mydba.myd…

PIP一些问题解决办法

研究生期间遇到关于PIP一些问题报错以及解决办法的汇总 pip安装报错&#xff1a;is not a supported wheel on this platform 本节转自 https://blog.csdn.net/happywlg123/article/details/107281936 ​ 出现这个问题&#xff0c;是由于这个whl和系统python版本不匹配导致的。…

数字人解决方案——数字人类不仅仅是长着一张脸的人工智能

数字人类曾经是简单的聊天机器人&#xff0c;经常误解问题&#xff0c;这让许多人感到沮丧。现在&#xff0c;他们已经发展成为先进的虚拟代理&#xff0c;可以像最好的客户服务代表一样有效地沟通&#xff0c;拥有专家级的知识&#xff0c;并且看起来与真人惊人地相似。 这些…

基于协同过滤的电影推荐与大数据分析的可视化系统

基于协同过滤的电影推荐与大数据分析的可视化系统 在大数据时代&#xff0c;数据分析和可视化是从大量数据中提取有价值信息的关键步骤。本文将介绍如何使用Python进行数据爬取&#xff0c;Hive进行数据分析&#xff0c;ECharts进行数据可视化&#xff0c;以及基于协同过滤算法…

<电力行业> - 《第7课:发电》

1 发电的原理 电力生产的发电环节是利用电能生产设备将各种一次能源或其他形式的能转换为电能。生产电能的主要方式有火力发电、水力发电、核能发电、地热发电、风力发电、太阳能发电、潮汐能发电、生物智能发电和燃料电池发电等。 除太阳能发电的光伏电池技术和燃料电池发电…

[单机版架设]新天堂2-死亡骑士338|带AI机器人

前言 今天给大家带来一款单机游戏的架设&#xff1a;新天堂2-死亡骑士338单机服务端—带AI机器人 如今市面上的资源参差不齐&#xff0c;大部分的都不能运行&#xff0c;本人亲自测试&#xff0c;运行视频如下&#xff1a; 新天堂2 搭建教程 此游戏架设不需要虚拟机&#xf…

利用LLM本身训练SoTA embedding模型

今天分享一篇Microsoft公司的一篇文章&#xff0c;Title: Improving Text Embeddings with Large Language Models&#xff1a;使用大语言模型改善文本嵌入。 这篇文章探索了直接利用LLM来做embedding模型&#xff0c;其只需要利用合成数据和少于1000次的训练步骤就能获得高质…

Arthas快速入门

简介 Arthas 是一款线上监控诊断产品&#xff0c;通过全局视角实时查看应用 load、内存、gc、线程的状态信息&#xff0c;并能在不修改应用代码的情况下&#xff0c;对业务问题进行诊断&#xff0c;包括查看方法调用的出入参、异常&#xff0c;监测方法执行耗时&#xff0c;类…

仓库管理系统12--供应商设置

1、添加供应商窗体 2、布局控件UI <UserControl x:Class"West.StoreMgr.View.SupplierView"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mc"http://…

什么是机器学习,机器学习与人工智能的区别是什么(一)?

人工智能和计算机游戏领域的先驱阿瑟塞缪尔&#xff08;Arthur Samuel&#xff09;创造了 "机器学习"一词。他将机器学习定义为 “一个让计算机无需明确编程即可学习的研究领域” 。通俗地说&#xff0c;机器学习&#xff08;ML&#xff09;可以解释为根据计算机的经…

前端学习笔记(2406261):jquery使用checkbox控制页面自动刷新

文章目录 需求登录页面主页面 API用户登录login获取数据getdata 代码登录页面主页面 关于后端 需求 这是一个物联网的演示项目&#xff0c;web端能够实时显示后台数据的变化&#xff0c;其流程非常简单&#xff1a; 用户登录登录成功后显示主界面面主界面进入后自动显示数据数…

Java中的Checked Exception和Unchecked Exception的区别

在Java中&#xff0c;异常分为两大类&#xff1a;已检查异常&#xff08;Checked Exception&#xff09;和未检查异常&#xff08;Unchecked Exception&#xff09;。 已检查异常是在编译时必须被捕获或声明的异常。换句话说&#xff0c;如果你的方法可能会抛出某个已检查异常&…

古人的智慧结晶——水铳:揭秘明清时期的消防神器

明代的《奇器图说》是一本记录了当时各种奇巧机械的著作&#xff0c;而水铳则是书中记载的一项令人惊叹的发明&#xff0c;它不仅展示了古人对物理原理的深刻理解&#xff0c;更是早期消防技术的一个缩影。 水铳&#xff0c;这个名字听起来似乎有些陌生&#xff0c;但在古代&am…

Kafka~消息发送过程与ISR机制了解

消息发送过程 使用Kafka发送消息时&#xff0c;一般有两种方式分别是&#xff1a; 同步发送异步发送 同步发送时&#xff0c;可以在发送消息后&#xff0c;通过get方法等待消息结果&#xff0c;这种情况能够准确的拿到消息最终的发送结果&#xff0c;要么是成功、要么是失败…

AES加密算法及AES-CMAC原理白话版系统解析

本文框架 前言1. AES加密理论1.1 不同AES算法区别1.2 加密过程介绍1.2.1 加密模式和填充方案选择1.2.2 密钥扩展1.2.3分组处理1.2.4多轮加密1.2.4.1字节替换1.2.4.2行移位1.2.4.3列混淆1.2.4.4轮密钥加1.3 加密模式1.3.1ECB模式1.3.2CBC模式1.3.3CTR模式1.3.4CFB模式1.3.5 OFB模…

社团成员信息系统

ER实体关系图与数据库模型 DDL CREATE TABLE club (club_id int(11) NOT NULL AUTO_INCREMENT,club_name varchar(100) NOT NULL,president_name varchar(50) DEFAULT NULL,foundation_date date DEFAULT NULL,description text,PRIMARY KEY (club_id),KEY president_name (pr…

虚拟化技术(二)

目录 三、存储虚拟化&#xff08;一&#xff09;存储虚拟化的一般模型&#xff08;二&#xff09;存储虚拟化的实现方式&#xff08;三&#xff09;案例分析 四、网络虚拟化&#xff08;一&#xff09;核心层网络虚拟化&#xff08;二&#xff09;接入层网络虚拟化&#xff08;…

生成独立的zedboard+ad9361起始项目

文件分享 链接&#xff1a;https://pan.baidu.com/s/17wB_9xVWjO7HhxNvmmZyuA 提取码&#xff1a;94zz 首先下载HDL和NO-OS项目 git clone --recursive https://github.com/analogdevicesinc/hdl git clone --recursive https://github.com/analogdevicesinc/no-OS下载…

L03_Redis知识图谱

这些知识点你都掌握了吗?大家可以对着问题看下自己掌握程度如何?对于没掌握的知识点,大家自行网上搜索,都会有对应答案,本文不做知识点详细说明,只做简要文字或图示引导。 Redis 全景图 Redis 知识全景图都包括什么呢?简单来说,就是“两大维度,三大主线”。 Redis …