架构设计
我们把Mybatis的功能架构分为三层:
(1) API接口层:提供给外部使用的接口 API,开发人员通过这些本地API来操纵数据库。接口层一接收
到 调用请求就会调用数据处理层来完成具体的数据处理。
MyBatis和数据库的交互有两种方式:
a. 使用传统的MyBati s提供的API ;
b. 使用Mapper代理的方式
(2) 数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根
据调用的请求完成一次数据库操作。
(3) 基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是
共 用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑
大致流程如下
主要构件及其相互关系如下:
源码剖析
1.加载配置文件流程
在通过SqlSessionfactoryBuilder创建SqlSessionfactory时候会先将配置文件转换成文件流并且解析后存放在configuration对象中
可以看到通过XNLconfigBuilder中parse解析xml文件流到configuration中,parse方法如下(Configuration中主要存放数据库链接信息配置信息以及sql相关信息具体可以查阅Configuration)
parseConfiguration方法如下,其中</mapper>下存放的就是
解析完成后会返回一个含有一个configuration的SQlsessionfactory工厂类 用来生成sqlsession
2.生成sqlSession流程
整体流程大致如下;
主要看一下创建 Executor 对象过程(毕竟执行sql的主要接口,大多数操作实际都是在该类中完成的)
主要注意的就是如果开启了二级缓存就会使用CachingExecutor类 没有开启就是要用SimpleExecutor
sql执行过程
这里以sqlsession,selectList()作为例子进行演示
selectList源码如下
这里以没有开启二级缓存代码进行分析query方法 这里主要生成两个对象在执行数据查询前
BoundSql:存放了sql信息 cacheKey存放了缓存的key
获取到key和boundSQL后 后续操作 涉及到一级缓存 具体流程可以参考一级缓存的介绍,这里主要看从数据库查询过程
实际数据查询的操作是在queryFromDatabase中 流程如下
StatementHandler主要是封装了JDBC Statement操作,负责对JDBC statement的操作,如设置参数、将Statement结果集转换成List集合。
可以看出这里也是可以使用插件的
handler.prepare(connection, transaction.getTimeout())用来生成Statement对象 也是对数据sql查询的核心
设置 SQL 上的参数,例如 PrepareStatement 对象上的占位符handler.parameterize(stmt);如?的替换 主要用到了反射实现而这个具体过程交由了TypeHander处理
包含了各种类型的参数的处理实现类
sql处理完成后交由Statement来执行 返回的结果集交由resultSetHandler来处理
getMapper代理方式
首先使用mapper的写法如下:
开始之前介绍一下MyBatis初始化时对接口的处理:MapperRegistry是Configuration中的一个属性,它内部维护一个HashMap用于存放mapper接口的工厂类,每个接口对应一个工厂类。mappers中可以配置接口的包路径,或者某个具体的接口类。
当解析mappers标签时,它会判断解析到的是mapper配置文件时,会再将对应配置文件中的增删 改查标签 封装成MappedStatement对象,存入mappedStatements中。(上文介绍了)当判断解析到接口时,会建此接口对应的MapperProxyFactory对象,存入HashMap中,key =接口的字节码对象,value =此接口对应的MapperProxyFactory对象。
源码剖析-getmapper()进入 sqlSession.getMapper(UserMapper.class )中
继承了INcocationhandler实现invoke方法
最终执行的还是sqlSession方法呀
public Objectexecute(SqlSession sqlSession, Object[] args) {
Object result;
//判断mapper中的方法类型,最终调用的还是SqlSession中的方法
switch (command.getType()) {
case INSERT: {
// 转换参数
Object param =method.convertArgsToSqlCommandParam(args);
// 执行 INSERT 操作
// 转换rowCount
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
// 转换参数
Object param =method.convertArgsToSqlCommandParam(args);
// 转换rowCount
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
// 转换参数
Object param =method.convertArgsToSqlCommandParam(args);
// 转换rowCount
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
// 无返回,并且有 ResultHandler 方法参数,则将查询的结果,提交给 ResultHandler 进行处理
if (method.returnsVoid() &&method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result =null;
// 执行查询,返回列表
}else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
// 执行查询,返回Map
}else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
// 执行查询,返回Cursor
}else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
// 执行查询,返回单个对象
}else {
// 转换参数
Object param =method.convertArgsToSqlCommandParam(args);
// 查询单条
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional() &&
(result ==null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " +command.getName());
}
// 返回结果为 null ,并且返回类型为基本类型,则抛出 BindingException 异常
if (result ==null &&method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" +command.getName()
+" attempted to return null from a method with a primitive return type (" +method.getReturnType() +").");
}
// 返回结果
return result;
}
哈哈