String resource = "mybatis-config.xml";
Reader reader;
try {
//将XML配置文件构建为Configuration配置类
reader = Resources.getResourceAsReader(resource);
// 通过加载配置文件流构建一个SqlSessionFactory DefaultSqlSessionFactory
SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
// 数据源 执行器 DefaultSqlSession
SqlSession session = sqlMapper.openSession();
try {
// 执行查询 底层执行jdbc
User user = (User)session.selectOne("com.tuling.mapper.UserMapper.selectById", 1L);
UserMapper mapper = session.getMapper(UserMapper.class);
System.out.println(mapper.getClass());
User user = mapper.selectById(1L);
session.commit();
System.out.println(user.getUserName());
} catch (Exception e) {
e.printStackTrace();
}finally {
session.close();
}
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
这句话主要就是解析我们的xml配置文件生成Configuration对象,构建SqlSessionFactory

我们看下xml怎么变成Configuration的
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--SqlSessionFactoryBuilder中配置的配置文件的优先级最高;config.properties配置文件的优先级次之;properties标签中的配置优先级最低 -->
<properties resource="org/mybatis/example/config.properties">
<property name="username" value="dev_user"/>
<property name="password" value="F2Fa3!33TYyg"/>
</properties>
<!--一些重要的全局配置-->
<settings>
<setting name="cacheEnabled" value="true"/>
<!--<setting name="lazyLoadingEnabled" value="true"/>-->
<!--<setting name="multipleResultSetsEnabled" value="true"/>-->
<!--<setting name="useColumnLabel" value="true"/>-->
<!--<setting name="useGeneratedKeys" value="false"/>-->
<!--<setting name="autoMappingBehavior" value="PARTIAL"/>-->
<!--<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>-->
<!--<setting name="defaultExecutorType" value="SIMPLE"/>-->
<!--<setting name="defaultStatementTimeout" value="25"/>-->
<!--<setting name="defaultFetchSize" value="100"/>-->
<!--<setting name="safeRowBoundsEnabled" value="false"/>-->
<!--<setting name="mapUnderscoreToCamelCase" value="false"/>-->
<!--<setting name="localCacheScope" value="STATEMENT"/>-->
<!--<setting name="jdbcTypeForNull" value="OTHER"/>-->
<!--<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>-->
<!--<setting name="logImpl" value="STDOUT_LOGGING" />-->
</settings>
<typeAliases>
</typeAliases>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!--默认值为 false,当该参数设置为 true 时,如果 pageSize=0 或者 RowBounds.limit = 0 就会查询出全部的结果-->
<!--如果某些查询数据量非常大,不应该允许查出所有数据-->
<property name="pageSizeZero" value="true"/>
</plugin>
</plugins>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://10.59.97.10:3308/windty"/>
<property name="username" value="windty_opr"/>
<property name="password" value="windty!234"/>
</dataSource>
</environment>
</environments>
<databaseIdProvider type="DB_VENDOR">
<property name="MySQL" value="mysql" />
<property name="Oracle" value="oracle" />
</databaseIdProvider>
<mappers>
<!--这边可以使用package和resource两种方式加载mapper-->
<!--<package name="包名"/>-->
<!--<mapper resource="./mappers/SysUserMapper.xml"/>-->
<mapper resource="./mappers/CbondissuerMapper.xml"/>
</mappers>
</configuration>

构建了XMLConfigBuilder 对象用来解析xml
这里面没啥好说的,就是解析xml然后负值到Configuration属性上,包括日志、别名、插件、数据源、数据库厂商、typeHandler、mapper,这样我们Configuration对象就构建好了。
重点看下mapper怎么解析的

构建了XMLMapperBuilder用来解析namespace、参数、resultMap、缓存等
ResultMap
org.apache.ibatis.builder.xml.XMLMapperBuilder#resultMapElement(org.apache.ibatis.parsing.XNode, java.util.List<org.apache.ibatis.mapping.ResultMapping>, java.lang.Class<?>)
这里会构建ResultMapping集合

其实也就是我们一些jdbcType、javaType,对应的列,以及resultMap唯一id

然后放到
protected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection");
id 就是 唯一id
接下来看怎么解析sql 标签

org.apache.ibatis.builder.xml.XMLStatementBuilder#parseStatementNode
解析一些缓存、节点名称、节点类型SqlCommandType(后续执行sql 会用到,判断是增删改查)、自增id、解析sql、resultMap 最后封装成MappedStatement当道Configuration的mappedStatements中,key 就是namsespace+MappedStatementId
重点看下sql 怎么解析的

分为动态sql、和静态sql,返回SqlSource 放入我们的MappedStatement
sqlSource 有getBoundSql 方法

然后创建 MapperProxyFactory 放入到
Configuration的MapperRegistry的knownMappers.put(type, new MapperProxyFactory<>(type));
SqlSession session = sqlMapper.openSession();

其实就是构建了我们的DefaultSqlSession,DefaultSqlSession对象中包含Executor和Configuration
我们看下Executor

默认就是SimpleExecutor
如果开启缓存CachingExecutor,然后调用拦截器
UserMapper mapper = session.getMapper(UserMapper.class);


可以看到就是从我们的configuration的mapperRegistry 中拿,看下具体怎么拿的


从knownMappers拿到MapperProxyFactory,MapperProxyFactory创建代理,所以最后返回的对象就是MapperProxy
User user = mapper.selectById(1L);
也就是我们代理对象 的
org.apache.ibatis.binding.MapperProxy#invoke

首先 封装 MapperMethod


通过MappedStatement解析出name和type
接下来执行

其实最后还是调用了sqlSession的方法
sqlSession最后还是调用了executor


拿出sql
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
会解析我们的sql,还记得上面的动态sql吗

如果开启了二级缓存,从二级缓存先取


可以看到二级缓存是装饰器模式,可以实现线程安全、fifo内存淘汰、命中率统计
二级缓存没有,从一级缓存取,一级缓存就是一个map
一级缓存是sqlSession级别的,insert、update 操作会清空缓存,commit 也会,所以同一个sqllSerssion 多次操作 会命中一级缓存,一级缓存默认开启,二级缓存默认关闭,且可以持久化,二级缓存提交的时候才回去更新进去,防止回滚脏读,之前也是暂存在map里
org.apache.ibatis.cache.decorators.TransactionalCache#flushPendingEntries



new RoutingStatementHandler的时候会初始化父类,这个时候会进行


ParameterHandler和ResultSetHandler的拦截
StatementHandler 拦截



通过typeHandler 设置我们的参数

org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleResultSet
org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleRowValues
org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleRowValuesForNestedResultMap
org.apache.ibatis.executor.resultset.DefaultResultSetHandler#applyPropertyMappings
org.apache.ibatis.executor.resultset.DefaultResultSetHandler#getPropertyMappingValue
大概思想就是通过
TypeHandler 对应类型的实现类 从resultSet 中 通过列名 拿到对应的java类型的值,反射放到object上
Plugin



上面说过 ParameterHandler、ResultSetHandler、StatementHandler、Executor实例化后会调用我们的InterceptorChain.pluginAll方法,其实就是调用每个拦截器的plugin方法
所以我们plugin方法就是返回代理对象,默认Plugin.wrap(target, this);
我们看看他做了什么

其实也就是判断当前自定义注解上的type和 目前调用plugin是不是一样,一样就能返回代理对象,那代理对象调用的时候如果方法包含目标方法又回调了我们Interceptor的intercept方法,否则调用本身方法




![[Flink]部署模式(看pdf上的放上面)](https://img-blog.csdnimg.cn/img_convert/c678d04caffc4f4d808fd0867bb8e4c6.png)


![[游戏开发][Unity]Assetbundle打包篇(4)开始打包](https://img-blog.csdnimg.cn/img_convert/acdc27b7494d43fb3bf70d75f4166f00.png)












