`

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

阅读更多

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

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

一、从getMapper说起

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


 

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

 

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

 


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

 

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


 

 

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

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        MapperProxyFactory<T> 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);
            }
        }
    }

分析一下。


 

第一句: 

MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);

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


 

第二句: mapperProxyFactory.newInstance(sqlSession);

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

 

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

 


 

第一句:

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

 


 

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


 

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

 

 

二、从执行CRUD说起

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



 当我们执行到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为我们的代理类,

 


 

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判断中内容。


 在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,如下图:


 因此,这里得到的MapperStatement就是上面图的value值。执行完SqlCommand的构造函数后,得到的各参数内容如下:


 

设置完的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对象的内容:

 

  • 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);
     得到的index为null。
  • this.resultHandlerIndex = this.getUniqueParamIndex(method, ResultHandler.class);与上一步一样,得到的resultHandlerIndex 也为null。
  • this.params = Collections.unmodifiableSortedMap(this.getParams(method, this.hasNamedParameters));这一步得到方法传递参数的map(经过排序的)--SortedMap

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


 

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


 

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<String, Object> param = new MapperMethod.ParamMap();
                    int i = 0;

                    for(Iterator i$ = this.params.entrySet().iterator(); i$.hasNext(); ++i) {
                        Entry<Integer, String> 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> T selectOne(String statement, Object parameter) {
            List<T> 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;
            }
        }

      方法里调用了this.selectList(statement, parameter);源码如下:
    public <E> List<E> selectList(String statement, Object parameter) {
            return this.selectList(statement, parameter, RowBounds.DEFAULT);
        }
     继续往下找重载的selectList方法,源码如下:
    public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
            List var6;
            try {
                MappedStatement ms = this.configuration.getMappedStatement(statement);
                List<E> 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如下图:

     接着,执行List<E> 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 <E> List<E> 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语句、参数等信息:

      第二句中,CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql);得到该语句的缓存key值,见下图:

     第三句中,调用了CachingExecutor中重载的方法,this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);并将结果返回。我们看下源码:
    public <E> List<E> 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<E> 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);
        }
     

    这里,我们的cache为null,因此直接调用了BaseExecutor类中的query方法,看下源码:
    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 (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 <E> List<E> 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 <E> List<E> 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中,又初始化了参数处理器和结果集处理器:



     我们看下最关键的一行代码,var9 = handler.query(stmt, resultHandler);源码追踪,最后跟踪到PreparedStatementHandler类的query方法,这里跟jdbc处理的方法一样:
    public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
            PreparedStatement ps = (PreparedStatement)statement;
            ps.execute();
            return this.resultSetHandler.handleResultSets(ps);
        }
     

     this.resultSetHandler.handleResultSets(ps)结果集处理的源码如下:
    public List<Object> handleResultSets(Statement stmt) throws SQLException {
            List<Object> multipleResults = new ArrayList();
            int resultSetCount = 0;
            ResultSetWrapper rsw = this.getFirstResultSet(stmt);
            List<ResultMap> 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中执行CRUD的过程从源码层面上有了了解。内容比较多,能坚持下来也是种进步。

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

 

  • 大小: 8.7 KB
  • 大小: 9.5 KB
  • 大小: 27.5 KB
  • 大小: 25.8 KB
  • 大小: 7.7 KB
  • 大小: 10.1 KB
  • 大小: 37.9 KB
  • 大小: 39 KB
  • 大小: 45 KB
  • 大小: 6.4 KB
  • 大小: 6.1 KB
  • 大小: 10.2 KB
  • 大小: 15.6 KB
  • 大小: 35.8 KB
  • 大小: 41.9 KB
  • 大小: 26.1 KB
  • 大小: 21.2 KB
  • 大小: 30.6 KB
  • 大小: 25.7 KB
  • 大小: 8 KB
  • 大小: 17.9 KB
  • 大小: 29.1 KB
  • 大小: 31.4 KB
  • 大小: 4 KB
  • 大小: 27.5 KB
  • 大小: 33.7 KB
  • 大小: 43.5 KB
  • 大小: 19.5 KB
  • 大小: 53.4 KB
分享到:
评论
1 楼 小小小小小纯洁 2018-10-19  

相关推荐

    mybatis-3.5.9 源码(mybatis-3-mybatis-3.5.9.zip)

    - SqlSessionFactory 是 MyBatis 的核心组件,它用于创建 SqlSession 对象,每次数据库操作都需要通过 SqlSession 进行。SqlSessionFactory 的创建通常通过 SqlSessionFactoryBuilder,它解析配置文件或 XML 配置流...

    spring-boot-starter-mybatis-spring-boot-3.0.0.tar.gz

    - 下载:在官方仓库或第三方平台找到"spring-boot-starter-mybatis-spring-boot-3.0.0.tar.gz",并使用`wget`命令进行下载。 - 解压:使用`tar -zxvf spring-boot-starter-mybatis-spring-boot-3.0.0.tar.gz`命令...

    mybatis-3.2.0-SNAPSHOT-bundle

    4. Session和Transaction管理:MyBatis的SqlSession对象提供了CRUD操作,并且可以管理事务。在需要时,可以调用SqlSession的beginTransaction、commit和rollback方法来控制事务。 5. 参数映射和结果映射:MyBatis...

    【Java-框架-Mybatis】(01) - 文件

    - 分页查询:通过设置RowBounds对象实现物理分页,或者使用Mybatis Plus等扩展库进行更复杂的分页。 - 复杂查询:利用Mybatis的动态SQL,可以轻松处理复杂条件的查询。 总结,Mybatis作为一款优秀的Java持久层...

    mybatis-generator-core-1.3.2

    这些接口与MyBatis的SqlSession一起使用,实现CRUD操作。 6. **运行MBG**:一旦配置完成,你可以通过Maven、Gradle或者命令行执行MBG,它会根据配置生成所有必要的代码。在持续集成环境中,这一步通常在构建过程中...

    spring-boot-starter-mybatis-spring-boot-2.1.4.tar.gz

    本文将深入探讨Spring Boot如何与MyBatis集成,并以`spring-boot-starter-mybatis-spring-boot-2.1.4`为例,解析集成过程及其实现原理。 一、Spring Boot与MyBatis的集成原理 Spring Boot通过`spring-boot-starter...

    mybatis-spring-1.0.1-bundle mybatis spring 官方下载

    MyBatis-Spring 是一个将 MyBatis ORM 框架与 Spring 框架集成的库,旨在简化在 Spring 应用程序中使用 MyBatis 的过程。这个`mybatis-spring-1.0.1-bundle`是官方发布的版本,包含了一个稳定的集成框架,使得开发者...

    mybatis-plus-join-master.zip

    MyBatis-Plus 是 MyBatis 的一个扩展库,它为 MyBatis 提供了更多的功能,包括自动化 CRUD 操作、动态 SQL、以及更高级的查询能力,如本例中的连表查询。在 "mybatis-plus-join-master.zip" 文件中,我们可以深入...

    mybatis-spring-1.0.0-中文指南

    接下来是一些示例代码,展示了如何使用 MyBatis-Spring 进行简单的 CRUD 操作。 ```java public interface UserMapper { int insert(User user); } @Configuration public class AppConfig { @Bean public ...

    MyBatis-Simplified-Chinese.rar_ mybatis-3_Mybatis-spring_mybatis

    mybatis-spring 是一个 MyBatis 与 Spring 框架的整合库,它提供了与 Spring 的无缝集成,如事务管理、自动扫描 Mapper 类、SqlSession 的自动关闭等,简化了在 Spring 中使用 MyBatis 的过程。 七、最佳实践 1. ...

    MyBatis-CRUD-Annotation.zip

    在"MyBatis-CRUD-Annotation.zip"这个压缩包中,很显然,它包含了一个关于如何使用MyBatis的注解进行CRUD(创建、读取、更新、删除)操作的示例或教程。以下是对MyBatis中注解使用及相关知识点的详细解释: 1. **...

    mybatis源码分析思维导图.rar

    MyBatis的源码分析对于理解其工作原理、优化数据库交互以及进行二次开发至关重要。通过思维导图的方式,我们可以更直观、系统地理解MyBatis的架构和流程。 首先,MyBatis的核心概念包括SqlSessionFactory、...

    spring-boot-starter-mybatis-spring-boot-2.3.1.zip

    3. SqlSessionFactory:MyBatis的核心工厂类,负责创建SqlSession对象,处理数据库交互。 四、Spring Boot自动化配置 Spring Boot会自动配置MyBatis的SqlSessionFactory和SqlSessionTemplate,开发者无需手动创建。...

    mybatis-plus源码

    3. **SqlSessionTemplate**:对MyBatis的SqlSession进行了封装,提供了更安全的线程安全操作。 4. **QueryWrapper**和**UpdateWrapper**:这两个是条件构造器,用于构建复杂的查询和更新条件,它们内部封装了SQL的...

    spring-boot-starter-mybatis-spring-boot-1.2.2.zip

    3.2 兼容性:与Spring Boot 1.x系列其他版本相比,1.2.2版本在兼容MyBatis框架方面表现良好,适合大部分应用场景。 3.3 自动配置:1.2.2版本中,Spring Boot对MyBatis的自动配置已经相当成熟,大大减少了手动配置的...

    mybatis-spring-1.2.3-source

    《深入解析MyBatis-Spring 1.2.3源码》 MyBatis-Spring 是 MyBatis 框架与 Spring 框架之间的桥梁,它使得两个优秀的框架可以无缝集成,提供了对事务管理和依赖注入的支持。本文将详细探讨 MyBatis-Spring 1.2.3 ...

    SpringBoot+MyBatis+SqlSession

    MyBatis可以使用简单的XML或注解进行配置和原始映射,将接口和Java的POJOs(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。通过这种方式,MyBatis能够使数据库操作变得简单且易于维护。 SqlSession...

    MyBatis-CRUD

    MyBatis-CRUD 是一个围绕MyBatis框架展开的主题,它主要涵盖了数据库的基本操作,包括创建(Create)、读取(Read)、更新(Update)和删除(Delete)等CRUD操作。MyBatis是一个优秀的持久层框架,它支持定制化SQL、...

    mybatis-demo3-crud.zip

    在本示例"Mybatis-demo3-crud.zip"中,我们将深入探讨MyBatis如何进行单表查询操作,这是数据库操作中最基本也最常用的功能。 首先,MyBatis通过XML配置文件或注解方式定义SQL语句,这使得SQL与代码分离,增强了...

    spring_mybatis_spring-mybatis_

    - **SqlSessionFactory和SqlSession**:在MyBatis中,SqlSessionFactory是创建SqlSession的工厂,SqlSession则是执行SQL的会话对象。 - **Spring的Bean管理**:Spring通过Bean定义来管理MyBatis的...

Global site tag (gtag.js) - Google Analytics