前言
MyBatis主要是在做两件事,一件是解析xml文件,一件就是执行SQL了。在上篇我们已经解析了mybatis-config.xml配置文件和Mapper.xml映射文件,本篇我们看是如何执行一条SQL的。
SQL执行
看MyBatis如何调用一个方法来执行SQL的
创建SqlSession
创建一个会话
SqlSession sqlSession = sqlSessionFactory.openSession()
在MyBatis的单元测试AutoConstructorTest.fullyPopulatedSubject()中 从sqlSessionFactory创建一个SqlSession对象,默认是使用DefaultSqlSession。创建的同时,我们从Configuration这个对象中获取Executor执行类型,默认SIMPLE。
// DefaultSqlSessionFactory 类
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// 环境数据源配置
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 创建一个简单执行器
final Executor executor = configuration.newExecutor(tx, execType);
// 创建一个默认的SqlSession
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
// 清空该线程上下文信息
ErrorContext.instance().reset();
}
}
获取Mapper对象
// 获取Mapper对象
AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);
AutoConstructorMapper是一个接口不能直接调用方法,MyBatis利用JDK的动态代理为我们创建了一个对象
/**
* MapperRegistry 类
* @param type 类名,例如:org.apache.ibatis.autoconstructor.AutoConstructorMapper
* @param sqlSession
* @return 返回type的一个对象
*/
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// knownMappers 是个Map,key是类名,value是MapperProxyFactory代理工厂
// 在解析配置文件的时候会添加进去的
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
// 通过mapperProxy new一个对象
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
// MapperProxyFactory 类
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
执行SQL
通过JDK动态代理创建了一个代理对象,这时候我们调用findById()方法,会执行MapperProxy类中的invoke()方法,因为该类实现了InvocationHandler接口
/**
*
* @param proxy
* @param method 执行的方法,例如:findById(int)
* @param args 该方法的入参,例如:1
* @return
* @throws Throwable
*/
@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 (method.isDefault()) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
// 去执行sql
return mapperMethod.execute(sqlSession, args);
}
来到MapperMethod类中的execute方法
public Object execute(SqlSession sqlSession, Object[] args) {
case INSERT: {
// insert 代码逻辑
}
case UPDATE: {
// update 代码逻辑
}
case DELETE: {
// delete 代码逻辑
}
case SELECT: {
// 省略其它一些判断
// 参数
Object param = method.convertArgsToSqlCommandParam(args);
// command.getName()其实就是完整的方法名称,例如:
// org.apache.ibatis.autoconstructor.AutoConstructorMapper.findById
// 执行DefaultSqlSession类下的selectOne()方法
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional()
&& (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
}
通过DefaultSqlSession类中的多个构造方法,最终来到该方法查询
// DefaultSqlSession 类
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
// MappedStatement对象有要执行的sql,例如:SELECT * FROM subject WHERE id = ?
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();
}
}
statement包下的*StatementHandler类进行和JDBC的操作
// PreparedStatementHandler 类
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
// 这里就是我们的JDBC PreparedStatement对象了
PreparedStatement ps = (PreparedStatement) statement;
// 执行sql
ps.execute();
// ResultSetHandler接口 对查询结果集进行封装处理
return resultSetHandler.handleResultSets(ps);
}
查询结果集处理
ResultSetHandler的实现类DefaultResultSetHandler进行结果及处理,具体处理在这个核心方法
@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;
// 获取第一个结果集
// 在这里我们查询的一个对象,ResultSetWrapper有该对象的属性,属性类型,对应的JDBC类型
// 还有对应数据库查询结果
ResultSetWrapper rsw = getFirstResultSet(stmt);
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
// 数据库字段和Java类属性之间的映射处理
// 同时将这个对象添加到multipleResults集合中
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
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++;
}
}
// 调用#1,如果集合只有一个元素,就取下标为0的元素返回,否则就返回所有
return collapseSingleResultList(multipleResults);
}
// #1
private List<Object> collapseSingleResultList(List<Object> multipleResults) {
return multipleResults.size() == 1
? (List<Object>) multipleResults.get(0)
: multipleResults;
}
小结
本篇我们对mapper调用方法到执行具体的SQL,很粗略的走了一遍,会经过MapperProxy,DefaultSqlSession,MappedStatement,Executor,ResultHandler这几个重要的类。了解了MyBatis是如何通过调用方法来执行SQL的,其中具体的处理细节感兴趣,可以一步步Debug,多Debug几遍,一探究竟!
请你相信我所说的都是错的