前言
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);// 创建一个默认的SqlSessionreturn 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*/@Overridepublic 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);// 去执行sqlreturn 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 类@Overridepublic <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 类@Overridepublic <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {// 这里就是我们的JDBC PreparedStatement对象了PreparedStatement ps = (PreparedStatement) statement;// 执行sqlps.execute();// ResultSetHandler接口 对查询结果集进行封装处理return resultSetHandler.handleResultSets(ps);}
查询结果集处理
ResultSetHandler的实现类DefaultResultSetHandler进行结果及处理,具体处理在这个核心方法
@Overridepublic 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);}// #1private 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几遍,一探究竟!
请你相信我所说的都是错的
