Mybatis有两种方式调用Mapper接口:
private static SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader); // 第一种 try (SqlSession session = sqlMapper.openSession(TransactionIsolationLevel.SERIALIZABLE)) { Blog blog = session.selectOne("org.apache.ibatis.domain.blog.mappers.BlogMapper.selectBlogWithPostsUsingSubSelect", 1); } // 第二种 try (SqlSession session = sqlMapper.openSession()) { AuthorMapper mapper = session.getMapper(AuthorMapper.class); Author author = mapper.selectAuthor(101); }
从上面代码可以看到无论是哪一种首先都要创建 SqlSessionFactory 对象,然后通过这个对象拿到 SqlSession 对象。在早期版本中只能通过该对象的增删改调用Mapper接口,很明显这种方式可读性很差,难以维护,写起来也复杂,所以后面谷歌开始维护Mybatis后,重新封装提供了第二种方式直接调用Mapper接口。不过本质上第二种是在第一种的基础之上实现的,所以下面就以第二种为主进行分析,进入到 getMapper 方法:
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); } 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); } }
mapperRegistry 对象在上一篇分析过,是在解析xml中的 mapper 节点时注册进去的,而这个对象中缓存了 Mapper 接口和对应的 代理工厂 的映射,所以 getMapper 的核心就是通过这个工厂去创建代理对象:
public T newInstance(SqlSession sqlSession) { //每次调用都会创建新的MapperProxy对象 final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); }
然后通过Mapper接口调用时首先就会调用到 MapperProxy 的 invoke 方法:
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); } //从缓存中获取mapperMethod对象,如果缓存中没有,则创建一个,并添加到缓存中 final MapperMethod mapperMethod = cachedMapperMethod(method); //调用execute方法执行sql return mapperMethod.execute(sqlSession, args); } private MapperMethod cachedMapperMethod(Method method) { return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration())); }
首先从缓存中拿到 MapperMethod 对象,这个对象封装了SQL语句的类型、命名空间、入参、返回类型等信息,然后通过它的 execute 方法调用 SqlSession 的增删查改方法:
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); 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()); } 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; }
上文说过 SqlSession 本质上是 门面模式 的体现,其本质上是通过 Executor 执行器组件实现的,在该组件中定义了所有访问数据库的方法:
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 对象是在获取 SqlSession 时创建的:
public SqlSession openSession() { return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false); } private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { //获取mybatis配置文件中的environment对象 final Environment environment = configuration.getEnvironment(); //从environment获取transactionFactory对象 final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); //创建事务对象 tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); //根据配置创建executor final Executor executor = configuration.newExecutor(tx, execType); //创建DefaultSqlSession 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(); } }
TransactionFactory 是我们在xml中配置的 transactionManager 属性,可选的属性有JDBC和Managed,然后根据我们的配置创建事务对象,之后才是创建 Executor 对象。
public Executor newExecutor(Transaction transaction, ExecutorType executorType) { 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); } //如果有<cache>节点,通过装饰器,添加二级缓存的能力 if (cacheEnabled) { executor = new CachingExecutor(executor); } //通过interceptorChain遍历所有的插件为executor增强,添加插件的功能 executor = (Executor) interceptorChain.pluginAll(executor); return executor; }
Executor 有三个基本的实现类:
这三个执行器都继承了自抽象的 BaseExecutor ,同时如果开启了二级缓存功能,在这里还会装饰一个 CachingExecutor 为其添加二级缓存的能力。另外还要注意在这段代码的最后还有拦截器进行了包装,也就是扩展插件的实现 ,关于这部分内容在一篇进行分析。
二级缓存的代码很简单,这里直接略过,所以直接进入到 BaseExecutor.query 方法:
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); } 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."); } if (queryStack == 0 && ms.isFlushCacheRequired()) {//非嵌套查询,并且FlushCache配置为true,则需要清空一级缓存 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(); } // issue #601 deferredLoads.clear(); if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {//如果当前sql的一级缓存配置为STATEMENT,查询完既清空一集缓存 // issue #482 clearLocalCache(); } } return list; }
首先从一级缓存 localCache 里面拿,如果没有,才真正地访问数据库,并将返回结果存入到一级缓存中。
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 是子类实现的,即 模板模式 ,以 SimpleExecutor 为例:
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对象 //创建StatementHandler对象, StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); //StatementHandler对象创建stmt,并使用parameterHandler对占位符进行处理 stmt = prepareStatement(handler, ms.getStatementLog()); //通过statementHandler对象调用ResultSetHandler将结果集转化为指定对象返回 return handler.<E>query(stmt, resultHandler); } finally { closeStatement(stmt); } }
通读这里的代码我们可以发现, Executor 本身是不会访问到数据库,而是作为指挥官,指挥三个小弟干事:
上面三个对象都是在 configuration.newStatementHandler 方法中创建的,然后调用 prepareStatement 拿到合适的 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; }
如果在DEBUG模式下拿到的 Connection 对象是 ConnectionLogger ,这就和第一篇的内容串联起来了。之后再通过 query 方法调用 execute 执行SQL语句,并使用 ResultSetHandler 处理结果集:
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); }
这里最终就是通过 反射模块 以及 Configuration 类中的 result 相关配置进行结果映射:
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 { // issue #228 (close resultsets) //调用resultset.close()关闭结果集 closeResultSet(rsw.getResultSet()); } } 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()); } } 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; }
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException { //获取resultSet中存在的,但是ResultMap中没有明确映射的列,填充至autoMapping中 List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix); boolean foundValues = false; if (!autoMapping.isEmpty()) { //遍历autoMapping,通过自动匹配的方式为属性复制 for (UnMappedColumnAutoMapping mapping : autoMapping) { //通过typeHandler从resultset中拿值 final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column); if (value != null) { foundValues = true; } if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) { // gcode issue #377, call setter on nulls (value is not 'found') //通过metaObject给属性赋值 metaObject.setValue(mapping.property, value); } } } return foundValues; }
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); // issue #541 make property optional //获得属性名称 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; }
反射实例化对象的代码比较长,但逻辑都比较清晰,上面的关键流程代码也都加上了注释,读者可自行参照源码阅读。
Mybatis核心原理就分析完了,相比较Spring源码简单了很多,但代码的优雅度和优秀的设计思想一点也不亚于Spring,也是非常值得我们好好学习掌握的。不过这3篇只是分析了Mybaits的核心执行原理,另外还有插件怎么扩展、拦截器会拦截哪些方法以及Mybatis和Spring的整合又是怎么实现的呢?读者们可以好好思考下,答案将在下一篇揭晓。
原文链接:https://www.cnblogs.com/yewy/p/13263817.html