前言
接着environmentElement获取数据源信息后,同级执行代码的mappersElement。里面参杂了mybatis缓存。
Mybatis源码(三)如何操作数据库
MyBatis源码(二)如何执行sql
Mybatis源码(一)获取数据源
结构小结
分析main函数:
public class MybatisMain {
public static void main(String[] args) throws IOException {
String resource = "mapper/config/mybatis-config.xml"; //全局配置
InputStream is = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession session = sqlSessionFactory.openSession();
OrderPojo orderPojo = session.selectOne("com.cbry.mybatis.OrderDao.findSimpleOrderInfo" , 0);
System.out.println(orderPojo);
}
}
我们将一条简单SQL的执行分为三个部分:
- Mybatis是如何获取数据库源
- Mybatis是如何获取SQL
- Mybatis是如何操作数据库
如何运行
寻到DefaultSqlSessionFactory.openSession方法:这里注意到this.configuration.getEnvironment, 我们在获取数据源的时候setEnvironment。如何连接起来的呢?都是同一个类对象:
//setEnvironment
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
//getEnvironment
SqlSession session = sqlSessionFactory.openSession();
由openSession进入:
这里注意到执行器的产生,这里默认是execTyoe SIMPLE
BaseExcutor这是一个抽象类,用来存放Executor共有功能的,比如获取连接、缓存操作(一级缓存)
Caching Executor同样实现了Executor接口,拥有二级缓存的功能,也有着Executor属性,而且使用了**装饰者模式(在不改变原有类结构和继承的情况下,通过组装这个类对象,来扩展一个新功能
数据库源的配置信息在SqlSessionFactory里面的openssion用到
一级缓存默认开启的源码
selectOne
对应代码
OrderPojo orderPojo = session.selectOne("com.cbry.mybatis.OrderDao.findSimpleOrderInfo" , 0);
selectOne和too many报错
我们把存起来的MappedStatemen又拿出来了。
在query里面(CachingExecutor实现类):我们发现执行SQL
传入SQL为何由#dv 变为 ?
执行器里面的sql:传入SQL由#dv 变为 ? :防止SQL注入问题,出入的SQL语句参数全部用?代替的,预编译PreparedStatement将参数值用单引号包起来,进行转义,仅将其看作字符串:
延申:技术的本质:ORM框架将对象的值 映射 对应到数据库类型: 如 String -> varchar。
ORM框架只是映射,Java操作Mysql只能通过JDBC操作(本质:是封装他)。
运用到的设计模式:构建者模式、工厂模式、动态代理模式
ps:学习源码:主干方法 => debug明白流程哪里出现问题,快速定位。 出现问题的方法最终会回溯到主干逻辑源码中。
缓存
如果没有用到二级缓存,依旧使用BaseExcutor里面的query方法(是否使用一级缓存)
不使用一级缓存,则query里面调用queryFromDatabase,再调用doQuery
doQuery构造一个RoutingStatementHandler ,
具体是在构造的时候就设置了PREPARED,选择PreparedStatementHandler。
PreparedStatementHandler到这里实际用的就已经是JDBC的方法了
ResultSetWrapper怎么封装的呢?
三个集合columnNames、jdbcTypes、classNames遍历获取值:
得到对应表字段的列名、值类型、java对应类型,后面的转换
继续往下走,找到handleResulSet即对结果集
进行处理:
再往下挖DefaultResultSetHandler.handleRowValues(),这里再往下的handleRowValuesForNestedResultMap方法,已经相对于遍历row结果集了:
while(rs.next()){
//Retrieve by column name
int id = rs.getInt("id");
int age = rs.getInt("age");
String first = rs.getString("first");
String last = rs.getString("last");
}
getRowValue里面的applyAutomaticMappings
下面这里的this,getNullableResult方法具体实现类是:
StringTypeHandler,对应字段String类型
再回顾一下,这里对应上了rs.getString,换做其它类型如int同理:
while(rs.next()){
//Retrieve by column name
int id = rs.getInt("id");
int age = rs.getInt("age");
String first = rs.getString("first");
String last = rs.getString("last");
}
输出结果:(PS:mapper.xml的代码早就在SqlSessionFactory build的时候就加载到jvm里面了)执行就只是调用执行而已