不使用 XML 构建 SqlSessionFactory
概述
MyBatis 提供两套并行的配置路径来构建 SqlSessionFactory:基于 XML 配置文件 的传统方式,和基于 Java 代码 的纯编程方式。两种方式完全等价,选择取决于项目阶段和个人偏好。本节将对比两种路径,帮助你做出合适选择。
两种构建路径对比
路径一:XML 方式(传统)
这是 MyBatis 官方推荐的生产环境配置方式。通过 mybatis-config.xml 集中管理数据源、事务、映射器等配置。
// 1. 读取 XML 配置文件
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 2. 构建 SqlSessionFactory
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(inputStream);
对应的 mybatis-config.xml:
<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/feixiang"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/feixiang/mapper/EmployeeMapper.xml"/>
</mappers>
</configuration>
路径二:Java 代码方式(纯编程)
完全用 Java 代码替代 XML,适合快速原型开发和单元测试。
// 1. 创建数据源
DataSource dataSource = new PooledDataSource(
"com.mysql.cj.jdbc.Driver",
"jdbc:mysql://localhost:3306/feixiang",
"root",
"123456"
);
// 2. 创建事务工厂
TransactionFactory transactionFactory = new JdbcTransactionFactory();
// 3. 组装环境
Environment environment = new Environment("development",
transactionFactory, dataSource);
// 4. 构建 Configuration
Configuration configuration = new Configuration(environment);
configuration.addMapper(EmployeeMapper.class);
// 5. 构建 SqlSessionFactory
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(configuration);
流程对比图
实战场景:飞翔科技公司员工管理系统
场景介绍
飞翔科技公司由技术总监大翔带领团队开发内部员工管理系统。团队分工如下:
| 角色 | 成员 | 职责 |
|---|---|---|
| 技术总监 | 大翔 | 架构决策,确定生产用 XML 配置 |
| 高级开发 | 白歌 | 编写核心 Mapper XML |
| 后端开发 | 孔蓝 | 实现 Service 层 |
| 初级开发 | 小崔 | 编写单元测试 |
| 测试工程师 | 黄俪 | 集成测试验证 |
场景一:小崔写单元测试(Java 代码方式)
小崔需要为 EmployeeMapper 写单元测试,不想依赖外部 XML 配置文件,于是用 Java 代码方式快速搭建测试环境:
public class EmployeeMapperTest {
private SqlSessionFactory sqlSessionFactory;
@BeforeEach
public void setUp() {
// 使用 H2 内存数据库,无需外部依赖
DataSource dataSource = new PooledDataSource(
"org.h2.Driver",
"jdbc:h2:mem:test;DB_CLOSE_DELAY=-1",
"sa",
""
);
TransactionFactory txFactory = new JdbcTransactionFactory();
Environment env = new Environment("test", txFactory, dataSource);
Configuration config = new Configuration(env);
// 直接注册 Mapper 接口
config.addMapper(EmployeeMapper.class);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
// 初始化表结构
try (SqlSession session = sqlSessionFactory.openSession()) {
session.getConnection().createStatement().execute(
"CREATE TABLE employee (id INT PRIMARY KEY, name VARCHAR(50), dept VARCHAR(50))"
);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Test
public void testFindById() {
try (SqlSession session = sqlSessionFactory.openSession()) {
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
// 先插入测试数据
Employee emp = new Employee(1, "小崔", "研发部");
mapper.insert(emp);
session.commit();
// 再验证查询
Employee result = mapper.findById(1);
assertEquals("小崔", result.getName());
}
}
}
关键点:configuration.addMapper(EmployeeMapper.class) 不仅注册了 Mapper 接口,MyBatis 还会自动去 classpath 下查找同名的 EmployeeMapper.xml 并加载。这意味着即使你用 Java 代码构建 Configuration,XML 映射文件仍然可以共存。
场景二:大翔的生产配置(XML 方式)
生产环境由大翔决定使用 XML 方式,方便运维统一管理数据库连接参数:
<!-- mybatis-config.xml -->
<configuration>
<properties resource="db.properties"/> <!-- 密码等敏感信息外置 -->
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
<environments default="production">
<environment id="production">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/feixiang/mapper/EmployeeMapper.xml"/>
<mapper resource="org/feixiang/mapper/DepartmentMapper.xml"/>
</mappers>
</configuration>
# db.properties — 独立于代码的敏感配置
db.driver=com.mysql.cj.jdbc.Driver
db.url=jdbc:mysql://10.0.1.100:3306/feixiang_prod
db.username=feixiang_app
db.password=Prod@2026!
两种方式适用场景
| 维度 | Java 代码方式 | XML 配置方式 |
|---|---|---|
| 适用阶段 | 单元测试、快速原型、Demo 演示 | 生产部署、正式项目 |
| 配置修改 | 需改代码重新编译 | 改 XML 即可,部分支持热加载 |
| 环境切换 | 代码中硬编码或手动传参 | <properties> 外置,切换配置文件即可 |
| 团队协作 | 配置散落在 Java 类中,不易查找 | 统一入口,运维友好 |
| IDE 支持 | 无语法提示、无校验 | 有 Schema 约束和自动补全 |
| 复杂度上限 | 可配置所有项但代码冗长 | 结构清晰,适合复杂配置 |
| 推荐人群 | 开发者自测、小崔这样的新手快速上手 | 大翔这样的架构师做项目级决策 |
易错场景提醒
1. 忘记注册 Mapper
// ❌ 错误:只创建了 Configuration,没有 addMapper
Configuration config = new Configuration(environment);
SqlSessionFactory sf = new SqlSessionFactoryBuilder().build(config);
// 后续 getMapper 会报错:Type interface xxx is not known to the MapperRegistry
// ✅ 正确:必须在 build 前注册
Configuration config = new Configuration(environment);
config.addMapper(EmployeeMapper.class); // 关键步骤!
SqlSessionFactory sf = new SqlSessionFactoryBuilder().build(config);
2. XML 与注解混用时路径不对
// Java 代码方式中 addMapper 会自动查找同名 XML,但 XML 必须
// 与 Mapper 接口在同一 classpath 目录下,且文件名完全一致。
// 例如:org/feixiang/mapper/EmployeeMapper.java
// 对应 org/feixiang/mapper/EmployeeMapper.xml
3. 生产环境用 Java 代码硬编码密码
// ❌ 危险:密码写在代码中,提交到 Git 就泄露了
DataSource ds = new PooledDataSource(..., "root", "MyPassword123");
大翔的忠告:生产环境务必使用 XML + properties 外置敏感信息。
面试考点
MyBatis 有几种构建 SqlSessionFactory 的方式?分别是什么?
- 两种:基于 XML 配置文件(
build(InputStream))和基于 Java Configuration 对象(build(Configuration))。
- 两种:基于 XML 配置文件(
configuration.addMapper(Class)背后做了什么?- 将 Mapper 接口注册到
MapperRegistry,同时自动查找 classpath 下同名的 XML 映射文件并加载。
- 将 Mapper 接口注册到
什么场景下应该用 Java 代码方式而非 XML?
- 单元测试(内存数据库、无需外部文件)、快速原型验证、需要动态构建配置的场景。
Java 代码方式能否完全替代 XML 映射文件?
- 不能。复杂映射(嵌套联合映射、动态 SQL)仍需要 XML 映射文件。Java 注解有表达能力上限。