mybatis 工作原理
1. SqlSessionFactory 初始化
作用:加载Mybatis配置,用于生成SqlSession用于数据库操作
// 加载mybatis全局配置文件,生成SqlSessionFactory
InputStream inputStream = Resources.getResourceAsStream(factedfilePath);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
全局配置文件:SqlMapConfig.xml
1.0 解析SqlMapConfig.xml
代码:org.apache.ibatis.session.SqlSessionFactoryBuilder#build
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// 本质由XMLConfigBuilder解析生成Configuration对象,由Configuration初始化SqlSessionFactory
parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
1.1 XMLConfigBuilder解析配置,生成Configuration
Configuration: 包含mybatis所有环境配置信息:例如
- MapperRegistry: mapper接口注册信息,用于后续动态代理处理数据库逻辑
- InterceptorChain: mybatis拦截器,用于mybatis各个阶段功能加强,例如监控、分页等
- TypeHandlerRegistry: 用于ResultSetHandler解析ResultSet时的数据转换,例如数据库存储JOSN字符串,可解析成对象
- TypeAliasRegistry:类型别名注册,包含指定类的别名,ResultMap时可直接映射
- mappedStatements:包含Mapper.xml文件配置所有信息,对应CRUD的XML片段(业务执行核心)
- keyGenerators: 主键自动生成器,类似mysql auto_increment 主键自动增长功能
- resultMaps: resultMap 配置信息(数据库响应字段与实体映射)
- parameterMaps: parameterMap 配置信息(请求实体字段与数据库字段映射)
- environment: 环境信息,包含数据库相关配置,封装数据源和事务工厂
源码:org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration
private void parseConfiguration(XNode root) {
try {
//解析mybatis变量属性:对应标签:
propertiesElement(root.evalNode("properties"));
//解析settings标签,并作做字段校验,防止非法字段
Properties settings = settingsAsProperties(root.evalNode("settings"));
// 加载Vfs配置:虚拟文件系统配置数据:vfsImpl
loadCustomVfs(settings);
// 加载自定义日志实现 logImpl
loadCustomLogImpl(settings);
// 类别名解析:对应标签:案例将包com.zhiwei.entity下实力映射别名,例如类Student,别名为student,ResultMap可直接引用
//
//
//
typeAliasesElement(root.evalNode("typeAliases"));
//插件解析:用于Mybatis拦截器处理:interceptorChain
pluginElement(root.evalNode("plugins"));
// 对象工厂解析,用于创建对象、设置属性等操作(工具类)
objectFactoryElement(root.evalNode("objectFactory"));
// 对象包装器工厂,类似Spring Bean和BeanWrapper的关系,用于生成包装对象
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
// 反射器工厂:缓存类和类反射器,用于反射操作
reflectorFactoryElement(root.evalNode("reflectorFactory"));
//应用mybatis最终配置,例如cacheEnabled 默认开启二级缓存
settingsElement(settings);
//mybatis环境配置:主要封装数据库信息
environmentsElement(root.evalNode("environments"));
//databaseIdProvider:用于标识environment配置的数据源,例如Oracle、MYSQL产品名称
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
// 类型处理器:主要用于数据库查询返回后,字段与实体对应,例如数据库存储为JSON,实际实体属性为对象,这里就需类型处理器进行转换
typeHandlerElement(root.evalNode("typeHandlers"));
// mapper.xml解析: 底层实现:org.apache.ibatis.builder.xml.XMLMapperBuilder.parse
// MappedStatement: 对应CRUD的SQL片段,唯一标识:namespace+id
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
1.2 MappedStatement 生成逻辑
案例:StudentMapper.xml
select * from student where sid = #{sid}
源码:org.apache.ibatis.builder.xml.XMLMapperBuilder.configurationElement
private void configurationElement(XNode context) {
//context XNode对应标签:
try {
//获取namespace: com.zhiwei.advanced.mapper.StudentMapper
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper"s namespace cannot be empty");
}
//工具类MapperBuilderAssistant设置当前处理的Mapper文件,namespace标识
builderAssistant.setCurrentNamespace(namespace);
// 缓存引用处理,实现多个Mapper共享相同的缓存,缓存由Configuration caches维护
cacheRefElement(context.evalNode("cache-ref"));
// 缓存处理:mapper级别缓存控制,默认全局Setting(settingsElement(settings))配置cacheEnabled开启二级缓存,Configuration维护也一份缓存供其他Mapper引用:cache-ref
cacheElement(context.evalNode("cache"));
// parameterMap维护:用于映射请求实体和数据库字段(基本弃用,parameterType代替)
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
//resultMap 数据库查询结果集映射
resultMapElements(context.evalNodes("/mapper/resultMap"));
// sql解析,封装到MappedStatement sqlSource属性
sqlElement(context.evalNodes("/mapper/sql"));
// 构建MappedStatement对象(CRUD操作才被被映射成MappedStatement)
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. The XML location is "" + resource + "". Cause: " + e, e);
}
}
2. SqlSession 执行数据库操作
案例:
StudentMapper.java:
public interface StudentMapper {
public Student findStudentById(Integer sid);
}
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
studentMapper.findStudentById(id);
2.1 获取SqlSession的方式
案例:SqlSession sqlSession = sqlSessionFactory.openSession();
解析:因是Select操作,不设计数据变更,采用默认的autoCommit=true,自动提交事务,本质和java.sql.Connection.setAutoCommit(true)一样,执行1个操作就自动提交事务
底层源码:org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.openSessionFromDataSource
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// 获取mybatis环境变量,主要用于构建事务工厂,为后续数据库操作做好准备
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// 基于数据源创建事务:JdbcTransaction(Connection 数据库连接、dataSource 数据源、level 数据库隔离级别、autoCommit 是否自动提交事务)
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 执行器: 所有数据库操作底层都是交给Executor负责调度
final Executor executor = configuration.newExecutor(tx, execType);
// 创建SqlSession,包含mybatis配置、数据库操作执行器、事务自动提交标识
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
2.2 executor创建逻辑
源码:org.apache.ibatis.session.Configuration.newExecutor
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
//executorType: 执行器类型,默认Simple,可自定义:org.apache.ibatis.session.Configuration.defaultExecutorType
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
// 默认开启二级缓存,自动创建CachingExecutor,包装SimpleExecutor,二级缓存存储在TransactionalCacheManager,
// 注意开启Mapper二级缓存需要定义cache标签或者引用别的Mapper缓存:cache-ref
// 二级缓存开启步骤:
// 1. 全局开启: 默认开启 (configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));)
// 2. mapper.xml cache 开启
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
3. Mapper 生成逻辑(JDK 动态代理)
案例代码:StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
MapperRegistry:映射器注册表,主要维护Mapper接口和代理对象工厂,用于生成Mapper代理对象,用以实际的数据库业务处理
底层源码:org.apache.ibatis.binding.MapperRegistry.getMapper
public T getMapper(Class type, SqlSession sqlSession) {
final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
// 映射器动态代理工厂生成mapper接口动态代理对象
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
3.1 mapperProxyFactory生成Mapper动态代理对象
public T newInstance(SqlSession sqlSession) {
final MapperProxy mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy mapperProxy) {
// JDK1.8 动态代理对象生成
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
3.2 MapperProxy 动态代理对象
MapperProxy实现动态代理接口:InvocationHandler
源码:org.apache.ibatis.binding.MapperProxy.invoke
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
//缓存method封装类:MapperMethod
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
//缓存MapperMethod:包含Mapper接口、methon、mybatis环境配置
private MapperMethod cachedMapperMethod(Method method) {
return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
4. Mapper执行业务逻辑
案例代码:studentMapper.findStudentById(id);
分析:底层逻辑交由JDK动态代理对象执行,本质执行位置:org.apache.ibatis.binding.MapperProxy.invoke
代码:mapperMethod.execute(sqlSession, args);
org.apache.ibatis.binding.MapperMethod.execute
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
// sql insert 操作,对应 mapper.xml insert标签
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
// sql update操作:对应mapper.xml update标签
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
// sql delete 操作,对应mapper.xml delete标签
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
// sql select 操作,对应mapper.xml select标签
case SELECT:
// 根据返回值进行判断,进行对象的操作
// 没有返回值,直接返回NULL
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
// 返回值为Collection或Array
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
//返回值为Map
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
// 返回值为游标
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
// 其他默认处理: 案例属于这一种
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional() &&
(result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
// flush 操作: Mapper方法注解:@Flush,刷新Statement,释放数据库资源
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method "" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
4.1 sqlSession执行数据库逻辑
案例代码: result = sqlSession.selectOne(command.getName(), param);
源码:org.apache.ibatis.session.defaults.DefaultSqlSession.selectList
- RowBounds: mybatis 查询默认分页逻辑:0 ~ Integer.MAX_VALUE(2147483648, int存在符号位,只有31位标识数值:2^31-1)
@Override
public List selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//从configuration缓存获取MappedStatement: statement(sql唯一标识): com.zhiwei.advanced.mapper.StudentMapper.findStudentById
MappedStatement ms = configuration.getMappedStatement(statement);
// 底层实际逻辑交给executor执行器处理,这里executor是CachingExecutor,因为默认开启二级缓存,sqlSession创建时自动创建,里面包装SimpleExecutor对象(装饰器模式),补充缓存功能
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();
}
}
//请求参数封装类:如果请求参数是collection或array,则将参数存放到Map返回,若为原生对象则直接返回
private Object wrapCollection(final Object object) {
if (object instanceof Collection) {
StrictMap
4.2 CachingExecutor query代码(mybatis二级缓存)
源码:org.apache.ibatis.executor.CachingExecutor.query
@Override
public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
//MappedStatement获取二级缓存:通过Configuration.cacheEnabled + cache/cache-ref 标签开启,注意缓存真实数据保存在MappedStatement自身,
// MappedStatement维护在Configuration内部从而实现相同Mapper不同session的缓存共享(二级缓存)
// cache-ref: 引用其他MappedStatement缓存,可理解为跨Mapper级别的二级缓存
Cache cache = ms.getCache();
if (cache != null) {
//MappedStatement指定flushCache=true,则强制清理缓存,相当于每次访问都清空缓存
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
// sqlSession内部事务缓存获取,若存则直接返回,否则交给委托Executor执行:SimpleExecutor,并将结果存入二级缓存(相同Session)
List list = (List) tcm.getObject(cache, key);
if (list == null) {
// 缓存不存在直接委托给SimpleExecutor执行
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
// 全局开启二级缓存,但是Mapper.xml没有开启二级缓存则默认二级缓存无效,直接委托给SimpleExecutor执行
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
4.3 SimpleExecutor执行逻辑(mybatis一级缓存:localCache)
源码:org.apache.ibatis.executor.BaseExecutor.query
@Override
public List 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) {
throw new ExecutorException("Executor was closed.");
}
//第一次查询或所有查询操作都完成并且mappedStatement flushCache配置true,强制清空本地缓存(一级缓存Session级别)
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List list;
try {
queryStack++;
// mybatis一级缓存获取结果集,存在则直接缓存,否则执行对应的查询逻辑
list = resultHandler == null ? (List) 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();
}
// issue #601
deferredLoads.clear();
//一级缓存细分的缓存作用域:默认 Configuration.localCacheScope(Session),可自定义为STATEMENT缓存
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
4.4 数据库业务底层处理
接口:org.apache.ibatis.executor.BaseExecutor.queryFromDatabase
private List queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List list;
// 一级缓存数据更新,存放正在处理的标志EXECUTION_PLACEHOLDER, 标识缓存正在处理,缓存对外不能提供服务,不能加载
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
// 数据库底层操作
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;
}
4.5 数据库数据查询
接口:org.apache.ibatis.executor.SimpleExecutor.doQuery
@Override
public List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
// 构造 StatementHandler, 主要用于生成JDBC的PrepreStatement(预编译解决SQL注入问题)
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
4.5.1 StatementHandler 构造逻辑
接口:org.apache.ibatis.session.Configuration.newStatementHandler
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//构造RoutingStatementHandler:本质是StatementHandler的装饰器,根据statementType构造不同的StatementHandler,案例 statementType为PREPARED, 对应PreparedStatementHandler
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
//mybatis 拦截器处理(责任链模式):StatementHandler
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
4.5.2 StatementHandler构造Statement(JDBC规范)
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
//transaction获取数据库连接:transaction(JdbcTransaction)
Connection connection = getConnection(statementLog);
// 获取 Statement
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
org.apache.ibatis.executor.statement.BaseStatementHandler.prepare:
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
// 初始化Statement
statement = instantiateStatement(connection);
// 设置查询和事务超时事件
setStatementTimeout(statement, transactionTimeout);
// 设置拉取数据量
setFetchSize(statement);
return statement;
} catch (SQLException e) {
closeStatement(statement);
throw e;
} catch (Exception e) {
closeStatement(statement);
throw new ExecutorException("Error preparing statement. Cause: " + e, e);
}
}
org.apache.ibatis.executor.statement.PreparedStatementHandler.instantiateStatement
// Statement初始化,本质遵循JDBC操作规范
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
//如果设置主键自动生成策略:Jdbc3KeyGenerator
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
String[] keyColumnNames = mappedStatement.getKeyColumns();
if (keyColumnNames == null) {
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
} else {
return connection.prepareStatement(sql, keyColumnNames);
}
//结果集类型: 默认类型
} else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
return connection.prepareStatement(sql);
//其他类型
} else {
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
}
}
4.5.3 Statement参数化处理
案例:handler.parameterize(stmt);
底层源码:org.apache.ibatis.scripting.defaults.DefaultParameterHandler.setParameters
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
// 解析请求参数映射
List parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
//请求参数映射模式,默认为ParameterMode.IN
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value; // 具体的请求参数的值
String propertyName = parameterMapping.getProperty();
//boundSql若boundSql有相同附加参数,直接赋值
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
value = boundSql.getAdditionalParameter(propertyName);
//请求参数对象为空则直接返回null
} else if (parameterObject == null) {
value = null;
// 类型处理器是否包含指定请求参数类型,如果包含直接返回,原生支持(这里SID为Integer类型,直接支持)
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
// 其他类型:直接从请求参数对象通过反射机制获取值
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
// 获取请求参数映射的请求处理器
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
// JDBC规范:prepareStatement参数赋值,类似如 select *from student where sid = ?, "?"为复制的地方,赋值完成后等价于sql=select *from student where sid = "xxx"
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
} catch (SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
4.5.4 执行SQL查询
案例代码:handler.query(stmt, resultHandler);
源码:org.apache.ibatis.executor.statement.PreparedStatementHandler.query
@Override
public List query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
//执行prepareStatment
ps.execute();
//resultSetHandler处理查询操作后的结果集
return resultSetHandler.handleResultSets(ps);
}
4.5.5 SQL查询结果集处理resultSetHandler
接口:org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleResultSets
@Override
public List handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List multipleResults = new ArrayList<>();
int resultSetCount = 0;
//构建ResultSetWrapper(构造函数), 本质获取数据库表字段元数据,方便后续结果集封装
ResultSetWrapper rsw = getFirstResultSet(stmt);
// 获取ResultMap
List resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
// 数据库记录解析:ResultSetHandler解析解析为实体对象,对象保存在multipleResults集合
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
// 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);
}
.......................... mybatis 整个工作流程梳理完成 ..............................