MyBatis初始化基本过程

MyBatis初始化方式

MyBatis初始化提供了两种方式:

  • 基于XML配置文件:基于XML配置文件的方式是将MyBatis的所有配置信息放在XML文件中mybatis-config.xmlMyBatis通过加载并XML配置文件,将配置文信息组装成内部的Configuration对象
  • 基于Java API:基于Java API的方式是手动创建Configuration对象,然后将配置参数set 进入Configuration对象中。

  任何框架的初始化,应该都是先加载配置信息,接下来我们将使用基于XML配置文件的方式,来深入讨论MyBatis是如何通过配置文件构建Configuration对象。

基于Xml配置初始化

  XML 配置文件中包含了对 MyBatis 系统的核心设置,包括获取数据库连接实例的数据源(DataSource)以及决定事务作用域和控制方式的事务管理器(TransactionManager)。

通过一个简单的例子,分析一下基于Xml配置MyBatis是怎样完成初始化的,都做了些什么?

  • 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>
  <settings>
    <!-- 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载 -->
    <setting name="lazyLoadingEnabled" value="true"/>

    <!-- 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。-->
    <setting name="logImpl" value="STDOUT_LOGGING"/>

    <!--是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn -->
    <setting name="mapUnderscoreToCamelCase" value="true"/>

  </settings>

  <!-- 定义数据库的信息,默认使用development数据库构建环境 -->
  <environments default="development">
    <environment id="development">
      <!-- 使用了 JDBC 的提交和回滚功能,它依赖从数据源获得的连接来管理事务作用域 -->
      <transactionManager type="JDBC"/>
      
      <!-- 数据库信息替换为自己的环境 -->
      <dataSource type="POOLED">
        <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/work"/>
        <property name="username" value="xxxx"/>
        <property name="password" value="xxxx"/>
      </dataSource>
    </environment>
  </environments>


  <!-- 定义映射器 -->
  <mappers>
    <mapper class="org.apache.ibatis.example.mapper.ScheduleSettingMapper"/>
  </mappers>
</configuration>

MyBatis配置项提供了很多配置项,这个配置文件中只配置了一些基本的节点,只是用来演示。

  如果有对MyBatis配置项不了解的或者不知道MyBatis提供哪些配置,可以去看看MyBatis官网文档,文档上对每一个配置项都已经做出了很详细的说明和示例。

  • 程序入口代码
String resource = "mybatis-config.xml";
InputStream inputStream = null;
try {
  inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
}
SqlSessionFactory sqlSessionFactory = null;
//初始化
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

  上述代码的功能是通过Resources工具类,调用ClassLoader读取classpath下的mybatis-config.xml配置文件,得到一个输入流inputStream,SqlSessionFactoryBuilder根据传入的数据流生成Configuration对象,然后根据Configuration对象创建默认的SqlSessionFactory实例

源码分析

  前面提到,SqlSessionFactoryBuilder根据传入的数据流生成Configuration对象,然后根据Configuration对象创建默认的SqlSessionFactory实例,现在让我们通过源码来一步一步看一看

  • 调用SqlSessionFactoryBuilder对象的build(inputStream)方法
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

  SqlSessionFactoryBuilder是SqlSessionFactory的构造器,用于创建SqlSessionFactory,采用了Builder设计模式

  • SqlSessionFactoryBuilder会根据输入流inputStream等创建XMLConfigBuilder对象
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
  try {
    // 创建XMLConfigBuilder对象用来解析XML配置文件生成Document对象,创建Configuration对象
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);

    // XMLConfigBuilder 对象调用parse()方法,将XML配置文件内的信息解析成Configuration对象  
    Configuration configuration = parser.parse();
    
    // 根据解析好的Configuration对象,创建DefaultSqlSessionFactory对象
    return build(configuration);
  } 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.
    }
  }
}

// 可以通过Configuration创建DefaultSqlSessionFactory对象
public SqlSessionFactory build(Configuration config) {
  return new DefaultSqlSessionFactory(config);
}

  通过new XMLConfigBuilder()创建对象时,会生成Configuration对象和XPathParser对象

  • Configuration对象主要是用来保存xml文件的配置信息
  • XPathParser对象持有解析mybatis-config.xml文件和Mapper文件生成Document对象和解析mybatis-3-config.dtd文件和mybatis-3-mapper.dtd转换成XMLMapperEntityResolver对象
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) {
  // Configuration的初始化
  super(newConfig(configClass));
  ErrorContext.instance().resource("SQL Mapper Configuration");
  // 设置自定义配置
  this.configuration.setVariables(props);
  // 解析标志
  this.parsed = false;
  // environment初始化
  this.environment = environment;
  // 包装配置 InputStream 的 XPathParser
  this.parser = parser;
}
  • SqlSessionFactoryBuilder调用XMLConfigBuilder对象的parse()方法
public Configuration parse() {
  if (parsed) {
    throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  }
  parsed = true;
  // 通过XPathParser获取<configuration>节点对应的Node对象
  XNode xNode = parser.evalNode("/configuration");
  // 解析/configuration子节点信息
  parseConfiguration(xNode);
  return configuration;
}

private void parseConfiguration(XNode root) {
  try {
    // 解析properties节点
    propertiesElement(root.evalNode("properties"));
    // 解析settings节点
    Properties settings = settingsAsProperties(root.evalNode("settings"));
    // 配置自定义虚拟文件系统实现
    loadCustomVfsImpl(settings);
    // 配置自定义日志实现
    loadCustomLogImpl(settings);
    // 解析typeAliases节点
    typeAliasesElement(root.evalNode("typeAliases"));
    // 解析plugins节点
    pluginsElement(root.evalNode("plugins"));
    // 解析objectFactory节点
    objectFactoryElement(root.evalNode("objectFactory"));
    // 解析objectWrapperFactory节点
    objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
    // 解析reflectorFactory节点
    reflectorFactoryElement(root.evalNode("reflectorFactory"));
    settingsElement(settings);
    // 解析environments节点
    environmentsElement(root.evalNode("environments"));
    // 解析databaseIdProvider节点
    databaseIdProviderElement(root.evalNode("databaseIdProvider"));
    // 解析typeHandlers节点
    typeHandlersElement(root.evalNode("typeHandlers"));
    // 解析mappers节点
    mappersElement(root.evalNode("mappers"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  }
}

  XMLConfigBuilder对象的parse()方法,主要是通过XpathParser根据Xpath表达式获取基本的DOM节点以及子节点Node信息的操作(解析的节点configuration, properties, settings, typeAliases, typeHandlers, objectFactory, objectWrapperFactory, plugins, environments, databaseIdProvider, mappers), 然后将这些值解析出来设置到Configuration对象中,最后返回Configuration对象。

这里的节点解析就不一一去看了,后续会有单独的文章挑几个核心节点做详细介绍。

  • 调用SqlSessionFactoryBuilder对象的build(configuration)方法

  通过赋值的Configuration对象,调用build方法创建DefaultSqlSessionFactory对象。基于Java API方式,手动创建XMLConfigBuilder,并解析创建Configuration对象,最后调用此方法生成SqlSessionFactoryBuilder对象。

  至此,我们就知道了MyBatis是如何通过配置文件构建Configuration对象,并使用它创建SqlSessionFactory对象。

总结

  我们通过一个时序图,把整个myBatis初始化过程串起来,方便小伙伴更加直观的把整个流程串起来,从而对整个初始化过程了解的更加清晰

MyBatis初始化基本过程:

image.png

  每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 从 XML 配置文件或一个预先配置的 Configuration 实例来构建出来。

screenshot-20240417-103455.png

<think>首先,用户的问题是关于如何使用MyBatisPlus调用存储过程的示例。用户指定了参考站内引用,但提到这些引用仅供参考,不是真正的需求,并且我应该参考用户上一次的问题和我的回答。回顾对话:-系统级指令要求使用中文回答,并在回答中自然添加引用标识。-用户提供的引用是关于MyBatis调用存储过程的示例,但没有提到MyBatisPlus。-用户上一次的问题是我的第一个消息,是关于MyBatisPlus调用存储过程,而这次是“请问mybatisplus调用存储过程示例”,这是一个新的查询。用户上一次的问题:-用户的输入是:“我想了解如何使用MyBatisPlus调用存储过程请问mybatisplus调用存储过程示例”-所以,这是第一次查询,没有历史回答。我的第一个响应是空的,因为我刚刚开始处理这个查询。在我的思考中:-我需要基于MyBatisPlus提供调用存储过程的示例。-MyBatisPlus是MyBatis的增强工具,因此它扩展了MyBatis的功能。-在MyBatis中,调用存储过程通常通过MapperXML文件使用`<select>`或`<update>`标签调用,具体取决于存储过程是否有返回值。-对于MyBatisPlus,它可能有自己的方式或简化调用过程。检查用户提供的引用:-引用[1]显示Controller层代码调用了Service层,Service调用Mapper来执行存储过程。-引用[2]提到MyBatis调用Oracle存储过程,使用HashMap或Entity。-引用[3]显示存储过程的定义。-但这些是基于MyBatis的,不是直接关于MyBatisPlus。所以,我应该结合这些引用来提供MyBatisPlus的示例。核心要求:-回答使用中文。-结构清晰,逐步解决用户问题。-保证真实可靠:我需要回想MyBatisPlus的文档或常识。-在回答中,如果引用内容,末尾添加引用标识,如[^1]。-生成相关问题。步骤:1.**解释MyBatisPlus调用存储过程的基础**:MyBatisPlus建立在MyBatis之上,因此调用存储过程的方式类似,可以使用MapperXML。2.**提供一个简单的示例**:包括Controller、Service、Mapper和MapperXML的代码片段。3.**参考用户提供的引用**:既然用户给出了引用,我应该自然地引用它们,但要适配到MyBatisPlus。例如,引用[1]有Controller代码,我可以基于它。4.**使用正确的格式**:-行内数学表达式用$...$(但这里可能不需要,因为是代码相关)。-独立公式用$$...$$(同样,不适用)。-LaTeX语法正确(主要是代码块)。-代码块用```来包围。5.**生成相关问题**:在回答末尾,添加“§§相关问题§§”部分,列出几个相关问题。构建回答:-**开头**:简述MyBatisPlus调用存储过程。-**主体**:-步骤1:创建Mapper方法。-步骤2:在MapperXML中定义调用存储过程。-步骤3:实现Service调用Mapper。-步骤4:Controller调用Service。-**代码示例**:基于引用[1]和[2],适配到MyBatisPlus。-**添加引用**:如果直接基于引用,添加如[^1]。-**结尾**:生成相关问题。MyBatisPlus调用存储过程的特定方式:-在MyBatisPlus中,Mapper接口继承了BaseMapper,但调用存储过程通常还是通过自定义XML或使用@Select等注解。-我会使用自定义XML方法,因为它更常见。-示例存储过程:假设一个简单的过程,如调用时传递参数,并可能影响数据库。示例代码构思:-Controller:类似引用[1],但有注解。-Service:调用Mapper方法。-Mapper:定义接口方法。-MapperXML:定义存储过程调用。相关性问题:1.MyBatisPlus调用存储过程的性能如何?2.如何处理存储过程返回的游标或输出参数?3.MyBatisPlus是否有内置方法优化存储过程调用?确保引用:-在回答中,如果提到基于引用[1]的Controller,添加[^1]。完整结构:-解答用户问题。-生成相关问题。</think> ### 如何使用 MyBatis Plus 调用存储过程 MyBatis Plus 是 MyBatis 的一个增强工具,它在底层仍然兼容 MyBatis 的功能,因此调用存储过程的方法与传统 MyBatis 类似。核心是通过 Mapper XML 文件使用 `<select>`、`<update>` 或 `<call>` 标签(具体标签取决于存储过程是否返回结果),并在 Mapper 接口中定义对应方法。MyBatis Plus 本身不提供专用的调用存储过程方法,但可以通过自定义 XML 实现。以下是一个完整的、基于 Spring Boot 和 MyBatis Plus 的调用存储过程示例,参考了用户提供的引用[^1] 和 [^2] 的 Controller 和服务层逻辑。 #### 步骤说明 1. **创建 Mapper 接口**:定义调用存储过程的方法。 2. **配置 Mapper XML**:在 XML 文件中实现存储过程的调用。 3. **实现 Service 层**:封装业务逻辑,调用 Mapper 方法。 4. **实现 Controller 层**:处理 HTTP 请求,调用 Service。 5. **调用示例**:演示如何传递参数。 #### 完整代码示例 假设您正在使用 Spring Boot 项目集成了 MyBatis Plus,存储过程定义参考引用[^3] 的例子(例如 Oracle 数据库)。存储过程名称为 `p_testprod`,接受输入参数并返回输出值。 1. **Mapper 接口(使用 MyBatis Plus BaseMapper)** 在 `CallProcedureMapper.java` 文件中定义接口。MyBatis Plus 的 BaseMapper 提供了基本的 CRUD 操作,但调用存储过程需要自定义方法。 ```java import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Param; import java.util.Map; public interface CallProcedureMapper extends BaseMapper<Object> { void callTestProd(@Param("params") Map<String, Object> params); // 使用 Map 传递参数 } ``` 2. **Mapper XML 文件(配置存储过程调用)** 在 `CallProcedureMapper.xml` 文件中定义调用逻辑。使用 `<select>` 或 `<update>` 标签(因为存储过程可能有输出参数,此处以 `<update>` 为例)。参考引用[^2] 中 HashMap 的方式传递参数。 ```xml <?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="com.example.mapper.CallProcedureMapper"> <!-- 调用存储过程,使用 HashMap 接收输入输出参数 --> <update id="callTestProd" statementType="CALLABLE"> {call p_testprod( #{params.v_lbdm, mode=IN, jdbcType=VARCHAR}, #{params.v_lang_id, mode=IN, jdbcType=VARCHAR}, #{params.v_acc_user, mode=IN, jdbcType=VARCHAR}, #{params.v_ids, mode=IN, jdbcType=VARCHAR}, #{params.n_return, mode=OUT, jdbcType=VARCHAR} )} </update> </mapper> ``` - 说明: - `statementType="CALLABLE"` 表示调用存储过程。 - `mode=IN` 或 `mode=OUT` 指定参数是输入还是输出。 - `jdbcType` 指定数据类型,如 VARCHAR。 - 参数通过一个 Map(键值对)传递,便于处理多个参数。 3. **Service 层(调用 Mapper)** 在 `CallProcedureService.java` 中实现业务逻辑,使用 Map 封装参数并调用 Mapper。参考引用[^1] 的 Service 结构。 ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.HashMap; import java.util.Map; @Service public class CallProcedureService { @Autowired private CallProcedureMapper callProcedureMapper; public void callProcedure(String oldNo, String id, String versionFlag) { // 创建 Map 存储输入参数,假设存储过程需要这些值 Map<String, Object> params = new HashMap<>(); params.put("v_lbdm", oldNo); params.put("v_lang_id", id); params.put("v_acc_user", "user"); // 示例固定值 params.put("v_ids", versionFlag); // 添加输出参数占位符 params.put("n_return", null); // 初始化输出参数 // 调用 Mapper 方法执行存储过程 callProcedureMapper.callTestProd(params); // 获取输出参数值 String result = (String) params.get("n_return"); System.out.println("存储过程返回值: " + result); // 处理返回结果 } } ``` 4. **Controller 层(处理 HTTP 请求)** 在 `CallProcedureController.java` 中处理请求,调用 Service。参考引用[^1] 的代码风格。 ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import com.example.response.BaseResponse; @RestController @RequestMapping("/api") public class CallProcedureController { @Autowired private CallProcedureService callProcedureService; @PostMapping("/callProcedure") public BaseResponse<?> callProcedure(@RequestParam String oldNo, @RequestParam String id, @RequestParam String versionFlag) { callProcedureService.callProcedure(oldNo, id, versionFlag); return new BaseResponse<>(); // 返回成功响应 } } ``` #### 关键注意事项 - **参数处理**:使用 `Map` 可以灵活传递输入和输出参数(如 `n_return`)。如果参数较多,也可以用实体类替代 Map。 - **数据库兼容性**:此示例适用于 Oracle(参考引用[^2]),其他数据库如 MySQL 类似,但存储过程语法可能不同。 - **MyBatis Plus 集成**:确保项目中引入了 MyBatis Plus 依赖(例如通过 Maven 添加),并在启动类使用 `@MapperScan` 扫描 Mapper 接口。 - **错误处理**:在实际应用中,添加 try-catch 捕获数据库异常,并处理输出参数类型转换。 - **参考来源**:以上代码结构参考了 Controller 和 Service 层示例[^1],以及参数传递方式[^2]。 ####
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值