本篇源码:https://gitee.com/YanX2000/yan-batis/tree/master/yanbatis-04
上一篇
概述
YanBatis-04 在 YanBatis-03 的基础上,新增了数据源管理、JDBC事务处理和真正的数据库操作功能,实现了从模拟SQL执行到实际数据库查询的重要跨越。这对于深入理解 MyBatis 这类ORM框架的内部工作原理是一个关键的里程碑。
新增功能对比 (相对于 YanBatis-03)
核心架构变化对比
| 组件 |
YanBatis-03 |
YanBatis-04 |
变化说明 |
| SQL执行 |
模拟返回字符串 |
真实数据库查询 |
从模拟执行转向实际JDBC操作 |
| 数据源管理 |
无数据源概念 |
Environment+DataSource |
新增数据源配置和管理 |
| 事务处理 |
无事务管理 |
JdbcTransaction |
支持JDBC事务管理 |
| 结果处理 |
返回硬编码字符串 |
ResultSet到对象映射 |
支持查询结果自动映射 |
| 配置解析 |
简单XML解析 |
环境配置解析 |
支持environments配置解析 |
1. 数据源和环境配置管理
YanBatis-03 的配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<mappers>
<mapper resource="mapper/User_Mapper.xml"/>
</mappers>
</configuration>
YanBatis-04 的完整配置:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="DRUID">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/yanbatis?useUnicode=true&useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="123456."/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/User_Mapper.xml"/>
</mappers>
</configuration>
新增核心类:
- Environment: 环境配置类,封装环境ID、事务工厂和数据源
- DataSourceFactory: 数据源工厂接口,支持不同类型数据源
- DruidDataSourceFactory: Druid数据源工厂实现
- TransactionFactory: 事务工厂接口
- JdbcTransactionFactory: JDBC事务工厂实现
- JdbcTransaction: JDBC事务实现类
2. SQL执行方式变革
YanBatis-03 的模拟执行:
@Override
public <T> T selectOne(String statement, Object parameter){
MappedStatement mappedStatement = configuration.getMappedStatement(statement);
return (T) ("你被代理了!\n方法:" + statement + "\n入参:" + parameter + "\n待执行SQL:" + mappedStatement.getSql());
}
YanBatis-04 的真实数据库操作:
@Override
public <T> T selectOne(String statement, Object parameter){
try {
MappedStatement mappedStatement = configuration.getMappedStatement(statement);
Environment environment = configuration.getEnvironment();
Connection connection = environment.getDataSource().getConnection();
BoundSql boundSql = mappedStatement.getBoundSql();
PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSql());
preparedStatement.setLong(1, Long.parseLong(((Object[]) parameter)[0].toString()));
ResultSet resultSet = preparedStatement.executeQuery();
List<T> objList = resultSet2Obj(resultSet, Class.forName(boundSql.getResultType()));
return objList.getFirst();
} catch (Exception e) {
log.debug("selectOne error", e);
returnnull;
}
}
关键改进:
- 真实JDBC操作: 使用PreparedStatement执行SQL
- 参数绑定: 支持参数绑定到PreparedStatement
- ResultSet处理: 将查询结果自动映射为Java对象
- 异常处理: 完善的异常处理机制
3. 结果集映射功能
新增ResultSet到对象映射方法:
private <T> List<T> resultSet2Obj(ResultSet resultSet, Class<?> clazz){
List<T> list = new ArrayList<>();
try {
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
// 每次遍历行值
while (resultSet.next()) {
T obj = (T) clazz.newInstance();
for (int i = 1; i <= columnCount; i++) {
Object value = resultSet.getObject(i);
String columnName = metaData.getColumnName(i);
String setMethod = "set" + columnName.substring(0, 1).toUpperCase() + columnName.substring(1);
Method method;
if (value instanceof Timestamp) {
method = clazz.getMethod(setMethod, Date.class);
} else {
method = clazz.getMethod(setMethod, value.getClass());
}
method.invoke(obj, value);
}
list.add(obj);
}
} catch (Exception e) {
log.error("resultSet2Obj error", e);
}
return list;
}
功能特点:
- 自动映射: 根据列名自动调用对应的setter方法
- 类型处理: 支持不同数据类型的自动转换
- 反射调用: 使用反射动态创建对象并设置属性值
- 特殊类型处理: 对Timestamp等特殊类型进行适配
4. 配置解析增强
YanBatis-03 的XMLConfigBuilder:
public Configuration parse(){
try {
// 解析映射器
mapperElement(root.element("mappers"));
} catch (Exception e) {
thrownew RuntimeException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
return configuration;
}
YanBatis-04 的增强解析:
public Configuration parse(){
try {
// 环境
log.debug("step 2 解析mybatis-config.xml");
log.debug("step 2.1 解析环境");
environmentsElement(root.element("environments"));
// 解析映射器
log.debug("step 2.2 解析映射器");
mapperElement(root.element("mappers"));
} catch (Exception e) {
thrownew RuntimeException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
return configuration;
}
新增environmentsElement方法:
private void environmentsElement(Element context)throws Exception {
String environment = context.attributeValue("default");
List<Element> environmentList = context.elements("environment");
for (Element e : environmentList) {
String id = e.attributeValue("id");
if (environment.equals(id)) {
// 事务管理器
TransactionFactory txFactory = (TransactionFactory) typeAliasRegistry.resolveAlias(e.element("transactionManager").attributeValue("type")).newInstance();
// 数据源
Element dataSourceElement = e.element("dataSource");
DataSourceFactory dataSourceFactory = (DataSourceFactory) typeAliasRegistry.resolveAlias(dataSourceElement.attributeValue("type")).newInstance();
List<Element> propertyList = dataSourceElement.elements("property");
Properties props = new Properties();
for (Element property : propertyList) {
props.setProperty(property.attributeValue("name"), property.attributeValue("value"));
}
dataSourceFactory.setProperties(props);
DataSource dataSource = dataSourceFactory.getDataSource();
// 构建环境
Environment environmentBuilder = Environment.builder()
.id(id)
.dataSource(dataSource)
.transactionFactory(txFactory)
.build();
configuration.setEnvironment(environmentBuilder);
}
}
}
5. 类型别名注册机制
Configuration构造器中注册别名:
public Configuration(){
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("DRUID", DruidDataSourceFactory.class);
}
TypeAliasRegistry功能:
- 支持类型别名到具体类的映射
- 简化XML配置中的类名书写
- 提供类型解析和实例化功能
test_SqlSessionFactory() 执行流程分析
整体执行时序图
ApiTest → SqlSessionFactoryBuilder → XMLConfigBuilder → Configuration → Environment → SqlSessionFactory → SqlSession → Mapper代理 → 真实数据库查询
详细执行步骤分析
第1步: 读取配置文件
Reader reader = Resources.getResourceAsReader("mybatis-config-datasource.xml");
日志输出:
step 1: 获取MyBatis配置文件
- 使用Resources工具类从classpath读取XML配置文件
第2步: 构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
2.1 创建XMLConfigBuilder并解析配置
step 2 解析mybatis-config.xml
step 2.1 解析环境
2.2 解析环境配置(environments)
- 解析
<environments default="development"> 元素
- 根据default属性选择对应的environment
- 解析transactionManager:
type="JDBC" → JdbcTransactionFactory
- 解析dataSource:
type="DRUID" → DruidDataSourceFactory
- 设置数据源属性: driver、url、username、password
- 创建Environment对象并设置到Configuration
2.3 解析映射器配置(mappers)
step 2.2 解析映射器
step 1: 获取MyBatis配置文件
step 2.2.1 解析 SQL :
SELECT id, userId, userName, userHead
FROM user
where id = ?
,参数:{1:"id"}
step 2.2.2 获取 SQL 类型:SELECT
step 2.2.3 添加解析 SQL
step 2.2.4 注册Mapper映射器:com.yanx.yanbatis.test.dao.IUserDao
step 2.2.5 注册映射器代理工厂:interface com.yanx.yanbatis.test.dao.IUserDao
====================注册完成=============================
- 解析User_Mapper.xml文件
- 提取namespace:
com.yanx.yanbatis.test.dao.IUserDao
- 解析select元素,获取id、parameterType、resultType、SQL语句
- 使用正则表达式将
#{id} 转换为 ?,并记录参数映射
- 创建BoundSql对象封装SQL和参数信息
- 创建MappedStatement并添加到Configuration
- 注册Mapper接口到MapperRegistry
第3步: 打开SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
- DefaultSqlSessionFactory创建DefaultSqlSession实例
- SqlSession持有Configuration引用
第4步: 获取Mapper代理对象
IUserDao userDao = sqlSession.getMapper(IUserDao.class);
step 3 执行SQL
step 3.1 获取映射器代理工厂:interface com.yanx.yanbatis.test.dao.IUserDao
step 3.2 从工厂创建映射器代理对象:interface com.yanx.yanbatis.test.dao.IUserDao
- 通过MapperRegistry获取MapperProxyFactory
- 创建MapperProxy动态代理对象
第5步: 执行数据库查询
User user = userDao.queryUserInfoById(1L);
step 3.3 代理对象执行SQL语句:queryUserInfoById
{dataSource-1} inited
step 3.4 处理结果集
5.1 代理拦截与方法路由
- MapperProxy拦截方法调用
- 通过MapperMethod执行SQL命令
- 调用SqlSession.selectOne()方法
5.2 数据库连接与SQL执行
- 从Environment获取DataSource
- Druid连接池初始化:
{dataSource-1} inited
- 获取数据库Connection
- 创建PreparedStatement并设置参数
- 执行SQL查询获取ResultSet

5.3 结果集处理
- 调用resultSet2Obj()方法处理结果集
- 使用反射根据列名调用setter方法
- 将数据库记录映射为User对象
5.4 最终输出结果
测试结果:{"id":1,"userHead":"1_04","userId":"10001","userName":"yanx"}
执行流程关键节点总结
- 配置解析阶段: XMLConfigBuilder解析XML配置文件,新增Environment和DataSource配置
- 环境构建阶段: 创建事务工厂、数据源工厂,构建运行环境
- 映射器注册阶段: 解析Mapper XML,注册SQL映射和接口代理
- 代理创建阶段: 通过MapperRegistry创建动态代理对象
- SQL执行阶段: 真实的JDBC操作,包括连接获取、参数设置、SQL执行
- 结果映射阶段: ResultSet自动映射为Java对象
数据流转图
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│mybatis-config │───▶│XMLConfigBuilder │───▶│ Configuration │
│-datasource.xml │ │ │ │ │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│ │
▼ ▼
┌──────────────────┐ ┌─────────────────┐
│ Environment │ │ MapperRegistry │
│ +DataSource │ │ +MappedStatement│
│ +TxFactory │ │ │
└──────────────────┘ └─────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ SqlSession │───▶│ MapperProxy │───▶│ 真实数据库 │
│ │ │ │ │ │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│Connection │ │PreparedStatement │ │ ResultSet │
│(Druid Pool) │ │ │ │ │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│
▼
┌─────────────────┐
│ User Object │
│ (反射映射) │
└─────────────────┘
技术架构改进
1. 数据源管理
- 工厂模式: DataSourceFactory支持多种数据源类型
- 连接池技术: 集成Druid连接池,提升性能和资源管理
- 配置化管理: 通过XML配置数据源参数
2. 事务处理
- 事务抽象: Transaction接口定义事务行为
- JDBC事务: JdbcTransaction实现基于JDBC的事务管理
- 工厂创建: TransactionFactory负责创建事务实例
3. 环境配置
- Environment封装: 统一管理环境ID、数据源、事务工厂
- 多环境支持: 支持配置多个环境,通过default属性选择
- Builder模式: 使用Builder模式构建Environment对象
4. SQL执行引擎
- 真实JDBC: 从模拟执行转向真实数据库操作
- PreparedStatement: 使用预编译语句,支持参数绑定
- 结果映射: 自动将ResultSet映射为Java对象
5. 类型系统
- TypeAliasRegistry: 类型别名注册机制
- BoundSql: 绑定SQL封装,包含SQL语句和参数映射
- 反射映射: 使用反射实现数据库字段到对象属性的自动映射
关键改进对比
相比yanbatis-03,主要改进包括:
- 真实数据库操作: 从模拟SQL执行转向实际JDBC操作
- 数据源管理: 引入数据源工厂和连接池技术
- 事务处理: 新增完整的事务管理机制
- 环境配置: 支持多环境配置和数据源配置
- 结果映射: 实现ResultSet到Java对象的自动映射
- 类型管理: 引入类型别名系统简化配置
- 异常处理: 完善的异常处理和日志记录
这一版本实现了从配置驱动的代理模式到真正可用的ORM框架的重要跨越,具备了MyBatis的核心数据访问能力。掌握这些核心组件的实现,对于构建自己的数据访问层或深入排查生产问题都大有裨益。欢迎在 云栈社区 分享你的实践心得。