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

文章目录

  • 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 configurationPUBLIC "-//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 mapperPUBLIC "-//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.Testpublic 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,这里使用的正是这个:

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

而Configuration中的getMapper方法是:

@Overridepublic <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 constructortry {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() {@Overridepublic void error(SAXParseException exception) throws SAXException {throw exception;}@Overridepublic void fatalError(SAXParseException exception) throws SAXException {throw exception;}@Overridepublic 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 firstpropertiesElement(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 #631environmentsElement(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方法:

@Overridepublic <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.mzph.cn/web/36818.shtml

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

相关文章

【AMBA】APB总线的个人学习记录(一):理论知识

精通APB (Advanced Peripheral Bus) 总线设计通常意味着你不仅理解其基础概念&#xff0c;而且能够在实际的硬件设计中灵活运用APB总线&#xff0c;解决复杂问题&#xff0c;并优化设计。以下是一些关键点&#xff0c;当你掌握这些方面时&#xff0c;可以说你对APB总线设计有了…

《昇思25天学习打卡营第6天|onereal》

Vision Transformer&#xff08;ViT&#xff09;简介 近些年&#xff0c;随着基于自注意&#xff08;Self-Attention&#xff09;结构的模型的发展&#xff0c;特别是Transformer模型的提出&#xff0c;极大地促进了自然语言处理模型的发展。由于Transformers的计算效率和可扩…

[OtterCTF 2018]Bit 4 Bit

我们已经发现这个恶意软件是一个勒索软件。查找攻击者的比特币地址。** 勒索软件总喜欢把勒索标志丢在显眼的地方&#xff0c;所以搜索桌面的记录 volatility.exe -f .\OtterCTF.vmem --profileWin7SP1x64 filescan | Select-String “Desktop” 0x000000007d660500 2 0 -W-r-…

数据库系统概论(第5版教材)

第一章 绪论 1、数据(Data)是描述事物的符号记录&#xff1b; 2、数据库系统的构成&#xff1a;数据库 、数据库管理系统&#xff08;及其开发工具&#xff09; 、应用程序和数据库管理员&#xff1b; 3、数据库是长期存储在计算机内、有组织、可共享的大量数据的集合&…

带上作弊器,我不得起飞

前言 过去,我们对人工智能既期待又害怕.人类的惰性希望人工智能可以帮助大家从大部分繁重的工作中解放出来,但又害怕它失控. 智能系统的好处 工作方面 自动化与效率提升&#xff1a;可以自动执行许多重复性和低技能的任务&#xff0c;如制造业中的装配、数据输入和办公室的客户…

原子变量原理剖析

一、原子操作 原子操作保证指令以原子的方式执行&#xff0c;执行过程不被打断。先看一个实例&#xff0c;如下所示&#xff0c;如果thread_func_a和thread_func_b同时运行&#xff0c;执行完成后&#xff0c;i的值是多少&#xff1f; // test.c static int i 0;void thread…

多表执行嵌套查询,减少笛卡尔积,防止内存溢出

问题&#xff1a;当涉及四个表的查询时&#xff0c;会产生大量的笛卡尔积导致内存溢出。 解决办法 &#xff1a;可以使用嵌套查询将多表的联合查询拆分为单个表的查询&#xff0c;使用resultmap中的association&#xff08;适合一对一&#xff09; 或 collection&#xff08;一…

医院消防设施设备管理系统

医院为人员密集场所&#xff0c;且多为各类病患及其陪护人员&#xff0c;一旦发生火灾&#xff0c;人员疏散逃生困难&#xff0c;容易造成较严重的生命与财产损失。为规范医院的消防设施设备管理&#xff0c;通过凡尔码系统对医院消防设施设备进行信息化管理&#xff0c;提高医…

MapReduce学习

目录 7.3 MapReduce工作流程 7.3.1 工作流程概述 7.3.2 MapReduce各个执行阶段 7.3.3 Shuffle过程详解 1. Shuffle过程简介&#xff08;过程分为Map端的操作和Reduce端的操作&#xff09; 2、Map端的Shuffle过程&#xff1a; 3、在Reduce端的Shuffle过程 7.4 实例分析&am…

使用supportFragmentManager管理多个fragment切换

android studio创建的项目就没有一个简单点的框架&#xff0c;生成的代码都是繁琐而复杂&#xff0c;并且不实用。 国内的页面一般都是TAB页面的比较多&#xff0c;老外更喜欢侧边菜单。 如果我们使用一个activity来创建程序&#xff0c;来用占位符管理多个fragment切换&…

五、Spring IoCDI ★ ✔

5. Spring IoC&DI 1. IoC & DI ⼊⻔1.1 Spring 是什么&#xff1f;★ &#xff08;Spring 是包含了众多⼯具⽅法的 IoC 容器&#xff09;1.1.1 什么是容器&#xff1f;1.1.2 什么是 IoC&#xff1f;★ &#xff08;IoC: Inversion of Control (控制反转)&#xff09;总…

Python逻辑控制语句 之 判断语句--if、if else 和逻辑运算符结合

逻辑运算符&#xff1a; and or not 1.案例一 需求&#xff1a; 1. 获取⽤户输⼊的⽤户名和密码 2. 判断⽤户名是 admin 并且密码是 123456 时, 在控制台输出: 登录成功! 3. 否则在控制台输出: 登录信息错误! # 需求&#xff1a; # 1. 获取用户输入的用户名和密码 # 2. 判断…

【折腾笔记】兰空图床使用Redis做缓存

前言 最近发现我部署在群晖NAS上的兰空图床程序在高并发的情况下会导致图片加载缓慢或出现图片加载失败的情况&#xff0c;于是我查阅了官方文档资料并进行了一系列的测试&#xff0c;发现兰空图床如果开启了原图保护功能&#xff0c;会非常的吃CPU的性能&#xff0c;尤其是在…

【Python游戏】猫和老鼠

本文收录于 《一起学Python趣味编程》专栏,从零基础开始,分享一些Python编程知识,欢迎关注,谢谢! 文章目录 一、前言二、代码示例三、知识点梳理四、总结一、前言 本文介绍如何使用Python的海龟画图工具turtle,开发猫和老鼠游戏。 什么是Python? Python是由荷兰人吉多范…

【限免】线性调频信号的脉冲压缩及二维分离SAR成像算法【附MATLAB代码】

文章来源&#xff1a;微信公众号&#xff1a;EW Frontier QQ交流群&#xff1a;949444104 程序一 对线性调频信号进行仿真&#xff0c;输出其时频域的相关信息&#xff0c;并模拟回波信号&#xff0c; 对其进行脉冲压缩和加窗处理。 实验记录&#xff1a; 1.线性调频信号时…

从0构建一个录制UI测试工具

很多UI自动化测试工具都具备录制UI自动化测试的能力&#xff0c;例如playwright&#xff0c;可以通过playwright vscode插件完成录制&#xff0c;如下图所示&#xff0c;当选择录制脚本时&#xff0c;会打开一个浏览器&#xff0c;在浏览器中输入被测应用url&#xff0c;用户在…

C++:enum枚举共用体union

enum枚举 C继承C的枚举用法 (1)典型枚举类型定义&#xff0c;枚举变量定义和使用 (2)枚举类型中的枚举值常量不能和其他外部常量名称冲突&#xff1a; 举例1宏定义&#xff0c;举例2另一个枚举 // 定义一个名为Color的枚举类型 enum Color {RED, // 红色&#xff0c;默认值…

昇思25天学习打卡营第11天|SSD目标检测

1. 学习内容复盘 模型简介 SSD&#xff0c;全称Single Shot MultiBox Detector&#xff0c;是Wei Liu在ECCV 2016上提出的一种目标检测算法。使用Nvidia Titan X在VOC 2007测试集上&#xff0c;SSD对于输入尺寸300x300的网络&#xff0c;达到74.3%mAP(mean Average Precision)…

JAVA毕业设计145—基于Java+Springboot+vue+uniapp的驾校预约小程序(源代码+数据库+15000字论文)

毕设所有选题&#xff1a; https://blog.csdn.net/2303_76227485/article/details/131104075 基于JavaSpringbootvueuniapp的驾校预约小程序(源代码数据库15000字论文)145 一、系统介绍 本项目前后端分离&#xff0c;分为用户、教练、管理员三种角色 1、用户&#xff1a; …

ModuleNotFoundError: No module named ‘_sysconfigdata_x86_64_conda_linux_gnu‘

ModuleNotFoundError: No module named _sysconfigdata_x86_64_conda_linux_gnu 1.软件环境⚙️2.问题描述&#x1f50d;3.解决方法&#x1f421;4.结果预览&#x1f914; 1.软件环境⚙️ Ubuntu 20.04 Python 3.7.0 2.问题描述&#x1f50d; 今天发现更新conda之后&#xff0…