mybatis-config.xml是 MyBatis 的核心配置文件用于配置整个 MyBatis 框架的全局行为比如环境数据源、事务、类型别名、插件、Mapper 映射等。示例?xml version1.0 encodingUTF-8 ? !DOCTYPE configuration PUBLIC -//mybatis.org//DTD Config 3.0//EN http://mybatis.org/dtd/mybatis-3-config.dtd configuration !-- 映入外部的properties文件 -- properties resourcedb.properties/ !-- 开启Mybatis对标准日志的视线 -- settings setting namelogImpl valueSLF4J/ /settings environments defaultdevelopment !-- 每一个environment对应一个SqlSessionFactory -- environment iddevelopment !-- 事务管理器 -- transactionManager typeJDBC/ dataSource typePOOLED property namedriver value${jdbc.driver}/ property nameurl value${jdbc.url}/ property nameusername value${jdbc.username}/ property namepassword value${jdbc.password}/ /dataSource /environment /environments !-- 执行XxxMapper.xml文件的路径 -- !-- resource路径自动会从根路径下开始查找资源 -- mappers mapper resourceCarMapper.xml/ /mappers /configuration一、xml配置文件的开头1、任意的xml文件的首行都是如下的配置?xml version1.0 encodingUTF-8 ?2、xml文件中根标签的名字3、dtd是对xml文件的约束能有什么标签标签的顺序标签的属性有哪些子标签......二、environments环境标签可以配置多个环境一个环境对应一个sqlsessionFactory对象。在java中创建sqlSessionFactory的时候可以指定环境的id表明用哪一个数据库创建的sqlSessionFactorySqlSessionFactoryBuilder sqlSessionFactoryBuilder new SqlSessionFactoryBuilder(); // 两种方式创建sqlSessionFactory // 1、没有指明具体的environment中的id则用默认的environment SqlSessionFactory sqlSessionFactory1 sqlSessionFactoryBuilder.build(Resources.getResourceAsStream(mybatis-config.xml)); // 2、指明具体的environment中的id SqlSessionFactory sqlSessionFactory2 sqlSessionFactoryBuilder.build(Resources.getResourceAsStream(mybatis-config.xml), mybatisDB);2-1、什么叫一个数据库对应一个sqlsessionfactory对象1、SqlSessionFactory 是什么先拆解这个名字部分含义SqlSQL 语句Session会话一次数据库连接操作Factory工厂生产东西的机器SqlSessionFactory 生产 数据库会话 的工厂// 1. 先建工厂全局只需要一个很重 SqlSessionFactory factory new SqlSessionFactoryBuilder() .build(inputStream); // 读取 mybatis-config.xml // 2. 工厂生产会话每次操作数据库都要新开一个 SqlSession session factory.openSession(); // 3. 用会话执行 SQL UserMapper mapper session.getMapper(UserMapper.class); User user mapper.selectById(1); // 4. 用完关闭会话轻量用完即弃 session.close();2、为什么说一个数据库对应一个核心原因SqlSessionFactory 太重了它重在哪里┌─────────────────────────────────────────┐ │ SqlSessionFactory 里面装着什么 │ ├─────────────────────────────────────────┤ │ 1. 数据库连接池配置连接池初始化 │ │ 2. 所有 Mapper XML 的解析结果缓存 │ │ 3. 数据源 DataSource连接信息 │ │ 4. 全局配置缓存、插件、类型别名等 │ │ 5. 事务管理器配置 │ └─────────────────────────────────────────┘这些资源创建一次开销很大所以要全局复用类比理解想象 SqlSessionFactory 是一家银行的总行总行SqlSessionFactory ├── 金库连接池← 初始化很费劲 ├── 所有客户档案Mapper 配置← 加载很费时 ├── 规章制度全局配置 │ ├── 柜台1SqlSession← 轻量客户来了就开 ├── 柜台2SqlSession← 用完就关 └── 柜台3SqlSession一家银行数据库只需要一个总行Factory但有很多柜台Session服务客户。3、代码层面的对应关系单数据库最常见// mybatis-config.xml 里配置了一个数据库 // environment iddevelopment.../environment // 全局只创建一个 Factory public class MyBatisUtil { private static SqlSessionFactory factory; static { InputStream is Resources.getResourceAsStream(mybatis-config.xml); factory new SqlSessionFactoryBuilder().build(is); // ← 只执行一次 } public static SqlSession getSession() { return factory.openSession(); // ← 每次需要就新开一个 } }使用场景// 用户 A 来查询 SqlSession session1 MyBatisUtil.getSession(); // 用户 B 来插入 SqlSession session2 MyBatisUtil.getSession(); // 用户 C 来更新 SqlSession session3 MyBatisUtil.getSession(); // 三个 Session但共用一个 Factory4、如果有多个数据库呢那就需要多个 SqlSessionFactory// 数据库 1用户库user_db InputStream is1 Resources.getResourceAsStream(mybatis-user.xml); SqlSessionFactory userFactory new SqlSessionFactoryBuilder().build(is1); // 数据库 2订单库order_db InputStream is2 Resources.getResourceAsStream(mybatis-order.xml); SqlSessionFactory orderFactory new SqlSessionFactoryBuilder().build(is2); // 数据库 3日志库log_db InputStream is3 Resources.getResourceAsStream(mybatis-log.xml); SqlSessionFactory logFactory new SqlSessionFactoryBuilder().build(is3);对应关系数据库SqlSessionFactory用途user_dbuserFactory操作用户表order_dborderFactory操作订单表log_dblogFactory操作日志表5、为什么不能多个数据库共用一个 Factory因为Factory 在创建时就绑定了数据源!-- mybatis-config.xml -- environments defaultdevelopment environment iddevelopment !-- 这里的数据源一旦确定Factory 就绑死了这个数据库 -- dataSource typePOOLED property namedriver valuecom.mysql.cj.jdbc.Driver/ property nameurl valuejdbc:mysql://localhost:3306/user_db/ property nameusername valueroot/ property namepassword value123456/ /dataSource /environment /environments一个 Factory 只能对应一个dataSource配置6、总结图┌─────────────────────────────────────────────┐ │ 你的 Java 应用 │ ├─────────────────────────────────────────────┤ │ │ │ ┌─────────────────┐ ┌─────────────┐ │ │ │ SqlSessionFactory │ │ user_db │ │ │ │ (userFactory) │◄──►│ 用户数据库 │ │ │ └─────────────────┘ └─────────────┘ │ │ │ │ │ ┌────────┴────────┐ │ │ ▼ ▼ │ │ SqlSession1 SqlSession2 ← 线程1、线程2 │ │ (查用户) (改用户) ← 用完就关闭 │ │ │ │ ┌─────────────────┐ ┌─────────────┐ │ │ │ SqlSessionFactory │ │ order_db │ │ │ │ (orderFactory) │◄──►│ 订单数据库 │ │ │ └─────────────────┘ └─────────────┘ │ │ │ │ │ ┌────────┴────────┐ │ │ ▼ ▼ │ │ SqlSession3 SqlSession4 │ │ (查订单) (插订单) │ │ │ └─────────────────────────────────────────────┘7、一句话总结SqlSessionFactory 是数据库专属工厂创建时绑定了特定的数据库连接信息所以一个数据库对应一个 Factory而SqlSession 是工厂生产的临时会话每次操作数据库都新建一个用完即关。对象数量生命周期类比SqlSessionFactory一个数据库一个全局唯一应用启动创建银行总行SqlSession每次操作创建一个用完即关银行柜台8、多个数据库config文件 VS 多个environment标签一个mybatis-config.xml里确实可以有多个environment但SqlSessionFactory创建时只能选一个!-- mybatis-config.xml -- environments defaultdevelopment !-- 默认用哪个 -- environment iddevelopment !-- 环境1开发库 -- dataSource typePOOLED property nameurl valuejdbc:mysql://localhost:3306/db1/ /dataSource /environment environment idproduction !-- 环境2生产库 -- dataSource typePOOLED property nameurl valuejdbc:mysql://localhost:3306/db2/ /dataSource /environment /environments关键点SqlSessionFactoryBuilder.build()时只能指定一个 environment// 方式1不指定用 default即 development SqlSessionFactory factory1 new SqlSessionFactoryBuilder().build(inputStream); // 方式2指定 environment SqlSessionFactory factory2 new SqlSessionFactoryBuilder() .build(inputStream, production); // ← 只能选一个默认是iddevelopment。1、为什么我说多个数据库要多个 Factory场景1多环境切换开发/测试/生产这是MyBatis 设计environments的真正目的——同一套代码不同环境切换开发时build(inputStream, development) → 连 db1 测试时build(inputStream, test) → 连 db2 上线时build(inputStream, production) → 连 db3特点同一时间只连一个数据库场景2同时连多个数据库这才是我说的比如你的系统要同时操作user_db用户库order_db订单库log_db日志库这时候你需要同时存在 3 个活跃的 Factory// 配置文件1用户库 InputStream is1 Resources.getResourceAsStream(mybatis-user.xml); SqlSessionFactory userFactory new SqlSessionFactoryBuilder().build(is1); // 配置文件2订单库 InputStream is2 Resources.getResourceAsStream(mybatis-order.xml); SqlSessionFactory orderFactory new SqlSessionFactoryBuilder().build(is2); // 配置文件3日志库 InputStream is3 Resources.getResourceAsStream(mybatis-log.xml); SqlSessionFactory logFactory new SqlSessionFactoryBuilder().build(is3);2、对比两种方式的本质区别场景配置方式Factory 数量目的环境切换一个 XML多个environment1 个运行时选开发/测试/生产切换多库并存多个 XML或编程式配置多个同时存在同时操作不同数据库3、能不能一个 XML 配多个数据库同时用可以但有点别扭需要你自己管理!-- mybatis-config.xml -- environments defaultuser_db environment iduser_db.../environment environment idorder_db.../environment /environments// 自己建两个 Factory InputStream is Resources.getResourceAsStream(mybatis-config.xml); SqlSessionFactory userFactory new SqlSessionFactoryBuilder() .build(is, user_db); // ⚠️ 问题inputStream 只能读一次上面读完了这里要重新读 InputStream is2 Resources.getResourceAsStream(mybatis-config.xml); SqlSessionFactory orderFactory new SqlSessionFactoryBuilder() .build(is2, order_db);这样写很别扭所以通常直接拆成多个 XML 文件更清晰。4、Spring 中的优雅做法实际开发常用在 Spring 项目里根本不用 XML直接配多个数据源Configuration public class DataSourceConfig { Bean public SqlSessionFactory userSqlSessionFactory() { DataSource ds createDataSource(jdbc:mysql://localhost:3306/user_db); return new SqlSessionFactoryBean() {{ setDataSource(ds); }}.getObject(); } Bean public SqlSessionFactory orderSqlSessionFactory() { DataSource ds createDataSource(jdbc:mysql://localhost:3306/order_db); return new SqlSessionFactoryBean() {{ setDataSource(ds); }}.getObject(); } }5、总结你的问题答案一个 XML 能有多个environment吗✅ 能用于环境切换一个 Factory 能同时连多个数据库吗❌ 不能Factory 创建时绑死一个数据源同时操作多个数据库怎么办建多个 Factory每个对应一个数据库核心记住environment是选项不是多开。SqlSessionFactory是单选后的结果不是多选。三、Mybatis的事务管理器transactionManager标签1、作用配置事务管理器。指定mybatis具体使用什么方式去管理事务。2、type属性有两个值第一个JDBC使用原生的JDBC代码来管理事务。第二个MANAGEDmybatis不再负责事务的管理将事务管理交给其它的JEE(JavaEE)容器来管理。例如spring3、大小写无所谓。不区分大小写。但是不能写其他值。只能是二选一 jdbc、managed4、在mybatis中提供了一个事务管理器接口Transaction 该接口下有两个实现类JdbcTransactionManagedTransaction如果typeJDBC那么底层会实例化JdbcTransaction对象。如果typeMANAGED那么底层会实例化ManagedTransaction。四、dataSource配置3-1、基本配置介绍1、dataSource被称为数据源。2、dataSource作用是什么为程序提供Connection对象。【注意】但凡是给程序提供Connection对象的都叫做数据源。3、数据源实际上是一套规范接口。JDK中有这套规范javax.sql.DataSource这个数据源的规范这套接口实际上是JDK规定的。4、我们自己也可以编写数据源组件只要实现javax.sql.DataSource接口就行了。实现接口当中所有的方法。这样就有了自己的数据源。比如你可以写一个属于自己的数据库连接池数据库连接池是提供连接对象的所以数据库连接池就是一个数据源。5、常见的数据源组件有哪些呢【常见的数据库连接池有哪些呢】阿里巴巴的德鲁伊连接池druidc3p0dbcp....6、type属性用来指定数据源的类型就是指定具体使用什么方式来获取Connection对象type属性有三个值必须是三选一。type[UNPOOLED|POOLED|JNDI]UNPOOLED不使用数据库连接池技术。每一次请求过来之后都是创建新的Connection对象。POOLED使用mybatis自己实现的数据库连接池。JNDI集成其它第三方的数据库连接池。type 值说明适用场景POOLEDMyBatis 自带连接池默认简单应用无需额外依赖UNPOOLED无连接池每次创建新连接测试或极少并发场景JNDI从应用服务器 JNDI 获取企业级应用由容器管理3-2、JNDIJNDI是一套规范大部分web容器都实现了这个规范例如Tomcat、Jetty、WebLogic、WebSphere。这些服务器容器都实现了JNDI规范。所以可以把对应的连接池配置到服务器容器中如druid配置到Tomcat中然后Tomcat会给druid提供一个上下文的名称将上下文的名称配到对应的datasource位置即可。使用dataSource typeJNDI就是告诉 MyBatis别自己创建数据库连接了去应用服务器Tomcat/GlassFish/WebLogic拿现成的连接池资源。MyBatis 的dataSource标签本身并不直接指定连接池类型而是通过type 属性来选择使用哪种连接池实现。不同数据库连接池的配置纯mybatis项目自定义数据库连接池DataSourceFactoryJNDI方式Tomcat容器mybatis spring项目mybatis springboot项目查看Mybatis11-不同的数据库连接池的配置-CSDN博客3-3、type POOLED和UNPOOLED对比测试代码Test public void testType() throws Exception{ SqlSessionFactoryBuilder sqlSessionFactoryBuilder new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory sqlSessionFactoryBuilder.build(Resources.getResourceAsStream(mybatis-config.xml)); // SqlSessionFactory可以创建多个SqlSession! SqlSession sqlSession sqlSessionFactory.openSession(); Object selectById sqlSession.selectOne(selectById, 12); System.out.println(selectById); sqlSession.commit(); sqlSession.close(); // 第二次创建 SqlSession sqlSession2 sqlSessionFactory.openSession(); Object selectById2 sqlSession2.selectOne(selectById, 12); System.out.println(selectById2); sqlSession2.commit(); sqlSession2.close(); }情况一type UNPOOLED两个connection对象不一样情况二type POOLED同一个connection对象五、property标签在 MyBatis 的mybatis-config.xml配置文件中properties标签用于引入外部属性文件或者在XML 中内联配置参数这些参数可以被用于整个配置文件中的占位符${}替换例如数据库连接、分页插件参数等。一句话理解properties就是给 MyBatis 提供参数值的地方支持从外部.properties文件读取也可以直接在 XML 中写死键值对用于${xxx}占位符替换。4-1、用法示例 1引入外部属性文件文件结构src/ ├── mybatis-config.xml └── db.propertiesdb.properties 内容jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/mydb jdbc.usernameroot jdbc.password123456mybatis-config.xml 内容configuration !-- 映入外部的properties文件 -- properties resourcedb.properties/ environments defaultdevelopment environment iddevelopment transactionManager typeJDBC/ dataSource typePOOLED property namedriver value${jdbc.driver}/ property nameurl value${jdbc.url}/ property nameusername value${jdbc.username}/ property namepassword value${jdbc.password}/ /dataSource /environment /environments /configurationresource 路径是 classpath 相对路径示例resourcedb.properties表示类路径下的db.properties【备注】不建议【注意】properties resourcedb.properties/这行代码的作用是加载db.properties文件到 MyBatis 的配置上下文中。MyBatis 会把文件里的所有key-value 对都加载进来然后${xxx}就可以引用这些值了。4-2、用法示例 2在 XML 中内联写属性configuration properties property namejdbc.driver valuecom.mysql.cj.jdbc.Driver/ property namejdbc.url valuejdbc:mysql://localhost:3306/mydb/ property namejdbc.username valueroot/ property namejdbc.password value123456/ /properties environments defaultdevelopment environment iddevelopment transactionManager typeJDBC/ dataSource typePOOLED property namedriver value${jdbc.driver}/ property nameurl value${jdbc.url}/ property nameusername value${jdbc.username}/ property namepassword value${jdbc.password}/ /dataSource /environment /environments /configuration4-3、属性优先级默认行为如果你同时指定了外部文件resource或url和内联属性property子标签那么内联属性优先会覆盖外部文件中相同的属性值。MyBatis 的${}取值有优先级如果多处定义了同名 key优先级来源1最高properties标签内直接定义的property2resource或url引入的外部 properties 文件3Java 方法传入的参数如SqlSessionFactoryBuilder.build(..., props)4最低系统环境变量1. Java 方法传入的参数这是指在代码中通过编程方式传入配置而不是通过 XML 文件。示例代码// 创建 Properties 对象 Properties props new Properties(); props.setProperty(jdbc.username, admin); props.setProperty(jdbc.password, secret); // 构建 SqlSessionFactory 时把 props 作为参数传入 InputStream inputStream Resources.getResourceAsStream(mybatis-config.xml); SqlSessionFactory factory new SqlSessionFactoryBuilder().build(inputStream, props);作用你可以在运行时动态传入配置覆盖 XML 中或 properties 文件中的值。典型场景密码需要加密解密后在代码中传入不同环境开发/测试/生产用不同的配置配置需要从外部配置中心如 Apollo、Nacos获取2. 系统环境变量这是指操作系统级别的环境变量比如 Linux/Windows 中通过export或系统设置定义的环境变量。示例在 Linux 终端执行export jdbc_urljdbc:mysql://prod-server:3306/mydb或者在 Windows 系统环境变量中设置jdbc_urlxxx然后 MyBatis 的 XML 中可以直接用property nameurl value${jdbc_url}/作用把敏感配置或环境相关配置放到操作系统层面不写入代码仓库。典型场景生产环境的数据库密码不放在代码里Docker/K8s 容器通过环境变量注入配置CI/CD 流水线动态设置环境变量什么是windows系统环境变量Windows 设置环境变量的位置方法系统属性 → 环境变量右键此电脑→ 选择属性点击高级系统设置点击环境变量按钮在弹出的窗口中你可以看到两个区域用户变量仅当前用户有效系统变量所有用户有效然后新建变量点击新建按钮不是编辑Path字段填写内容变量名jdbc_url变量值jdbc:mysql://localhost:3306/mydb就像这样变量名: jdbc_url 变量值: jdbc:mysql://localhost:3306/mydb验证是否设置成功打开CMD 命令提示符输入echo %jdbc_url%如果输出你设置的值说明成功了。为什么实际很少这样用这种方式在 Windows 开发中不太常见原因缺点说明不方便团队协作每个人的电脑都要手动配容易忘记换电脑或重装系统后丢失不适合部署生产环境通常是 Linux实际更常见的做法// 从配置文件读取开发环境 // 或从环境变量读取Docker/K8s 部署 String url System.getenv(jdbc_url); // 读取系统环境变量在 Linux/Docker 中更自然export jdbc_urljdbc:mysql://prod:3306/mydb java -jar myapp.jar3、优先级总结从高到低1. XML 中 properties 标签内直接定义的 property ↓ 2. resource/url 引入的外部 .properties 文件 ↓ 3. Java 代码 build() 方法传入的 Properties 参数 ↓ 4. 系统环境变量如 export 设置的 ↓ 5. Java 系统属性如 -Djdbc.urlxxx 启动参数核心规则后加载的会覆盖先加载的同名 key所以优先级高的会赢。4、实际工作中的常见用法// 常见做法先加载配置文件再用代码覆盖敏感信息 Properties props new Properties(); // 从安全的地方获取真实密码如配置中心、密钥管理服务 String realPassword getPasswordFromVault(); props.setProperty(jdbc.password, realPassword); SqlSessionFactory factory new SqlSessionFactoryBuilder() .build(inputStream, props);这样db.properties里可以放个占位密码真实密码在运行时注入兼顾开发便利和生产安全。4-4、常见属性用途位置用途dataSource数据库连接参数插件如分页插件配置插件行为settings配置全局行为开关4-5、SqlSessionFactory对象不是所有信息都在 SqlSessionFactory 中而是MyBatis 配置文件的解析结果 加载的 properties → 生成一个 SqlSessionFactory 实例SqlSessionFactory是解析后的产物不是容器本身。具体流程db.properties ──┐ ├──→ MyBatis 解析配置 ──→ SqlSessionFactory 实例 mybatis-config.xml ┘ ↑ │ 解析时${jdbc.xxx} 被替换为实际值关键点阶段发生了什么解析阶段MyBatis 读取 XML properties把${jdbc.xxx}替换成真实值构建阶段用解析后的配置创建SqlSessionFactory运行阶段SqlSessionFactory已经不包含${}和 properties 文件只包含最终值验证SqlSessionFactory 里存的是什么// 构建完成后SqlSessionFactory 内部大概是这样的结构 SqlSessionFactory factory new SqlSessionFactoryBuilder().build(inputStream); // factory 内部已经存好了 // - DataSource 对象包含真实的 url、username、password // - Mapper 接口的代理工厂 // - 缓存配置、插件等 // 没有 properties 文件也没有 ${jdbc.xxx} 这种占位符SqlSessionFactory是编译后的结果不是源代码。SqlSessionFactory是工厂对象它的职责是根据配置创建数据库连接管理 Mapper 接口的代理提供SqlSession实例它内部存储的是解析后的、可直接使用的对象而不是原始的 XML 文本或 properties 文件内容。