一、binding模块分析
1、为什么使用mapper接口就能操作数据库?
配置文件解读 +动态代理的增强
@Test// 快速入门
public void quickStart() throws IOException {
SqlSession sqlSession = sqlSessionFactory.openSession();
TUserMapper mapper = sqlSession.getMapper(TUserMapper.class);
TUser user = mapper.selectByPrimaryKey(2); // 4.执行查询语句并返回单条数据
System.out.println(user);
}
快速入门本质分析 ibatis方式
@Test
public void quickStartOriginal(){
// 2.获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 3.执行查询语句并返回单条数据
TUser user = sqlSession.selectOne("com.chj.mybatis.mapper.TUserMapper.selectByPrimaryKey",1);
System.out.println(user.toString());
}
2、binding模块逻辑架构分析
MapperRegistry:mapper接口和对应的代理对象工厂的注册中心;
MapperProxyFactory:用于生成mapper接口动态代理的实例对象;
MapperProxy:实现了InvocationHandler接口,它是增强mapper接口的实现;
MapperMethod:封装了Mapper接口中对应方法的信息,以及对应的sql语句的信息;它是mapper接口与映射配置文件中sql语句的桥梁;
2.1、Mybatis初始化解析mapper.xml中的增删改查节点
接着第一阶段初始化的最后一步让我们从XMLMapperBuilder.bindMapperForNamespace()方法,解析select、insert、update、delete节点,开始入手吧!
private void configurationElement(XNode context) {
try {//获取mapper节点的namespace属性
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace); //设置builderAssistant的namespace属性
cacheRefElement(context.evalNode("cache-ref"));//解析cache-ref节点
//重点分析 :解析cache节点----------------1-------------------
cacheElement(context.evalNode("cache"));
parameterMapElement(context.evalNodes("/mapper/parameterMap"));//解析parameterMap节点(已废弃)
//重点分析 :解析resultMap节点(基于数据结果去理解)----------------2-------------------
resultMapElements(context.evalNodes("/mapper/resultMap"));
sqlElement(context.evalNodes("/mapper/sql"));//解析sql节点
//重点分析 :解析select、insert、update、delete节点 ----------------3-------------------
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
}
}
解析select、insert、update、delete节点:
private void buildStatementFromContext(List<XNode> list) {
if (configuration.getDatabaseId() != null) {
buildStatementFromContext(list, configuration.getDatabaseId());
}
buildStatementFromContext(list, null);
}
//处理所有的sql语句节点并注册至configuration对象
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
for (XNode context : list) {
//创建XMLStatementBuilder 专门用于解析sql语句节点
final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
try {//解析sql语句节点
statementParser.parseStatementNode();
} catch (IncompleteElementException e) {
configuration.addIncompleteStatement(statementParser);
}
}
}
解析配置的sql节点并通过builderAssistant实例化MappedStatement,并注册至configuration对象
public void parseStatementNode() {
//获取sql节点的id
String id = context.getStringAttribute("id");
String databaseId = context.getStringAttribute("databaseId");
......
//通过builderAssistant实例化MappedStatement,并注册至configuration对象
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
MappedStatement statement = statementBuilder.build();
configuration.addMappedStatement(statement);
return statement;
Configuration对象mapper文件中增删改查操作的注册中心:
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<>("Mapped Statements collection");
public void addMappedStatement(MappedStatement ms) {
mappedStatements.put(ms.getId(), ms);
}
2.2、MapperRegistry代码解析
通过sqlSession.getMapper(TUserMapper.class);方法找到对应的源码默认实现代码SqlSessionManager与DefaultSqlSession都会调用Configuration对象的getMapper方法:
@Override
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
MapperRegistry类是mapper接口和对应的代理对象工厂的注册中心:
public class MapperRegistry {
private final Configuration config;//config对象,mybatis全局唯一的
//记录了mapper接口与对应MapperProxyFactory之间的关系
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
org.apache.ibatis.binding.MapperProxyFactory.newInstance(MapperProxy<T> mapperProxy)
protected T newInstance(MapperProxy<T> mapperProxy) {
//创建实现了mapper接口的动态代理对象
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
//每次调用都会创建新的MapperProxy对象
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
接下来就是关于MapperProxy的具体实现逻辑处理部分代码,实现了InvocationHandler接口,它是增强mapper接口的实现,具体实现方法为invoke方法:
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {//如果是Object本身的方法不增强
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
// 1.从缓存中获取mapperMethod对象,如果缓存中没有,则创建一个,并添加到缓存中
final MapperMethod mapperMethod = cachedMapperMethod(method);
// 2.调用execute方法执行sql
return mapperMethod.execute(sqlSession, args);
}
2.3、解读MapperMethod
MapperMethod封装了Mapper接口中对应方法的信息,以及对应的sql语句的信息;它是mapper接口与映射配置文件中sql语句的桥梁;MapperMethod对象不记录任何状态信息,所以它可以在多个代理对象之间共享。
SqlCommand(内部类):从configuration中获取方法的命名空间,方法名以及SQL语句的类型
MethodSignature(内部类):封装mapper接口方法的相关信息(入参,返回类型)
ParamNameResolver:解析mapper接口方法中的入参
// 从缓存中获取mapperMethod对象,如果缓存中没有,则创建一个,并添加到缓存中
final MapperMethod mapperMethod = cachedMapperMethod(method);
private MapperMethod cachedMapperMethod(Method method) {
return methodCache.computeIfAbsent(method, k ->
new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
MapperMethod源码如下:
public class MapperMethod {
// 从configuration中获取方法的命名空间.方法名以及SQL语句的类型
private final SqlCommand command;
// 封装mapper接口方法的相关信息(入参,返回类型);
private final MethodSignature method;
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
1)SqlCommand源码实现:
public static class SqlCommand {
//sql的名称,命名空间+方法名称
private final String name;
//获取sql语句的类型
private final SqlCommandType type;
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
final String methodName = method.getName();//获取方法名称
final Class<?> declaringClass = method.getDeclaringClass();
//从configuration中获取mappedStatement
MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,configuration);
if (ms == null) {
if(method.getAnnotation(Flush.class) != null){
name = null;
type = SqlCommandType.FLUSH;
} else {
throw new BindingException("Invalid bound statement (not found): "
+ mapperInterface.getName() + "." + methodName);
}
} else {//如果mappedStatement不为空
name = ms.getId();//获取sql的名称,命名空间+方法名称
type = ms.getSqlCommandType();//获取sql语句的类型
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
}
public String getName() {
return name;
}
public SqlCommandType getType() {
return type;
}
//从configuration中获取mappedStatement
private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
Class<?> declaringClass, Configuration configuration) {
//sql语句的id为命名空间+方法名字
String statementId = mapperInterface.getName() + "." + methodName;
if (configuration.hasStatement(statementId)) {
//从configuration中获取mappedStatement
return configuration.getMappedStatement(statementId);
} else if (mapperInterface.equals(declaringClass)) {
return null;
}
for (Class<?> superInterface : mapperInterface.getInterfaces()) {
if (declaringClass.isAssignableFrom(superInterface)) {
MappedStatement ms = resolveMappedStatement(superInterface, methodName,
declaringClass, configuration);
if (ms != null) {
return ms;
}
}
}
return null;
}
}
// 从configuration中获取mappedStatement
public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
if (validateIncompleteStatements) {
buildAllStatements();
}
return mappedStatements.get(id);
}
2)MethodSignature封装mapper接口方法的相关信息(入参,返回类型)
public static class MethodSignature {
private final boolean returnsMany;//返回参数是否为集合类型或数组
private final boolean returnsMap;//返回参数是否为map
private final boolean returnsVoid;//返回值为空
private final boolean returnsCursor;//返回值是否为游标类型
private final boolean returnsOptional;//返回值是否为Optional
private final Class<?> returnType;//返回值类型
private final String mapKey;
private final Integer resultHandlerIndex;
private final Integer rowBoundsIndex;
private final ParamNameResolver paramNameResolver;//该方法的参数解析器
public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
//通过类型解析器获取方法的返回值类型
Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
if (resolvedReturnType instanceof Class<?>) {
this.returnType = (Class<?>) resolvedReturnType;
} else if (resolvedReturnType instanceof ParameterizedType) {
this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
} else {
this.returnType = method.getReturnType();
}
//初始化返回值等字段
this.returnsVoid = void.class.equals(this.returnType);
this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
this.returnsCursor = Cursor.class.equals(this.returnType);
this.returnsOptional = Jdk.optionalExists && Optional.class.equals(this.returnType);
this.mapKey = getMapKey(method);
this.returnsMap = this.mapKey != null;
this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
this.paramNameResolver = new ParamNameResolver(configuration, method);
}
3)ParamNameResolver解析mapper接口方法中的入参
public Object convertArgsToSqlCommandParam(Object[] args) {
return paramNameResolver.getNamedParams(args);
}
public Object getNamedParams(Object[] args) {
final int paramCount = names.size();
if (args == null || paramCount == 0) {
return null;
} else if (!hasParamAnnotation && paramCount == 1) {
return args[names.firstKey()];
} else {
final Map<String, Object> param = new ParamMap<>();
int i = 0;
for (Map.Entry<Integer, String> entry : names.entrySet()) {
param.put(entry.getValue(), args[entry.getKey()]);
// add generic param names (param1, param2, ...)
final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
// ensure not to overwrite parameter named with @Param
if (!names.containsValue(genericParamName)) {
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
}
2.4、翻译过程
MapperMethod.SqlCommand. type+MapperMethod.MethodSignature.returnType:找到session中对应的方法执行。
找到命名空间和方法名: MapperMethod.SqlCommand. name
传递参数:MapperMethod.MethodSignature.paramNameResolver
具体翻译执行过程通过调用execute方法执行sql:mapperMethod.execute(sqlSession, args);
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
//根据sql语句类型以及接口返回的参数选择调用不同的
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {//返回值为void
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {//返回值为集合或者数组
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {//返回值为map
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {//返回值为游标
result = executeForCursor(sqlSession, args);
} else {//处理返回为单一对象的情况
//通过参数解析器解析解析参数
Object param = method.convertArgsToSqlCommandParam(args);
// 以ibatis方式进行数据查询
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional() &&
(result == null || !method.getReturnType().equals(result.getClass()))) {
result = OptionalUtil.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
return result;
}
这个地方的sql查询执行与我们之前的demo中使用ibatis方式查询执行代码一模一样,经过配置文件的解析封装,最终将mybatis查询的方法翻译为ibatis查询方式,整个过程就是一个翻译的过程。
// 2.获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 3.执行查询语句并返回单条数据
TUser user = sqlSession.selectOne("com.chj.mybatis.mapper.TUserMapper.selectByPrimaryKey",1);
等价于:
sqlSession.selectOne(command.getName(), param);
二、核心组件Excutor
1、Executor组件分析
Executor是MyBaits核心接口之一,定义了数据库操作最基本的方法,SqlSession的功能都是基于它来实现的
接上面sql执行翻译的过程后开始真正的执行数据查询过程:
//通过参数解析器解析解析参数
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
默认执行DefaultSqlSession(SqlSessionManager)的selectOne方法:
@Override
public <T> T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
List<T> list = this.<T>selectList(statement, parameter);
if (list.size() == 1) {
return list.get(0);
} else if (list.size() > 1) {
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
return null;
}
}
@Override
public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//从configuration中获取要执行的sql语句的配置信息
MappedStatement ms = configuration.getMappedStatement(statement);
// 通过executor执行语句,并返回指定的结果集
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
Executor接口它有两个实现类:
CachingExecutor:二级缓存实现类
BaseExecutor抽象类:实现了executor接口的大部分方法,主要提供了缓存管理和事务管理的能力,其他子类需要实现的抽象方法为:doUpdate,doQuery等方法;
2、模板模式
模板模式(Template Pattern):一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定实现;
3、BaseExecutor代码分析
模板模式应用场景:
遇到由一系列步骤构成的过程需要执行,这个过程从高层次上看是相同的,但是有些步骤的实现可能不同,这个时候就需要考虑用模板模式了。比如:Executor查询操作流程:
BaseExecutor抽象类,实现了executor接口的大部分方法,主要提供了缓存管理和事务管理的能力,其他子类需要实现的抽象方法为:doUpdate,doQuery等方法;
如果开启了二级缓存则查询首先会进入CachingExecutor的query方法进行查询,如果缓存中有直接返回,如果没有则调用BaseExecutor的query方法进行数据库查询。
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
//从MappedStatement中获取二级缓存
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);//从二级缓存中获取数据
if (list == null) {
//二级缓存为空,才会调用BaseExecutor.query
list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
BaseExecutor的query方法源码如下:
public abstract class BaseExecutor implements Executor {
private static final Log log = LogFactory.getLog(BaseExecutor.class);
protected Transaction transaction;//事务对象
protected Executor wrapper;//封装的Executor对象
protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;//延迟加载的队列
protected PerpetualCache localCache;//一级缓存的实现,PerpetualCache
protected PerpetualCache localOutputParameterCache;//一级缓存用于缓存输出的结果
protected Configuration configuration;//全局唯一configuration对象的引用
protected int queryStack;//用于嵌套查询的的层数
private boolean closed;
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//获取sql语句信息,包括占位符,参数等信息
BoundSql boundSql = ms.getBoundSql(parameter);
//拼装缓存的key值
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {//检查当前executor是否关闭
throw new ExecutorException("Executor was closed.");
}
//非嵌套查询,并且FlushCache配置为true,则需要清空一级缓存
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;//查询层次加一
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;//查询以及缓存
if (list != null) {
//针对调用存储过程的结果处理
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
// 缓存未命中,从数据库加载数据
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {//延迟加载处理
deferredLoad.load();
}
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
//如果当前sql的一级缓存配置为STATEMENT,查询完既清空一集缓存
clearLocalCache();
}
}
return list;
}
真正访问数据库获取结果的方法:
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);//在缓存中添加占位符
try { //调用抽象方法doQuery,方法查询数据库并返回结果,可选的实现包括:simple、reuse、batch
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);//在缓存中删除占位符
}
localCache.putObject(key, list);//将真正的结果对象添加到一级缓存
if (ms.getStatementType() == StatementType.CALLABLE) {//如果是调用存储过程
localOutputParameterCache.putObject(key, parameter);//缓存输出类型结果参数
}
return list;
}
抽象方法doQuery的实现类如下所示:
4、Executor的三个实现类解读
真正的实现数据库查询的实现类如下:
4.1、SimpleExecutor默认方式
默认配置,使用PrepareStatement对象访问数据库,每次访问都要创建新的PrepareStatement对象。
@Override
//查询的实现
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();//获取configuration对象
//1、创建StatementHandler对象,
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 2、StatementHandler对象创建stmt,并使用parameterHandler对占位符进行处理
stmt = prepareStatement(handler, ms.getStatementLog());
//3、通过statementHandler对象调用ResultSetHandler将结果集转化为指定对象返回
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
1)Configuration的newStatementHandler方法实现:
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//创建RoutingStatementHandler对象,实际由statmentType来指定真实的StatementHandler来实现
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
RoutingStatementHandler implements StatementHandlerExcutor,组件真正实例化的子类,使用静态代理模式,根据上下文决定创建哪个具体实体类。
2)创建StatementHandler对象创建stmt,并使用parameterHandler对占位符进行处理
//创建Statement
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
//获取connection对象的动态代理,添加日志能力;
Connection connection = getConnection(statementLog);
//通过不同的StatementHandler,利用connection创建(prepare)Statement
stmt = handler.prepare(connection, transaction.getTimeout());
//使用parameterHandler处理占位符
handler.parameterize(stmt);
return stmt;
}
3)通过statementHandler对象调用ResultSetHandler将结果集转化为指定对象返回
4.2、预编译ReuseExecutor执行
使用预编译PrepareStatement访问数据库,访问时会重用缓存中的statement对象。
ReuseExecutor的查询方法与SimpleExecutor的主要区别在于prepareStatement方法实现上面不同,主要区别就是根据sql语句检查是否缓存了对应的Statement的处理,代码如下:
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Configuration configuration = ms.getConfiguration();//获取configuration对象
//创建StatementHandler对象
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//StatementHandler对象创建stmt,并使用parameterHandler对占位符进行处理
Statement stmt = prepareStatement(handler, ms.getStatementLog());
//通过statementHandler对象调用ResultSetHandler将结果集转化为指定对象返回
return handler.<E>query(stmt, resultHandler);
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
BoundSql boundSql = handler.getBoundSql();
String sql = boundSql.getSql();//获取sql语句
if (hasStatementFor(sql)) {//根据sql语句检查是否缓存了对应的Statement
stmt = getStatement(sql);//获取缓存的Statement
applyTransactionTimeout(stmt);//设置新的超时时间
} else {//缓存中没有statment,创建statment过程和SimpleExecutor类似
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
putStatement(sql, stmt);//放入缓存中
}
//使用parameterHandler处理占位符
handler.parameterize(stmt);
return stmt;
}
4.3、BatchExecutor:实现批量执行多条SQL语句的能力。
@Override
//batchExecutor查询实现
public <E> List<E> doQuery(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException {
Statement stmt = null;
try {
flushStatements();
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameterObject, rowBounds, resultHandler, boundSql);
Connection connection = getConnection(ms.getStatementLog());
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
5、Executor中StatementHandler的三个重要小弟
通过对SimpleExecutor的 doQuery()方法的解读发现,Executor是个指挥官,它在调度三个小弟工作
StatementHandler:它的作用是使用数据库的Statement或PrepareStatement执行操作,启承上启下作用;
ParameterHandler:对预编译的SQL语句进行参数设置,SQL语句中的的占位符“?”都对应BoundSql.parameterMappings集合中的一个元素,在该对象中记录了对应的参数名称以及该参数的相关属性
ResultSetHandler:对数据库返回的结果集(ResultSet)进行封装,返回用户指定的实体类型;
Executor内部运作过程:
SimpleExecutor的prepareStatement
//StatementHandler对象创建stmt,并使用parameterHandler对占位符进行处理
Statement stmt = prepareStatement(handler, ms.getStatementLog());
//创建Statement
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
//获取connection对象的动态代理,添加日志能力;
Connection connection = getConnection(statementLog);
//通过不同的StatementHandler,利用connection创建(prepare)Statement
stmt = handler.prepare(connection, transaction.getTimeout());
// 使用parameterHandler处理占位符
handler.parameterize(stmt);
return stmt;
}
以PreparedStatementHandler的parameterize方法为例:
@Override
//使用parameterHandler对sql语句的占位符进行处理
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
ParameterHandler sql占位符处理器,对预编译的SQL语句进行参数设置
public class DefaultParameterHandler implements ParameterHandler {
private final TypeHandlerRegistry typeHandlerRegistry;//typeHandler注册中心
private final MappedStatement mappedStatement;//对应的sql节点的信息
private final Object parameterObject;//用户传入的参数
private final BoundSql boundSql;//SQL语句信息,其中还包括占位符和参数名称信息
private final Configuration configuration;
@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
//从boundSql中获取sql语句的占位符对应的参数信息
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
//遍历这个参数列表,把参数设置到PreparedStatement中
if (parameterMappings != null) {
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {//对于存储过程中的参数不处理
Object value;//绑定的实参
String propertyName = parameterMapping.getProperty();//参数的名字
if (boundSql.hasAdditionalParameter(propertyName)) { // 获取对应的实参值
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
TypeHandler typeHandler = parameterMapping.getTypeHandler();//从parameterMapping中获取typeHandler对象
JdbcType jdbcType = parameterMapping.getJdbcType();//获取参数对应的jdbcType
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {//为statment中的占位符绑定参数
typeHandler.setParameter(ps, i + 1, value, jdbcType);
}
}
}
}
}
6、StatementHandler分析
StatementHandler完成Mybatis最核心的工作,也是Executor实现的基础;功能包括:创建statement对象,为sql语句绑定参数,执行增删改查等SQL语句、将结果映射集进行转化。
6.1、BaseStatementHandler
所有子类的抽象父类,定义了初始化statement的操作顺序,由子类实现具体的实例化不同的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;
protected final Executor executor;//记录执行语句的executor对象
protected final MappedStatement mappedStatement;//sql语句对应的MappedStatement
protected final RowBounds rowBounds;//分页信息
protected BoundSql boundSql;//sql语句
6.2、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最主要的功能就是根据mappedStatment的配置,生成一个对应的StatementHandler对象并赋值给delegate
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
6.3、SimpleStatmentHandler
使用statement对象访问数据库,无须参数化;
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
String sql = boundSql.getSql();//获取sql语句
statement.execute(sql);//执行sql语句
return resultSetHandler.<E>handleResultSets(statement);//使用resultSetHandler处理查询结果
}
6.3、Mybatis默认使用PreparedStatmentHandler
使用预编译PrepareStatement对象访问数据库;
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}
6.4、CallableStatmentHandler调用存储过
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
CallableStatement cs = (CallableStatement) statement;
cs.execute();
List<E> resultList = resultSetHandler.<E>handleResultSets(cs);
resultSetHandler.handleOutputParameters(cs);
return resultList;
}
7、ResultSetHandler分析
ResultSetHandler将从数据库查询得到的结果按照映射配置文件的映射规则,映射成相应的结果集对象;
Mybatis默认使用PreparedStatementHandler执行查询
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
//用于保存结果集对象
final List<Object> multipleResults = new ArrayList<>();
int resultSetCount = 0;
//statment可能返回多个结果集对象,这里先取出第一个结果集
ResultSetWrapper rsw = getFirstResultSet(stmt);
//获取结果集对应resultMap,本质就是获取字段与java属性的映射规则
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);//结果集和resultMap不能为空,为空抛出异常
while (rsw != null && resultMapCount > resultSetCount) {
//获取当前结果集对应的resultMap
ResultMap resultMap = resultMaps.get(resultSetCount);
//根据映射规则(resultMap)对结果集进行转化,转换成目标对象以后放入multipleResults中
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);//获取下一个结果集
cleanUpAfterHandlingResultSet();//清空nestedResultObjects对象
resultSetCount++;
}
//获取多结果集。多结果集一般出现在存储过程的执行,存储过程返回多个resultset,
//mappedStatement.resultSets属性列出多个结果集的名称,用逗号分割;
//多结果集的处理不是重点,暂时不分析
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
return collapseSingleResultList(multipleResults);
}
根据映射规则(resultMap)对结果集进行转化,转换成目标对象以后放入multipleResults中:
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
try {
if (parentMapping != null) {//处理多结果集的嵌套映射
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
} else {
if (resultHandler == null) {//如果resultHandler为空,实例化一个人默认的resultHandler
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
//对ResultSet进行映射,映射结果暂存在resultHandler中
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
//将暂存在resultHandler中的映射结果,填充到multipleResults
multipleResults.add(defaultResultHandler.getResultList());
} else { //使用指定的rusultHandler进行转换
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {//调用resultset.close()关闭结果集
closeResultSet(rsw.getResultSet());
}
}
对resultMap进行映射,映射和结果缓存到defaultResultHandler
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
if (resultMap.hasNestedResultMaps()) {//处理有嵌套resultmap的情况
ensureNoRowBounds();
checkResultHandler();
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else {//处理没有嵌套resultmap的情况
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
处理没有嵌套结果集-- 简单映射处理:
//简单映射处理
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
throws SQLException {
//创建结果上下文,所谓的上下文就是专门在循环中缓存结果对象的
DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
//1.根据分页信息,定位到指定的记录
skipRows(rsw.getResultSet(), rowBounds);
//2.shouldProcessMoreRows判断是否需要映射后续的结果,实际还是翻页处理,避免超过limit
while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
//3.进一步完善resultMap信息,主要是处理鉴别器的信息
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
//4.读取resultSet中的一行记录并进行映射,转化并返回目标对象
Object rowValue = getRowValue(rsw, discriminatedResultMap);
//5.保存映射结果对象
storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
}
}
// 4.读取resultSet中的一行记录并进行映射,转化并返回目标对象
//4.读取resultSet中的一行记录并进行映射,转化并返回目标对象
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
// 4.1 根据resultMap的type属性,实例化目标对象
Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
// 4.2 对目标对象进行封装得到metaObjcect,为后续的赋值操作做好准备
final MetaObject metaObject = configuration.newMetaObject(rowValue);
boolean foundValues = this.useConstructorMappings;//取得是否使用构造函数初始化属性值
if (shouldApplyAutomaticMappings(resultMap, false)) {//是否使用自动映射
// 4.3一般情况下 autoMappingBehavior默认值为PARTIAL,对未明确指定映射规则的字段进行自动映射
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
}
// 4.4 映射resultMap中明确指定需要映射的列
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
foundValues = lazyLoader.size() > 0 || foundValues;
//4.5 如果没有一个映射成功的属性,则根据<returnInstanceForEmptyRow>的配置返回null或者结果对象
rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
}
return rowValue;
}
//4.4 映射resultMap中明确指定需要映射的列
private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
throws SQLException {
//从resultMap中获取明确需要转换的列名集合
final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
boolean foundValues = false;
//获取ResultMapping集合
final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
for (ResultMapping propertyMapping : propertyMappings) {
String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);//获得列名,注意前缀的处理
if (propertyMapping.getNestedResultMapId() != null) {
// the user added a column attribute to a nested result map, ignore it
//如果属性通过另外一个resultMap映射,则忽略
column = null;
}
if (propertyMapping.isCompositeResult()//如果是嵌套查询,column={prop1=col1,prop2=col2}
|| (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))//基本类型映射
|| propertyMapping.getResultSet() != null) {//嵌套查询的结果
//获得属性值
Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
//获得属性名称
final String property = propertyMapping.getProperty();
if (property == null) {//属性名为空跳出循环
continue;
} else if (value == DEFERED) {//属性名为DEFERED,延迟加载的处理
foundValues = true;
continue;
}
if (value != null) {
foundValues = true;
}
if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
// gcode issue #377, call setter on nulls (value is not 'found')
//通过metaObject为目标对象设置属性值
metaObject.setValue(property, value);
}
}
}
return foundValues;
}
//5.保存映射结果对象
//保存映射结果对象
private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {
if (parentMapping != null) {
//如果是嵌套结果或嵌套查询,将对象保存至父对象
linkToParents(rs, parentMapping, rowValue);
} else {
//普通映射则把对象保存至resultHandler和resultContext
callResultHandler(resultHandler, resultContext, rowValue);
}
}
private void callResultHandler(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue) {
resultContext.nextResultObject(rowValue);
((ResultHandler<Object>) resultHandler).handleResult(resultContext);
}