MyBatis 源码分析-- SQL请求执行流程( Mapper 接口方法的执行的过程)

前言

前面我们从源码层面梳理了 SqlSessionFactory、SqlSession 的创建过程及 Mapper 获取过程,本篇我们继续分析一下 Mapper 接口方法的执行的过程,也就是 SQL 的执行流程。

Mybatis 相关知识传送门

初识 MyBatis 【MyBatis 核心概念】

MyBatis 源码分析–SqlSessionFactory

MyBatis 源码分析–获取SqlSession

MyBatis 源码分析-- getMapper(获取Mapper)

SQL 请求执行流程

分析 SQL 执行流程之前,先附上一张简单的源码调用链路图,方便后面分析 SQL 的执行流程。

在这里插入图片描述

执行 Mapper 方法

前面分析了,从 SqlSession 中获取的 Mapper 是代理对象,执行 Mapper 接口方法时候,会调用 MapperProxy#invoke 方法。

MapperProxy#invoke 方法源码分析

MapperProxy#invoke 方法会判断执行的方法是否是 Object 类的方法,如果是就无需代理,否则才会走代理的方法,代理的方法有两个默认实现 一个是普通方法 PlainMethodInvoker 和默认方法实现 DefaultMethodInvoker。

//org.apache.ibatis.binding.MapperProxy#invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
	try {
		//判断是不是 Object 类的方法  是就直接返回 无需代理  不是则调用缓存的 invoke 方法 有两个实现类 一个是普通方法 PlainMethodInvoker  和默认方法实现 DefaultMethodInvoker
		return Object.class.equals(method.getDeclaringClass()) ? method.invoke(this, args) : this.cachedInvoker(method).invoke(proxy, method, args, this.sqlSession);
	} catch (Throwable var5) {
		throw ExceptionUtil.unwrapThrowable(var5);
	}
}

MapperProxy#cachedInvoker 方法源码分析

MapperProxy#cachedInvoker 方法会先从缓存中获取 MapperMethodInvoker,如果缓存不存在则穿件一个 MapperMethodInvoker 再加入缓存,创建 MapperMethodInvoker 时候会判断是普通方法还是默认方法。

//org.apache.ibatis.binding.MapperProxy#cachedInvoker
private MapperProxy.MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
	try {
		//获取 MapperMethodInvoker
		MapperProxy.MapperMethodInvoker invoker = (MapperProxy.MapperMethodInvoker)this.methodCache.get(method);
		//缓存中是否存在 不存在 则加入缓存
		return invoker != null ? invoker : (MapperProxy.MapperMethodInvoker)this.methodCache.computeIfAbsent(method, (m) -> {
			//是否是默认的
			if (m.isDefault()) {
				try {
					//默认的方法
					return privateLookupInMethod == null ? new MapperProxy.DefaultMethodInvoker(this.getMethodHandleJava8(method)) : new MapperProxy.DefaultMethodInvoker(this.getMethodHandleJava9(method));
				} catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException var4) {
					throw new RuntimeException(var4);
				}
			} else {
				//普通方法 new MapperMethod 重点关注
				return new MapperProxy.PlainMethodInvoker(new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration()));
			}
		});
	} catch (RuntimeException var4) {
		Throwable cause = var4.getCause();
		throw (Throwable)(cause == null ? var4 : cause);
	}
}

MapperMethod 创建源码分析

MapperMethod 的创建中做了两个操作,一个是创建了 SqlCommand,还有一个是创建了MethodSignature。

//org.apache.ibatis.binding.MapperMethod#MapperMethod
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
	//创建 SqlCommand
	this.command = new MapperMethod.SqlCommand(config, mapperInterface, method);
	//创建 MethodSignature
	this.method = new MapperMethod.MethodSignature(config, mapperInterface, method);
}

SqlCommand 创建源码分析

SqlCommand 的创建其实是从 MappedStatement 中获取 name、SqlCommandType 完成赋值,从这里我们可以看出 MappedStatement 中存储的是 SQL 信息,例如:id、SELECT、UPDATE、DELETE 等 。



//org.apache.ibatis.binding.MapperMethod.SqlCommand#SqlCommand
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
	//获取方法名称
	String methodName = method.getName();
	//获取方法的 class
	Class<?> declaringClass = method.getDeclaringClass();
	//得到 MappedStatement
	MappedStatement ms = this.resolveMappedStatement(mapperInterface, methodName, declaringClass, configuration);
	//MappedStatement 为空判断
	if (ms == null) {
		//是否是 Flush 注解标记的接口
		if (method.getAnnotation(Flush.class) == null) {
			//没有找到对应的 statement
			throw new BindingException("Invalid bound statement (not found): " + mapperInterface.getName() + "." + methodName);
		}

		this.name = null;
		//SqlCommandType FLUSH
		this.type = SqlCommandType.FLUSH;
	} else {
		//有 MappedStatement 
		this.name = ms.getId();
		this.type = ms.getSqlCommandType();
		//SqlCommandType.UNKNOWN 判断
		if (this.type == SqlCommandType.UNKNOWN) {
			throw new BindingException("Unknown execution method for: " + this.name);
		}
	}

}


//org.apache.ibatis.binding.MapperMethod.SqlCommand#resolveMappedStatement
private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName, Class<?> declaringClass, Configuration configuration) {
	//接口类名 + . 方法名
	String statementId = mapperInterface.getName() + "." + methodName;
	//在 mappedStatements 中是否存在 解析xml 时候存入的
	if (configuration.hasStatement(statementId)) {
		//找到返回
		return configuration.getMappedStatement(statementId);
	} else if (mapperInterface.equals(declaringClass)) {
		return null;
	} else {
		Class[] var6 = mapperInterface.getInterfaces();
		int var7 = var6.length;
		//查询父接口
		for(int var8 = 0; var8 < var7; ++var8) {
			Class<?> superInterface = var6[var8];
			if (declaringClass.isAssignableFrom(superInterface)) {
				MappedStatement ms = this.resolveMappedStatement(superInterface, methodName, declaringClass, configuration);
				if (ms != null) {
					return ms;
				}
			}
		}

		return null;
	}
}

SqlCommandType

public enum SqlCommandType {
    UNKNOWN,
    INSERT,
    UPDATE,
    DELETE,
    SELECT,
    FLUSH;

    private SqlCommandType() {
    }
}

MethodSignature#MethodSignature 源码分析

MethodSignature#MethodSignature 方法构造了方法签名,完成 MethodSignature 的初始化,方法签名初始化完成之后,MapperMethod 实例就创建完成了,回到了 MapperProxy 的 invoke 方法,准备开始执行 execute 方法。

//org.apache.ibatis.binding.MapperMethod.MethodSignature#MethodSignature
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();
	}
	//返回void
	this.returnsVoid = Void.TYPE.equals(this.returnType);
	//返回多条
	this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
	//返回游标
	this.returnsCursor = Cursor.class.equals(this.returnType);
	//是否返回Optional
	this.returnsOptional = Optional.class.equals(this.returnType);
	//mapKey的值
	this.mapKey = this.getMapKey(method);
	//是否map查询
	this.returnsMap = this.mapKey != null;
	//RowBounds 索引  RowBounds 是分页用的 记录 RowBounds 是第几个参数 
	this.rowBoundsIndex = this.getUniqueParamIndex(method, RowBounds.class);
	//ResultHandler 索引 resultHandler 是结果处理器 ResultHandler 是第几个参数
	this.resultHandlerIndex = this.getUniqueParamIndex(method, ResultHandler.class);
	//参数名称解析器
	this.paramNameResolver = new ParamNameResolver(configuration, method);
}

MapperProxy.PlainMethodInvoker 源码分析

前面我们分析了 MapperProxy.PlainMethodInvoker 包装了 MapperMethod,PlainMethodInvoker 的 invoke 方法最终会调用 MapperMethod 的 invoke 方法,我们接着分析 MapperMethod#execute 方法。

private static class PlainMethodInvoker implements MapperProxy.MapperMethodInvoker {
	private final MapperMethod mapperMethod;

	public PlainMethodInvoker(MapperMethod mapperMethod) {
		this.mapperMethod = mapperMethod;
	}

	public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
		//调用 MapperMethod#execute 方法
		return this.mapperMethod.execute(sqlSession, args);
	}
}

MapperMethod#execute 源码分析

MapperMethod#execute 方法会根据不用的 Type 和不同的返回值类型,调用不同的方法执行 SQL。

//org.apache.ibatis.binding.org.apache.ibatis.binding.MapperMethod#execute
public Object execute(SqlSession sqlSession, Object[] args) {
	//返回结果
	Object result;
	//参数
	Object param;
	//sql 语句类型 insert update delete select
	switch(this.command.getType()) {
	case INSERT:
		//解析参数
		param = this.method.convertArgsToSqlCommandParam(args);
		//rowCountResult 包装返回结果 sqlSession.insert 调用的是 DefauleSqlSession 的inset 方法 最终调用的的是 executor 的 update方法
		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()) {
			//没有返回值
			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 {
			//返回值为 单一对象的方法
			//将方法参数转换为 SQL 参数
			param = this.method.convertArgsToSqlCommandParam(args);
			//selectOne 语句的执行入口
			result = sqlSession.selectOne(this.command.getName(), param);
			if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) {
				result = Optional.ofNullable(result);
			}
		}
		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;
	}
}

//org.apache.ibatis.session.defaults.DefaultSqlSession#insert(java.lang.String, java.lang.Object)
public int insert(String statement, Object parameter) {
	return this.update(statement, parameter);
}


//org.apache.ibatis.session.defaults.DefaultSqlSession#update(java.lang.String, java.lang.Object)
public int update(String statement, Object parameter) {
	int var4;
	try {
		this.dirty = true;
		MappedStatement ms = this.configuration.getMappedStatement(statement);
		var4 = this.executor.update(ms, this.wrapCollection(parameter));
	} catch (Exception var8) {
		throw ExceptionFactory.wrapException("Error updating database.  Cause: " + var8, var8);
	} finally {
		ErrorContext.instance().reset();
	}

	return var4;
}

MethodSignature#convertArgsToSqlCommandParam 源码分析

MethodSignature#convertArgsToSqlCommandParam 方法主要是对参数进行解析。

//org.apache.ibatis.binding.MapperMethod.MethodSignature#convertArgsToSqlCommandParam
public Object convertArgsToSqlCommandParam(Object[] args) {
	return this.paramNameResolver.getNamedParams(args);
}


//org.apache.ibatis.reflection.ParamNameResolver#getNamedParams
public Object getNamedParams(Object[] args) {
	int paramCount = this.names.size();
	if (args != null && paramCount != 0) {
		if (!this.hasParamAnnotation && paramCount == 1) {
			//一个参数
			Object value = args[(Integer)this.names.firstKey()];
			return wrapToMapIfCollection(value, this.useActualParamName ? (String)this.names.get(0) : null);
		} else {
			//多个参数
			Map<String, Object> param = new ParamMap();
			int i = 0;
			//遍历加入 Map
			for(Iterator var5 = this.names.entrySet().iterator(); var5.hasNext(); ++i) {
				Entry<Integer, String> entry = (Entry)var5.next();
				param.put(entry.getValue(), args[(Integer)entry.getKey()]);
				//通用参数名
				String genericParamName = "param" + (i + 1);
				if (!this.names.containsValue(genericParamName)) {
					//不含有通用参数名才添加
					param.put(genericParamName, args[(Integer)entry.getKey()]);
				}
			}

			return param;
		}
	} else {
		//没有参数
		return null;
	}
}

//处理集合参数
//org.apache.ibatis.reflection.ParamNameResolver#wrapToMapIfCollection
public static Object wrapToMapIfCollection(Object object, String actualParamName) {
	ParamMap map;
	//是否是集合类型
	if (object instanceof Collection) {
		map = new ParamMap();
		map.put("collection", object);
		if (object instanceof List) {
			map.put("list", object);
		}

		Optional.ofNullable(actualParamName).ifPresent((name) -> {
			map.put(name, object);
		});
		return map;
	} else if (object != null && object.getClass().isArray()) {
		//是否 array 参数
		map = new ParamMap();
		map.put("array", object);
		Optional.ofNullable(actualParamName).ifPresent((name) -> {
			map.put(name, object);
		});
		return map;
	} else {
		//非集合数组参数直接返回
		return object;
	}
}

DefaultSqlSession#selectOne 源码分析

DefaultSqlSession#selectOne 方法实际调用的也是 selectList 方法。

//org.apache.ibatis.session.defaults.DefaultSqlSession#selectOne(java.lang.String, java.lang.Object)
public <T> T selectOne(String statement, Object parameter) {
	//调用的也是 selectList
	List<T> list = this.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;
	}
}

DefaultSqlSession#selectList 源码分析

DefaultSqlSession#selectList 方法调用了执行器 Executor 执行 SQL 语句。

//org.apache.ibatis.session.defaults.DefaultSqlSession#selectList(java.lang.String, java.lang.Object)
public <E> List<E> selectList(String statement, Object parameter) {
	//返回结果的最大值 int 默认值 2147483647
	return this.selectList(statement, parameter, RowBounds.DEFAULT);
}


//DefaultSqlSession#selectList 方法调用了执行器 Executor 执行 SQL 语句。
//org.apache.ibatis.session.defaults.DefaultSqlSession#selectList(java.lang.String, java.lang.Object, org.apache.ibatis.session.RowBounds)
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
	List var6;
	try {
		//获取 MappedStatement MappedStatement 中有已经处理的 sql 信息
		MappedStatement ms = this.configuration.getMappedStatement(statement);
		//最终调用执行器的 query  RowBounds 是用来逻辑分页  按照条件将数据从数据库查询到内存中  在内存中进行分页 wrapCollection 处理集合或者数组参数
		List<E> result = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
		var6 = result;
	} catch (Exception var10) {
		throw ExceptionFactory.wrapException("Error querying database.  Cause: " + var10, var10);
	} finally {
		ErrorContext.instance().reset();
	}

	return var6;
}

CachingExecutor#query 源码分析

CachingExecutor#query 方法会从 MappedStatement 中获取 SQL 信息,创建缓存 key 并执行查询,Configuration 中 cacheEnabled 属性值默认为 true,因此会执行 CachingExecutor 的 query方法。

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
	//获取 SQL 基本信息
	BoundSql boundSql = ms.getBoundSql(parameterObject);
	//创建缓存 key
	CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql);
	//执行查询
	return this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

CachingExecutor#query 源码分析

CachingExecutor#query 会判断是否使用了缓存,如果允许使用缓存会先从二级缓存查询,二级缓存中查询不到才会去查询一级缓存或者数据库,如果二级缓存为空或者不允许使用缓存就会直接去查询一级缓存或者数据库。

//org.apache.ibatis.executor.CachingExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.cache.CacheKey, org.apache.ibatis.mapping.BoundSql)
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
	//获取 二级缓存
	Cache cache = ms.getCache();
	//为空判断
	if (cache != null) {
		//是否刷新缓存
		//select 时候 flushCacheRequired 默认是false 不清除二级缓存 
		//insert update delete 时候 flushCacheRequired 是 true 会清除二级缓存
		this.flushCacheIfRequired(ms);
		//如果使用了缓存  ResultHandler 不为空 
		if (ms.isUseCache() && resultHandler == null) {
			//对存储过程的处理 确保没有存储过程 如果有存储过程 就报错
			this.ensureNoOutParams(ms, parameterObject, boundSql);
			//从二级缓存中查询数据
			List<E> list = (List)this.tcm.getObject(cache, key);
			//结果为空 判断
			if (list == null) {
				//为空 BaseExexutor 执行查询数据库
				list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
				//加入二级缓存
				this.tcm.putObject(cache, key, list);
			}

			return list;
		}
	}
	//缓存为空 BaseExexutor 直接查询数据库
	return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

BaseExecutor#query 源码分析

BaseExecutor#query 方法会先去一级缓存查询数据,一级缓存中查询不到数据才会去查询数据库的数据,同时也会做一些清除一级缓存的判断。

//org.apache.ibatis.executor.BaseExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.cache.CacheKey, org.apache.ibatis.mapping.BoundSql)
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 (this.closed) {
		throw new ExecutorException("Executor was closed.");
	} else {
		//queryStack 查询堆栈 防止递归查询重复处理缓存  是否刷新缓存 flushCacheRequired 是 true 清除一级缓存
		if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
			//清除一级缓存
			this.clearLocalCache();
		}

		List list;
		try {
			//查询堆栈 +1
			++this.queryStack;
			//从一级缓存中获取数据
			list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
			if (list != null) {
				//从一级缓存中获取数据
				this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
			} else {
				//从数据库查询数据
				list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
			}
		} finally {
			//查询堆栈-1
			--this.queryStack;
		}

		if (this.queryStack == 0) {
			Iterator i$ = this.deferredLoads.iterator();

			while(i$.hasNext()) {
				BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)i$.next();
				deferredLoad.load();
			}

			this.deferredLoads.clear();
			if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
				this.clearLocalCache();
			}
		}

		return list;
	}
}

BaseExecutor#queryFromDatabase 源码分析

BaseExecutor#queryFromDatabase 方法会去查询数据库,并将查询结果加入一级缓存。

//org.apache.ibatis.executor.BaseExecutor#queryFromDatabase
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
	//一级缓存 存储一个占位符
	this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);

	List list;
	try {
		//执行数据库查询
		list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
	} finally {
		//清除占位符缓存
		this.localCache.removeObject(key);
	}
	//存入一级缓存
	this.localCache.putObject(key, list);
	if (ms.getStatementType() == StatementType.CALLABLE) {
		this.localOutputParameterCache.putObject(key, parameter);
	}

	return list;
}

SimpleExecutor#doQuery 源码分析

SimpleExecutor#doQuery 方法通过 MappedStatement(SQL语句信息,包含 SQL、参数、返回值等)获取 Configuration,创建 StatementHandler(封装 JDBC 操作)。

//org.apache.ibatis.executor.SimpleExecutor#doQuery
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);
		//获取 Statement
		stmt = this.prepareStatement(handler, ms.getStatementLog());
		//执行查询
		var9 = handler.query(stmt, resultHandler);
	} finally {
		//关闭 statement
		this.closeStatement(stmt);
	}

	return var9;
}

SimpleExecutor#prepareStatement 源码分析

SimpleExecutor#prepareStatement 方法获取了数据库连接,创建 Statement,通过 StatementHandler 处理相关参数,最终返回 Statement。

//org.apache.ibatis.executor.SimpleExecutor#prepareStatement
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
	//获取数据库连接 会从事务管理器中获取连接
	Connection connection = this.getConnection(statementLog);
	//创建 Statement PreparedStatement
	Statement stmt = handler.prepare(connection);
	//通过 ParameterHandler 设置参数
	handler.parameterize(stmt);
	return stmt;
}

PreparedStatementHandler#query 源码分析

PreparedStatementHandler#query 方法通过 PreparedStatement 执行查询,ResultHandler 获取结果集,至此,一个简单的 SQL 查询结束了。

//org.apache.ibatis.executor.statement.PreparedStatementHandler#query
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
	PreparedStatement ps = (PreparedStatement)statement;
	//执行
	ps.execute();
	//ResultHandler 获取结果集
	return this.resultSetHandler.handleResultSets(ps);
}

至此,selectOne 的执行过程分析结束,其他查询的执行过程大同小异。

欢迎提出建议及对错误的地方指出纠正。

  • 29
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值