目录
Mybatis核心流程
Mybatis核心流程三大阶段
初始化阶段:读取XML配置文件和注解中的额配置信息,创建配置对象,并完成各个模块的初始化工作
代理阶段:封装iBatis的编程模型,使用mapper接口开发的初始化工作
数据读写阶段:通过SqlSession完成SQL的解析,参数的映射,SQL的执行、结果的解析过程
面试题:为什么使用mapper接口就能操作数据库?
答:通过配置文件解读+动态代理的增强实现
一、初始化阶段
1.1、Mybatis的初始化,建造者模式
建造者模式(Builder Pattern)使用多个简单的额对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
建造者模式UML:
Builder:给出一个抽象接口,以规范产品对象的各个组成成分的建造。这个接口规定要实现复杂对象的哪些部分的创建,并不涉及具体的对象部件的创建;
ConcreteBuilder:实现Builder接口,针对不同的商业逻辑,具体化复杂对象的各个部分的创建。在建造过程完成后,提供产品的实例;
Director:调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建;
Product:要创建的复杂对象
建造者模式逐渐变成了现在流行的编程风格,流式编程。
Random random = new Random(); random.ints().limit(10).forEach(System::out::println);
mybatis建造者类图:
问题:
为什么要使用建造者模式实例化对象?
建造者模式使用场景:
a、需要生成的对象具有复杂的内部接口,实例化对象时要屏蔽掉对象内部的细节,让上层代码与复杂对象的实例化过程解耦,可以使用建造者模式;简而言之,如果“遇到多个构造器参数时,要考虑用建造者模式”;
b、一个对象的实例化是依赖各个组件的生产及装配顺序,关注的是一步一步地组装出目标对象,可以使用建造者模式;
建造者模式和工厂模式有何区别?
对象复杂度:
建造者建造的对象更加复杂,是一个复合产品,它由各个部件复合而成,部件不同产品对象不同,生成产品粒度细;
在工厂模式里,我们关注的是一个产品的整体,无序关心产品的各个部分是如何创建出来的;
客户端参与程度:
建造者模式,客户端对象参与了产品的创建,决定了产品的类型和内容,参与度高;适合实例化对象时属性变化频繁的场景;
工厂模式,客户端对产品的创建过程参与度低,对象实例化时属性值相对比较固定;
1.2、Mybatis的初始化
将核心配置文件、Mybatis映射器(mapper.xml)
XMLConfigBuilder:解析核心配置文件,就是加载mybatis-config.xml里的内容
源码位置:
org.apache.ibatis.session.SqlSessionFactoryBuilder#build()
org.apache.ibatis.builder.xml.XMLConfigBuilder#parse
public void parse() {
//判断是否已加载该配置
if (!this.configuration.isResourceLoaded(this.resource)) {
this.configurationElement(this.parser.evalNode("/mapper"));//处理mapper节点
this.configuration.addLoadedResource(this.resource);//将mapper文件添加到configuration.loadedResources中
this.bindMapperForNamespace();//注册mapper接口
}
//处理解析失败的ResultMap节点
this.parsePendingResultMaps();
//处理解析失败的CacheRef节点
this.parsePendingChacheRefs();
//处理解析失败的Sql语句
this.parsePendingStatements();
}
org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration
//标签解析
//解析<setting>节点
Properties settings = this.settingsAsPropertiess(root.evalNode("settings"));
//解析<setting>节点
this.propertiesElement(root.evalNode("properties"));
this.loadCustomVfs(settings);
//解析<typeAliases>节点
this.typeAliasesElement(root.evalNode("typeAliases"));
//解析<plugins>节点
this.pluginElement(root.evalNode("plugins"));
//解析<objectFactory>节点
this.objectFactoryElement(root.evalNode("objectFactory"));
//解析<objectWrapperFactory>节点
this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
//解析<reflectorFactory>节点
this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
this.settingsElement(settings);
//解析<environments>节点
this.environmentsElement(root.evalNode("environments"));
//解析<databaseIdProvider>节点
this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//解析<typeHandlers>节点
this.typeHandlerElement(root.evalNode("typeHandlers"));
//解析<mappers>节点
this.mapperElement(root.evalNode("mappers"));
XMLMapperBuilder:解析mapper.xml中的映射信息等
private void configurationElement(XNode context) {
try {
//解析<namespace>标签
String namespace = context.getStringAttribute("namespace");
if (namespace != null && !namespace.equals("")) {
//设置builderAssistant的namespace属性
this.builderAssistant.setCurrentNamespace(namespace);
//解析cache-ref节点
this.cacheRefElement(context.evalNode("cache-ref"));
// 重点分析:解析cache节点 -------------1------------
this.cacheElement(context.evalNode("cache"));
//解析parameterMap节点(已废弃)
this.parameterMapElement(context.evalNodes("/mapper/parameterMap"));
// 重点分析:解析resultMap节点(基于数据结果去理解) 解析cache节点-----------2----------
this.resultMapElements(context.evalNodes("/mapper/resultMap"));
//解析sql节点
this.sqlElement(context.evalNodes("/mapper/sql"));
// 重点分析:解析select、insert、update、delete节点 ------------3------------
this.buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} else {
throw new BuilderException("Mapper's namespace cannot be empty");
}
} catch (Exception var3) {
throw new BuilderException("Error parsing Mapper XML. Cause: " + var3, var3);
}
}
数据结构:
public class ResultMap {
private String id;//resultMap的id属性
private Class<?> type;//resultMap的type属性
private List<ResultMapping> resultMappings;//除discriminator节点外的映射关系
private List<ResultMapping> idResultMappings;//记录id或者<constructor>中idArg的映射关系
private List<ResultMapping> constructorResultMappings;//记录<constructor>标志的映射关系
private List<ResultMapping> propertyResultMappings;//记录非<constructor>标志的映射关系
private Set<String> mappedColumns;//记录所有映射关系的columns字段
private Discriminator discriminator;//鉴别器,对应discriminator节点
private boolean hasNestedResultMaps;//是否有嵌套结果映射
private boolean hasNestedQueries;//是否有嵌套查询
private Boolean autoMapping;//是否开启了自动映射
}
public class ResultMapping {
private Configuration configuration;//引用的configuration对象
private String property;//对应节点的property属性
private String column;//对应节点的column属性
private Class<?> javaType;//对应节点的javaType属性
private JdbcType jdbcType;//对应节点的jdbcType属性
private TypeHandler<?> typeHandler;//对应节点的TypeHandler属性
private String nestedResultMapId;//对应节点的resultMap属性套,嵌套结果时使用
private String nestedQueryId;//对应节点的select属性,嵌套查询时使用
private Set<String> notNullColumns;//对应节点的notnullColumn属性
private String columnPrefix;//对应节点的columnPrefix属性
private List<ResultFlag> flags;//标志,id或者constructor
private List<ResultMapping> composites;
private String resultSet;//对应节点的resultSet属性
private String foreignColumn;//对应节点的foreignColumn属性
private boolean lazy;//对饮该节点的fetchType属性,是否延迟加载
}
XMLStatementBuilder:解析mapper.xml中的sql节点等
源码位置:
org.apache.ibatis.builder.xml.XMLMapperBuilder#buildStatementFromContext(List<XNode> list, String requiredDatabaseId)
org.apache.ibatis.builder.xml.XMLStatementBuilder#parseStatementNode
public void parseStatementNode() {
String id = this.context.getStringAttribute("id");
String databaseId = this.context.getStringAttribute("databaseId");
if (this.databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
Integer fetchSize = this.context.getIntAttribute("fetchSize");
Integer timeout = this.context.getIntAttribute("timeout");
String parameterMap = this.context.getStringAttribute("parameterMap");
String parameterType = this.context.getStringAttribute("parameterType");
Class<?> parameterTypeClass = this.resolveClass(parameterType);
String resultMap = this.context.getStringAttribute("resultMap");
String resultType = this.context.getStringAttribute("resultType");
String lang = this.context.getStringAttribute("lang");
LanguageDriver langDriver = this.getLanguageDriver(lang);
Class<?> resultTypeClass = this.resolveClass(resultType);
String resultSetType = this.context.getStringAttribute("resultSetType");
StatementType statementType = StatementType.valueOf(this.context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
ResultSetType resultSetTypeEnum = this.resolveResultSetType(resultSetType);
//根据sql节点的名称获取sqlCommandType(insert、update、delete、select)
String nodeName = this.context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = this.context.getBooleanAttribute("flushCache", !isSelect);
boolean useCache = this.context.getBooleanAttribute("useCache", isSelect);
boolean resultOrdered = this.context.getBooleanAttribute("resultOrdered", false);
//在解析sql语句之前先解析<include>节点
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(this.configuration, this.builderAssistant);
includeParser.applyIncludes(this.context.getNode());
//在解析sql语句之前先处理<selectKey>子节点,并在xml节点中删除
this.processSelectKeyNodes(id, parameterTypeClass, langDriver);
//解析sql语句是解析mapper.xml的核心,实例化sqlSource,使用sqlSource封装sql语句
SqlSource sqlSource = langDriver.createSqlSource(this.configuration, this.context, parameterTypeClass);
String resultSets = this.context.getStringAttribute("resultSets");
String keyProperty = this.context.getStringAttribute("keyProperty");
String keyColumn = this.context.getStringAttribute("keyColumn");
//根据<sekectKey>标签获取对应的SelectKeygenerator的id
String keyStatementId = id + "!selectKey";
keyStatementId = this.builderAssistant.applyCurrentNamespace(keyStatementId, true);
Object keyGenerator;
if (this.configuration.hasKeyGenerator(keyStatementId)) {
keyGenerator = this.configuration.getKeyGenerator(keyStatementId);
} else {
keyGenerator = this.context.getBooleanAttribute("useGeneratedKeys", this.configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
}
this.builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, (KeyGenerator)keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
}
数据结构:
public final class MappedStatement {
private String resource;//节点完整的id属性,包括命名空间
private Configuration configuration;
private String id;//节点的id属性
private Integer fetchSize;//节点的fetchSize属性,查询数据的条数
private Integer timeout;//节点的timeout属性,超时时间
private StatementType statementType;//节点的statementType属性,默认值StatementType,PREPARED
private ResultSetType resultSetType;//节点的resultType属性
private SqlSource sqlSource;//节点中的sql语句信息
private Cache cache;//对应的二级缓存
private ParameterMap parameterMap;//已废弃
private List<ResultMap> resultMaps;//节点的resultMaps属性
private boolean flushCacheRequired;//节点的flushCache属性是否刷新缓存
private boolean useCache;//节点的useCache属性,师傅使用二级缓存
private boolean resultOrdered;
private SqlCommandType sqlCommandType;//sql语句的类型,包括select,insert,update,delete
private KeyGenerator keyGenerator;//节点的keyGenerator属性
private String[] keyProperties;
private String[] keyColumns;
private boolean hasNestedResultMaps;//是否有嵌套的resultMap
private String databaseId;
private Log statementLog;
private LanguageDriver lang;
private String[] resultSets;//多结果集使用
}
映射器的关键类
Configuration:Mybatis启动初始化的核心就是将所有xml配置文件信息加载到Configuarion对象
中,Configuration是单例的,生命周期是应用级的;
configuration类图解:
MapperRegistry:mapper接口动态代理工厂类的注册中心。在Mybatis中,通过mapperProxy实现
InovationHandler接口,MapperProxyFactory用于生成动态代理的实例对象;
ResultMap:用于解析mapper.xml文件中的resultMap节点,使用ResultMapping来封装id,result
等子元素;两个关键类的数据结构完全对应mapper.xml配置文件的xml结构
resultMap图解
MappedStatement:用于存储mapper文件中的select、insert、update和delete节点,同时还包含
了这些节点的很多重要属性;
mappedStatment图解:
SqlSource:mapper.xml文件中的sql语句会被解析成SqlSource对象,经过解析SqlSource包含的语
句最终仅仅包含?占位符,可以直接提交给数据库执行;
mybatis初始化时序图:
二、代理阶段
Binding模块分析
解读MapperMethod,面向mapper接口编程的实质还是使用sqlSession进行操作(ibatis的操作方式),只是mybatis通过动态代理封装了一层。
@Test
public void quickStart() {
// 2.获取sqlsession 开启自动递交
SqlSession sqlSession = sqlSessionFactory.openSession(true);
// 3.获取对应mapper
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 4.执行查询语句并返回结果
User user = userMapper.list(1);
System.out.println(user.toString());
}
@Test
// 快速入门本质分析
public void originalOperation() {
// 2.获取sqlsession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 3.执行查询语句并返回结果
User user = sqlSession.selectOne("com.enjoy.mybatis.mapper.UserMapper.list", 1);
System.out.println(user.toString());
}
binding模块类图:
MapperRegistry:mapper接口和对应的代理对象工厂的注册中心;
MapperProxyFactory:用于生成mapper接口动态地理的实例对象;
MapperProxy:实现了IvocationHandler接口,它是增强mapper接口的实现;
MapperMethod:封装了Mapper接口中对应方法的信息,以及对应的sql语句的信息;它是
mapper接口与配置文件中sql语句的桥梁;mapperMethod对象不记录任何状态信息,所以它
可以在多个代理对象之间共享
SqlCommand:从configuration中获取方法的命名空间.方法名以及sql语句的类型;
MethodSingature:封装mapper接口方法的相关信息(入参,返回类型);
ParamNameResolver:解析mapper接口方法中的入参;
MapperProxyFactory类分析:
public class MapperProxyFactory<T> {
//mapper接口的class对象
private final Class<T> mapperInterface;
//key是mapper接口中的某个方法的method对象,value是对应mapperMethod,mapperMethod对象不记录任何状态信息
//所以它可以在多个代理对象之间共享
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap();
protected T newInstance(MapperProxy<T> mapperProxy) {
//创建实现了mapper接口的动态代理对象
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
//每次使用都会创建新的MapperProxy对象
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}
}
MapperProxy类分析:
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;//记录关联的sqlSession对象
private final Class<T> mapperInterface;//mapper接口对应的class对象
//key是mapper接口中的某个方法的method对象,value是对应mapperMethod,mapperMethod对象不记录任何状态信息
//所以它可以在多个代理对象之间共享
private final Map<Method, MapperMethod> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {//如果是object本身的方法则不增强
try {
return method.invoke(this, args);
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
} else {
//从缓存中获取获取MapperMethod对象,如果没有则创建一个添加到缓存中
MapperMethod mapperMethod = this.cachedMapperMethod(method);
//调用excut方法执行sql
return mapperMethod.execute(this.sqlSession, args);
}
}
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
this.methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
}
MapperMethod类解析:
public class MapperMethod {
//从configuration中获取方法的命名空间.方法名以及sql语句类型
private final MapperMethod.SqlCommand command;
//封装mapper接口方法的相关信息(入参,返回类型等)
private final MapperMethod.MethodSignature method;
//执行方法
public Object execute(SqlSession sqlSession, Object[] args) {
Object param;
Object result;
switch(this.command.getType()) {
case INSERT:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
break;
case UPDATE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
break;
case DELETE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
break;
case SELECT:
if (this.method.returnsVoid() && this.method.hasResultHandler()) {//返回值为void
this.executeWithResultHandler(sqlSession, args);
result = null;
} else if (this.method.returnsMany()) {//返回值是为集合或者数组
result = this.executeForMany(sqlSession, args);
} else if (this.method.returnsMap()) {//返回值为map
result = this.executeForMap(sqlSession, args);
} else if (this.method.returnsCursor()) {//返回值为游标
result = this.executeForCursor(sqlSession, args);
} else {
param = this.method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(this.command.getName(), param);
}
break;
case FLUSH://处理返回为单一对象的情况、通过参数解析器
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + this.command.getName());
}
if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
} else {
return result;
}
}
}
MethodSigature类解析:
public static class MethodSignature {
private final boolean returnsMany;//返回参数是否为集合类型或者数组
private final boolean returnsMap;//返回参数是否为map
private final boolean returnsVoid;//返回值为空
private final boolean returnsCursor;//返回值是否为游标类型
private final Class<?> returnType;//返回值类型
private final String mapKey;
private final Integer resultHandlerIndex;
private final Integer rowBoundsIndex;
private final ParamNameResolver paramNameResolver;//该方法的参数解析器
}
sqlCommand类解析:
public static class SqlCommand {
//sql的名称,命名空间+方法名称
private final String name;
//获取sql语句的类型
private final SqlCommandType type;
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
//获取方法名称
String statementName = mapperInterface.getName() + "." + method.getName();
//获取mapperStatement
MappedStatement ms = null;
if (configuration.hasStatement(statementName)) {
ms = configuration.getMappedStatement(statementName);
} else if (!mapperInterface.equals(method.getDeclaringClass())) {
String parentStatementName = method.getDeclaringClass().getName() + "." + method.getName();
if (configuration.hasStatement(parentStatementName)) {
ms = configuration.getMappedStatement(parentStatementName);
}
}
if (ms == null) {
if (method.getAnnotation(Flush.class) == null) {
throw new BindingException("Invalid bound statement (not found): " + statementName);
}
this.name = null;
this.type = SqlCommandType.FLUSH;
} else {
//如果statement不为空
this.name = ms.getId();//获取sql的名称,命名空间+方法名称
this.type = ms.getSqlCommandType();//获取sql的语句类型
if (this.type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + this.name);
}
}
}
}
三、数据读写阶段
创建SqlSession 策略模式
策略模式定义了一系列的算法,并将每一个算法封装起来,而且是他们可以互相替换,让算法独立于使用它的客户而独立变化。
策略模式的使用场景:
针对同一类型问题的多种处理方式,仅仅是具体行为有差别时;
出现同一抽象有多个子类,而又需要使用if-else或者switch-case来选择具体子类时
Context:算法调用者,使用setStrategy方法灵活的选择策略(strategy);
Strategy:算法的统一接口;
ConcreteStrategy:算法的具体实现;
SqlSession解析
SqlSessionshimybatis对外提供的最关键的核心接口,通过它可以执行数据库读写命令、获取映射器、管理事务等。
UML图
SqlSession查询接口嵌套关系图:
SqlSessionFactory类解析:默认实现类:DefaultSqlSessionFactory
//sqlSession的获取有两种方式
//根据数据源获取SqlSession
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
DefaultSqlSession var8;
try {
Environment environment = this.configuration.getEnvironment();
TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
Executor executor = this.configuration.newExecutor(tx, execType);
var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
} catch (Exception var12) {
this.closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + var12, var12);
} finally {
ErrorContext.instance().reset();
}
return var8;
}
//根据连接信息获取SqlSession
private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
DefaultSqlSession var8;
try {
boolean autoCommit;
try {
autoCommit = connection.getAutoCommit();
} catch (SQLException var13) {
autoCommit = true;
}
Environment environment = this.configuration.getEnvironment();
TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
Transaction tx = transactionFactory.newTransaction(connection);
Executor executor = this.configuration.newExecutor(tx, execType);
var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
} catch (Exception var14) {
throw ExceptionFactory.wrapException("Error opening session. Cause: " + var14, var14);
} finally {
ErrorContext.instance().reset();
}
return var8;
}
Executor组件分析:
executor是mybatis核心接口之一,定义了数据库操作最基本的方法,sqlSession的功能都是基于它来实现的
模板模式:
模板欧式:一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定实现;
mybatis中的模板模式实践:baseExecutor
baseExecutor执行流程图
查询数据库的得到结果有三种方式执行:simple、reuse、batch
Executor的三个实现类解读:
SimpleExecutor:默认配置,使用prepareStatement对象访问数据库,每次访问都要创建新的prepareStatement对象;
ReuseExecutor:适用预编译prepareStatement对象访问数据库,访问时,会重用缓存中的statement对象;
BatchExecutor:实现批量执行多条sql语句的能力;
Executor的三个重要小弟
通过对SimpleExecutor.doQuery()方法的解读发现,Executor是个指挥官,它在调度三个小弟(StatementHandler、ParameterHandler、ResultSetHandler)工作:
源码解读:
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
List var9;
try {
//获取configuration对象
Configuration configuration = ms.getConfiguration();
//创建statementHandler
StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//通过statementHandler对象创建statement对象,并使用prepareStatement对占位符进行处理
stmt = this.prepareStatement(handler, ms.getStatementLog());
//通过statementHandler对象调用ResultHandler将结果集转化为指定对象返回
var9 = handler.query(stmt, resultHandler);
} finally {
this.closeStatement(stmt);
}
return var9;
}
//创建statement对象
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
//获取connection对象动态代理,添加日志能力
Connection connection = this.getConnection(statementLog);
//通过不同的statementHandler,利用connection创建(prepare)statement
Statement stmt = handler.prepare(connection, this.transaction.getTimeout());
//利用prepareStatement处理占位符
handler.parameterize(stmt);
return stmt;
}
StatementHandler:它的作用是使用数据库的Statement或者prepareStatement执行操作,起承上启下的作用;
分析:statementHandler完成mybatis最核心的工作,也是executor实现的基础,功能包括:创建statement对象,为sql语句绑定从桉树,执行增删改查等sql语句,将结果集进行转化;
源码分析:
public interface StatementHandler {
//从连接中获取一个statement
Statement prepare(Connection var1, Integer var2) throws SQLException;
//占位符处理,绑定statement执行时需要的实参
void parameterize(Statement var1) throws SQLException;
//批量执行sql语句
void batch(Statement var1) throws SQLException;
//执行update/insert/delete语句
int update(Statement var1) throws SQLException;
//执行select语句
<E> List<E> query(Statement var1, ResultHandler var2) throws SQLException;
<E> Cursor<E> queryCursor(Statement var1) throws SQLException;
//获取sql语句
BoundSql getBoundSql();
//获取封装的参数处理器 ParameterHandler
ParameterHandler getParameterHandler();
}
BaseStatementHandler:所有子类的抽象父类,定义了初始化statemen的操作顺序,由子类实现具体的实例化不同的statement(模板模式);
源码分析:
public abstract class BaseStatementHandler implements StatementHandler {
protected final Configuration configuration;
protected final ObjectFactory objectFactory;
protected final TypeHandlerRegistry typeHandlerRegistry;
//结果处理器,对数据返回的结果集(resultSet)进行封装,返回用户指定的实体类å
protected final ResultSetHandler resultSetHandler;
//sql占位符处理器,对预编译的sql语句进行参数设置
protected final ParameterHandler parameterHandler;
//记录执行语句的executor对象
protected final Executor executor;
//sql语句对应的mapperStatement
protected final MappedStatement mappedStatement;
//分页信息
protected final RowBounds rowBounds;
//sql语句
protected BoundSql boundSql;
}
RoutingStatementHandler:Excutor组件真正实例化的子类,使用静态代理模式,根据上下文决定创建哪个具体实体类;
源码分析:
public class RoutingStatementHandler implements StatementHandler {
private final StatementHandler delegate;//底层封装的真正的statementHandler对象
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//routingStatementHandler醉蛛哟的而功能就是根据mapperStatementHandler的配置,生成一个statement对象并赋值给delegate
switch(ms.getStatementType()) {
case STATEMENT:
this.delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
this.delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
this.delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
}
SimpleStatementHandler:使用statement对象访问数据库,无序参数化;
PrepareStatementHandler:使用预编译PrepareStatement对象访问数据库;
CallableStatementHandler:调用存储过程
ParameterHandler:对预编译的sql语句进行参数设置,sql语句中的占位符“?”都对应BoundSql.parameterMapping集合中的一个元素,在该对象中记录了对应的参数名称以及该参数的相关属性;
ResultSetHandler:对数据库返回的结果集(ResultSet)进行封装,返回用户指定的实体类型;
数据获取之后进行结果集转化的代码,重点解析
疑问:为什么不配置自动映射?
Executor内部运作过程: