MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析

阅读更多

在源码解析篇三中,我们已经得到了SqlSession。长征已经走了一半,前途一片光明。此篇中我们要解析下如何使用SqlSession去进行CRUD(创建(Create)、查询(Retrieve)(重新得到数据)、更新(Update)和删除(Delete))。

我们结合debug过程,逐渐深入源码解析。

一、从getMapper说起

在我们入门示例中,使用sqlSession.getMapper(MalltUserDao.class)获取到了MalltUserDao,那这中间经历了什么?下面是整个执行过程的时序图。

MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第1张图片
 

我们从sqlSession的getMapper方法开始深入,源码如下,源码在DefaultSqlSession类中:

 

public  T getMapper(Class type) {
        return this.configuration.getMapper(type, this);
    }

 


这里,调用了configuration对象中的getMapper方法,传递了两个参数,一个是类类型,一个是sqlSession对象,源码如下,源码在Configuration类:

 

public  T getMapper(Class type, SqlSession sqlSession) {
        return this.mapperRegistry.getMapper(type, sqlSession);
    }


 

 

在这里又调用了MapperRegistry类的getMapper方法,我们看下源码:

public  T getMapper(Class type, SqlSession sqlSession) {
        MapperProxyFactory mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
        if (mapperProxyFactory == null) {
            throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        } else {
            try {
                return mapperProxyFactory.newInstance(sqlSession);
            } catch (Exception var5) {
                throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
            }
        }
    }

分析一下。

MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第2张图片
 

第一句: 

MapperProxyFactory mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);

其中,knownMappers是在解析核心配置文件时,使用this.mapperElement(root.evalNode("mappers"));解析的,其中最终的核心方法就是addMapper,这个我们在解析元素时候说,这里不是我们的重点。从这里我们可以知道,knownMappers中放的就是类类型和根据类类型得到的MapperProxy工厂类MapperProxyFactory。那么MapperProxyFactory mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type); 得到的就是类类型对应的MapperProxy工厂类。我们这里的类类型是interface com.zhaodf.dao.MalltUserDao。

MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第3张图片
 

第二句: mapperProxyFactory.newInstance(sqlSession);

在此部分,使用了mapperProxy工厂类,使用我们原来得到的sqlSession,去得到我们的接口代理类。源码如下:

 

public T newInstance(SqlSession sqlSession) {
        MapperProxy mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
        return this.newInstance(mapperProxy);
    }
 我们分析下:

 


 

第一句:

MapperProxy mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);

使用我们传递的参数sqlSession,以及代理工厂类自身已经设置好的mapperInterface(com.zhaodf.dao.MalltUserDao),还有methodCache,这个我们没用到,它的map大小为空。

第二句:

this.newInstance(mapperProxy);

这句,使用了MapperProxyFactory类中的newInstance方法,源码如下:

 

protected T newInstance(MapperProxy mapperProxy) {
        return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
    }
 这里就是典型的代理模式,将MalltUserDao通过类加载器,转变为代理类(代理类中包含了Mapper中所有要执行的方法以及构造函数等)。过程如下:

 

MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第4张图片
 

这样,经过一整个过程的getMapper方法,得到了我们接口类的代理类,代理类的内容如下图:

MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第5张图片
 

下一步,我们就要根据代理类去执行我们的CRUD方法了。

 

 

二、从执行CRUD说起

得到代理类后,那我们就可以开始执行CRUD方法了。我们先把整个执行流程图画上:


MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第6张图片
 当我们执行到MalltUser user = malltUserDao.findMalltUserById(9);时,这时,代理类会去调用MapperProxy类中的invoke方法,源码如下:

 

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (Object.class.equals(method.getDeclaringClass())) {
            try {
                return method.invoke(this, args);
            } catch (Throwable var5) {
                throw ExceptionUtil.unwrapThrowable(var5);
            }
        } else {
            MapperMethod mapperMethod = this.cachedMapperMethod(method);
            return mapperMethod.execute(this.sqlSession, args);
        }
    }
 其中,proxy为我们的代理类,

 

MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第7张图片
 

method为原接口中的方法对象:

args为方法调用参数:


 第一句if判断,if (Object.class.equals(method.getDeclaringClass())) ,因为这里method的clazz为MalltUserDao,因此,这里肯定是不成立的。

走到else,MapperMethod mapperMethod = this.cachedMapperMethod(method);这里就需要分开详细说下。

1、this.cachedMapperMethod(method);

这里调用了cachedMapperMethod方法去得到MapperMethod,我们看下这个方法源码:

 

private MapperMethod cachedMapperMethod(Method method) {
        MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method);
        if (mapperMethod == null) {
            mapperMethod = new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
            this.methodCache.put(method, mapperMethod);
        }

        return mapperMethod;
    }
 因为我们的methodCache是个空的map,因此执行MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method)得到的mapperMethod为null。

 

继续执行if判断中内容。

MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第8张图片
 在if判断体内,初始化了MapperMethod对象,我们看下它做了些什么。

 

public MapperMethod(Class mapperInterface, Method method, Configuration config) {
        this.command = new MapperMethod.SqlCommand(config, mapperInterface, method);
        this.method = new MapperMethod.MethodSignature(config, method);
    }
 这里初始化了两个MapperMethod的静态内部类,SqlCommand和MethodSignature。

 

 

我们先看下SqlCommand的构造函数:

 

public SqlCommand(Configuration configuration, Class mapperInterface, Method method) throws BindingException {
            String statementName = mapperInterface.getName() + "." + method.getName();
            MappedStatement ms = null;
            if (configuration.hasStatement(statementName)) {
                ms = configuration.getMappedStatement(statementName);
            } else if (!mapperInterface.equals(method.getDeclaringClass().getName())) {
                String parentStatementName = method.getDeclaringClass().getName() + "." + method.getName();
                if (configuration.hasStatement(parentStatementName)) {
                    ms = configuration.getMappedStatement(parentStatementName);
                }
            }

            if (ms == null) {
                throw new BindingException("Invalid bound statement (not found): " + statementName);
            } else {
                this.name = ms.getId();
                this.type = ms.getSqlCommandType();
                if (this.type == SqlCommandType.UNKNOWN) {
                    throw new BindingException("Unknown execution method for: " + this.name);
                }
            }
        }
 

 

因为在前期获取到configuration对象中,解析mapper标签时,设置了mapperStatement,如下图:

MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第9张图片
 因此,这里得到的MapperStatement就是上面图的value值。执行完SqlCommand的构造函数后,得到的各参数内容如下:

MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第10张图片
 

设置完的SqlCommand,name为:com.zhaodf.dao.MalltUserDao.findMalltUserById;

type为SELECT。

 

我们再看下另外一个静态内部类干了些啥,MethodSignature的构造函数源码如下:

 

public MethodSignature(Configuration configuration, Method method) throws BindingException {
            this.returnType = method.getReturnType();
            this.returnsVoid = Void.TYPE.equals(this.returnType);
            this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
            this.mapKey = this.getMapKey(method);
            this.returnsMap = this.mapKey != null;
            this.hasNamedParameters = this.hasNamedParams(method);
            this.rowBoundsIndex = this.getUniqueParamIndex(method, RowBounds.class);
            this.resultHandlerIndex = this.getUniqueParamIndex(method, ResultHandler.class);
            this.params = Collections.unmodifiableSortedMap(this.getParams(method, this.hasNamedParameters));
        }
我们逐行来说明。我们先看下method对象的内容:

 

MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第11张图片

  • this.returnType = method.getReturnType();获取方法的返回类型就是com.zhaodf.model.MalltUser。
  • this.returnsVoid = Void.TYPE.equals(this.returnType);我们返回的不是void,因此returnsVoid 为false。
  • this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();这里,我们的返回类型是对象,不是数组和集合,因此returnsMany为false。
  • this.mapKey = this.getMapKey(method);在getMapKey的方法源码中,调用了类类型对象的isAssignableFrom方法,源码如下:
    private String getMapKey(Method method) {
                String mapKey = null;
                if (Map.class.isAssignableFrom(method.getReturnType())) {
                    MapKey mapKeyAnnotation = (MapKey)method.getAnnotation(MapKey.class);
                    if (mapKeyAnnotation != null) {
                        mapKey = mapKeyAnnotation.value();
                    }
                }
    
                return mapKey;
            }
     这里的isAssignableFrom方法是一个本地方法,关于该方法的执行解释如下:有两个Class类型的类象,一个是调用isAssignableFrom方法的类对象(后称对象a),以及方法中作为参数的这个类对象(称之为对象b),这两个对象如果满足以下条件则返回true,否则返回false:  a对象所对应类信息是b对象所对应的类信息的父类或者是父接口,简单理解即a是b的父类或接口  a对象所对应类信息与b对象所对应的类信息相同,简单理解即a和b为同一个类或同一个接口。因此if不成立,得到的mapKey为null。
  • this.returnsMap = this.mapKey != null;因为mapKey为null,因此returnsMap 为false。
  • this.hasNamedParameters = this.hasNamedParams(method);这里我们没有使用参数注解,因此hasNamedParameters为false。
  • this.rowBoundsIndex = this.getUniqueParamIndex(method, RowBounds.class);MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第12张图片
     得到的index为null。
  • this.resultHandlerIndex = this.getUniqueParamIndex(method, ResultHandler.class);与上一步一样,得到的resultHandlerIndex 也为null。
  • this.params = Collections.unmodifiableSortedMap(this.getParams(method, this.hasNamedParameters));这一步得到方法传递参数的map(经过排序的)--SortedMap

这样最后设置完的MethodSignature的内容如下:

MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第13张图片
 

这样,我们的MapperMethod初始化完成,然后将该MapperMethod放在了methodCache中,内容如下:

MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第14张图片
 

2、经过上面一系列的步骤,终于到了执行的时候了。mapperMethod.execute(this.sqlSession, args);

源码如下:

 

public Object execute(SqlSession sqlSession, Object[] args) {
        Object param;
        Object result;
        if (SqlCommandType.INSERT == this.command.getType()) {
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
        } else if (SqlCommandType.UPDATE == this.command.getType()) {
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
        } else if (SqlCommandType.DELETE == this.command.getType()) {
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
        } else {
            if (SqlCommandType.SELECT != this.command.getType()) {
                throw new BindingException("Unknown execution method for: " + this.command.getName());
            }

            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()) {
                result = this.executeForMap(sqlSession, args);
            } else {
                param = this.method.convertArgsToSqlCommandParam(args);
                result = sqlSession.selectOne(this.command.getName(), param);
            }
        }

        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;
        }
    }
 源码内容很多,其实很简单,就是根据我们步骤1中得到的command,去执行不同的调用。我们现在得到的是SELECT,因此,直接找到最后一步的else。根据步骤1得到的MapperMethod.MethodSignature method中的各项参数,我们逐步判断,走到了最后:

 

param = this.method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(this.command.getName(), param);

我们逐句分析下。

  • 在方法this.method.convertArgsToSqlCommandParam(args)中,按照方法含义,就是将我们传递的实际参数值传递到我们的sql语句中,源码如下:

 

public Object convertArgsToSqlCommandParam(Object[] args) {
            int paramCount = this.params.size();
            if (args != null && paramCount != 0) {
                if (!this.hasNamedParameters && paramCount == 1) {
                    return args[(Integer)this.params.keySet().iterator().next()];
                } else {
                    Map param = new MapperMethod.ParamMap();
                    int i = 0;

                    for(Iterator i$ = this.params.entrySet().iterator(); i$.hasNext(); ++i) {
                        Entry entry = (Entry)i$.next();
                        param.put(entry.getValue(), args[(Integer)entry.getKey()]);
                        String genericParamName = "param" + String.valueOf(i + 1);
                        if (!param.containsKey(genericParamName)) {
                            param.put(genericParamName, args[(Integer)entry.getKey()]);
                        }
                    }

                    return param;
                }
            } else {
                return null;
            }
        }
 因为我们的args参数数组的大小为1,且根据我们构造MapperMethod.MethodSignature method得到的hasNamedParameters为false,因此直接走了 return args[(Integer)this.params.keySet().iterator().next()];

 

得到的param=9。


 

 

  • 在方法result = sqlSession.selectOne(this.command.getName(), param);中,我们调用了sqlSession的selectOne方法。这里的this.command.getName()为com.zhaodf.dao.MalltUserDao.findMalltUserById,param就是第一步得到的param--Object对象,内容为9。DefaultSqlSession的selectOne源码如下:
    public  T selectOne(String statement, Object parameter) {
            List 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;
            }
        }
    MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第15张图片
      方法里调用了this.selectList(statement, parameter);源码如下:
    public  List selectList(String statement, Object parameter) {
            return this.selectList(statement, parameter, RowBounds.DEFAULT);
        }
     继续往下找重载的selectList方法,源码如下:
    public  List selectList(String statement, Object parameter, RowBounds rowBounds) {
            List var6;
            try {
                MappedStatement ms = this.configuration.getMappedStatement(statement);
                List 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;
        }
     我们逐句分析,MappedStatement ms = this.configuration.getMappedStatement(statement);这里就是根据我们在mapper配置文件中的唯一性id去得到对应的MappedStatement。内容如下:
    public MappedStatement getMappedStatement(String id) {
            return this.getMappedStatement(id, true);
        }
    
        public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
            if (validateIncompleteStatements) {
                this.buildAllStatements();
            }
    
            return (MappedStatement)this.mappedStatements.get(id);
        }
     得到的MappedStatement如下图:
    MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第16张图片
     接着,执行List result = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);这里面包含了4个参数,第一个就是我们上一步得到的ms,第二个参数,就是将参数对象根据所属的不同实例类型进行包装,源码如下:
    private Object wrapCollection(Object object) {
            DefaultSqlSession.StrictMap map;
            if (object instanceof List) {
                map = new DefaultSqlSession.StrictMap();
                map.put("list", object);
                return map;
            } else if (object != null && object.getClass().isArray()) {
                map = new DefaultSqlSession.StrictMap();
                map.put("array", object);
                return map;
            } else {
                return object;
            }
        }
      因为我们这里的Object实际内容是Integer的9,因此,返回的object还是Integer 9。所有参数确定后,我们看下CachingExecutor中query方法的源码:
    public  List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
            BoundSql boundSql = ms.getBoundSql(parameterObject);
            CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql);
            return this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
        }
     在源码中,第一句BoundSql boundSql = ms.getBoundSql(parameterObject);根据传递的参数对象,获取到BoundSql。BoundSql 包含了sql语句、参数等信息:
    MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第17张图片
      第二句中,CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql);得到该语句的缓存key值,见下图:

     第三句中,调用了CachingExecutor中重载的方法,this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);并将结果返回。我们看下源码:
    public  List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
            Cache cache = ms.getCache();
            if (cache != null) {
                this.flushCacheIfRequired(ms);
                if (ms.isUseCache() && resultHandler == null) {
                    this.ensureNoOutParams(ms, parameterObject, boundSql);
                    List list = (List)this.tcm.getObject(cache, key);
                    if (list == null) {
                        list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
                        this.tcm.putObject(cache, key, list);
                    }
    
                    return list;
                }
            }
    
            return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
        }
     
    MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第18张图片
    这里,我们的cache为null,因此直接调用了BaseExecutor类中的query方法,看下源码:
    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 (this.closed) {
                throw new ExecutorException("Executor was closed.");
            } else {
                if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
                    this.clearLocalCache();
                }
    
                List list;
                try {
                    ++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 {
                    --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;
            }
        }
     我们直接看关键一句:
    list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
     
    private  List 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;
        }
     这里关键代码也只有一句,list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);是用来做查询的,其他操作只是将我们查询出来的结果缓存,放在localCache中。我们分析下doQuery方法,这个方法在SimpleExecutor类中,我们去看看:
    public  List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
            Statement stmt = null;
    
            List var9;
            try {
                Configuration configuration = ms.getConfiguration();
                StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
                stmt = this.prepareStatement(handler, ms.getStatementLog());
                var9 = handler.query(stmt, resultHandler);
            } finally {
                this.closeStatement(stmt);
            }
    
            return var9;
        }
     这里使用ms获取到我们configuration,然后使用configuration去得到对应的Statement处理器,关键的代码如下:
    public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
            StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
            StatementHandler statementHandler = (StatementHandler)this.interceptorChain.pluginAll(statementHandler);
            return statementHandler;
        }
     
    public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
            switch(ms.getStatementType()) {
            case STATEMENT:
                this.delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
                break;
            case PREPARED:
                this.delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
                break;
            case CALLABLE:
                this.delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
                break;
            default:
                throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
            }
    
        }
     代码看似很多,实际上就做了一个动作,根据我们statementType(有三种:STATEMENT,PREPARED 或 CALLABLE ,默认情况下是PREPARED )去得到不同的statement处理器,因此我们这里得到的是PreparedStatementHandler。在PreparedStatementHandler中,又初始化了参数处理器和结果集处理器:
    MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第19张图片

    MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第20张图片
     我们看下最关键的一行代码,var9 = handler.query(stmt, resultHandler);源码追踪,最后跟踪到PreparedStatementHandler类的query方法,这里跟jdbc处理的方法一样:
    public  List query(Statement statement, ResultHandler resultHandler) throws SQLException {
            PreparedStatement ps = (PreparedStatement)statement;
            ps.execute();
            return this.resultSetHandler.handleResultSets(ps);
        }
     
    MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第21张图片
     this.resultSetHandler.handleResultSets(ps)结果集处理的源码如下:
    public List handleResultSets(Statement stmt) throws SQLException {
            List multipleResults = new ArrayList();
            int resultSetCount = 0;
            ResultSetWrapper rsw = this.getFirstResultSet(stmt);
            List resultMaps = this.mappedStatement.getResultMaps();
            int resultMapCount = resultMaps.size();
            this.validateResultMapsCount(rsw, resultMapCount);
    
            while(rsw != null && resultMapCount > resultSetCount) {
                ResultMap resultMap = (ResultMap)resultMaps.get(resultSetCount);
                this.handleResultSet(rsw, resultMap, multipleResults, (ResultMapping)null);
                rsw = this.getNextResultSet(stmt);
                this.cleanUpAfterHandlingResultSet();
                ++resultSetCount;
            }
    
            String[] resultSets = this.mappedStatement.getResulSets();
            if (resultSets != null) {
                while(rsw != null && resultSetCount < resultSets.length) {
                    ResultMapping parentMapping = (ResultMapping)this.nextResultMaps.get(resultSets[resultSetCount]);
                    if (parentMapping != null) {
                        String nestedResultMapId = parentMapping.getNestedResultMapId();
                        ResultMap resultMap = this.configuration.getResultMap(nestedResultMapId);
                        this.handleResultSet(rsw, resultMap, (List)null, parentMapping);
                    }
    
                    rsw = this.getNextResultSet(stmt);
                    this.cleanUpAfterHandlingResultSet();
                    ++resultSetCount;
                }
            }
    
            return this.collapseSingleResultList(multipleResults);
        } MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第22张图片

    三、通过以上分析,我们对Mybatis中执行CRUD的过程从源码层面上有了了解。内容比较多,能坚持下来也是种进步。

    这里涉及到代理模式,后续我们会分享一篇代理模式的帖子。

     

    • MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第23张图片
    • 大小: 8.7 KB
    • MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第24张图片
    • 大小: 9.5 KB
    • MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第25张图片
    • 大小: 27.5 KB
    • MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第26张图片
    • 大小: 25.8 KB
    • MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第27张图片
    • 大小: 7.7 KB
    • MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第28张图片
    • 大小: 10.1 KB
    • MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第29张图片
    • 大小: 37.9 KB
    • MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第30张图片
    • 大小: 39 KB
    • MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第31张图片
    • 大小: 45 KB
    • MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第32张图片
    • 大小: 6.4 KB
    • MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第33张图片
    • 大小: 6.1 KB
    • MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第34张图片
    • 大小: 10.2 KB
    • MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第35张图片
    • 大小: 15.6 KB
    • MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第36张图片
    • 大小: 35.8 KB
    • MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第37张图片
    • 大小: 41.9 KB
    • MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第38张图片
    • 大小: 26.1 KB
    • MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第39张图片
    • 大小: 21.2 KB
    • MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第40张图片
    • 大小: 30.6 KB
    • MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第41张图片
    • 大小: 25.7 KB
    • MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第42张图片
    • 大小: 8 KB
    • MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第43张图片
    • 大小: 17.9 KB
    • MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第44张图片
    • 大小: 29.1 KB
    • MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第45张图片
    • 大小: 31.4 KB
    • MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第46张图片
    • 大小: 4 KB
    • MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第47张图片
    • 大小: 27.5 KB
    • MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第48张图片
    • 大小: 33.7 KB
    • MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第49张图片
    • 大小: 43.5 KB
    • MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第50张图片
    • 大小: 19.5 KB
    • MyBatis源码解析系列(四)--使用SqlSession去进行CRUD解析_第51张图片
    • 大小: 53.4 KB
    • 查看图片附件

    你可能感兴趣的:(Mybatis,源码,CRUD)