MyBatis源码解析——Mapper代理对象执行增删改查

上篇,我们知道,mybatis获取的Mapper是一个代理对象,接下来我们就看看它是如何通过这个代理对象来执行增删改查操作的。下面是我的测试代码,还是一步步debug,.我们直接从userMapper.selectUserById(1)开始讲起。

userMapper执行selectUserById(1)方法,接下来便进入MapperProxy.invoke()方法里,这是因为userMapper是一个代理对象,在jdk的动态代理中,代理对象调用任意一个方法,都会先执行代理类的invoke()方法,具体细节,这里不再详述。在invoke()方法里,调用cachedMapperMethod(method)方法,返回一个MapperMethod对象。我们来看一下这个方法,先是从methodCache(是一个map)中取出该method对象,由于我们第一次去查询,所以该methodCache缓存没有,为null,然后判断为null,则去new一个MapperMethod(mapperInterface,method,configuration)对象(同时也new出了SqlCommand,MethodSignature这两个对象),放进methodCache中,并返回。然后返回的mapperMethod对象执行execute(sqlSession,args)方法。

execute(sqlSession,args)方法中,根据command.getType(),也就是增删改查类型,判断是SELECT,调用method.convertArgsToSqlCommandParam(args)方法,该方法将传入的参数进行封装,返回给我们一个Object类型的param,这里我们传入的是参数是1,所以返回给我们的就是Integer类型的,param为1,如果传入的参数是多个,它会给我们封装出一个map集合。接着执行sqlSession.selectOne(command.getName(),param),这就到DefaultSqlSession类中,它先去执行selectList()方法,从configuration中取出MappedStatement对象ms,调用executor.query(ms,wrapCollection(parameter),rowBounds,Executor.NO_RESULT_HANDLER)方法,这里我们说一下,这几个参数的含义。第一个参数ms,就是mappedStatement;第二个参数wrapCollection(paramter),是对参数进一步封装,这里参数为1,既不是集合也不是数组,所以直接返回1;第三个参数rowBounds,这是mybatis给我们提供的类,用于实现分页查询,它有offset和limit两个关键字;第四个参数是一个ResultHandler,这里是null。

进入到CachingExecutor类中,先从ms中取出BoundSql,BoundSql中有我们的sql语句,还有参数类型,参数值等。然后调用creteCacheKey(ms,parameter,rowBounds,boundSql),创建出一个cacheKey这个cacheKey就是用来表示缓存项的key,mybatis就是通过这个cacheKey来判断我们的查询是否是同一次查询,从而从缓存中返回同样的结果,有兴趣的朋友可以去详细看一些cacheKey的数据结构。接着继续调用query(ms,parameter,rowBounds,resultHandler,cacheKey),先从mappedStatement中取缓存,取出为null,继续调用delegate.query()。到了BaseExecutor类中,先封装一个ErrorContext,然后通过判断来决定是否清空本地缓存(也就是一级缓存),这里说一句,mybatis有一级缓存和二级缓存,其中默认开启一级缓存,我们这里没有开启二级缓存。判断本地缓存中是否有cacheKey对应的值,继续调用queryFromDatabase(ms,parameter,rowBounds,resultHandler,key,boundSql),先将key和EXECUTION_PLACEHOLDER放入到localCache(是一个HashMap)中,调用deQuery(ms,parameter,rowBounds,resultHandler,boundSql)方法,从ms中取出configuration,调用configuration.newStatementHandler(参数省略),去new出一个statementHandler(mybatis默认是PreparedStatementHandler)对象handler,在new的过程中,也new出了ParameterHandler对象(负责参数处理)和ResultSetHandler(负责结果集的处理)对象,又调用interceptorChain.pluginAll(statementHandler),这个在之前的文章有提过,mybatis通过拦截目标对象,对目标对象进行封装,增强它的功能,这里就直接返回handler,接着继续调用prepareStatement(handler,ms.getStatementLog()),去预编译sql语句,调用ParameterHandler设置参数,调用TypeHandler给预编译sql的占位符去赋值,返回给我们一个Statement。这里我们看一下,这个statement究竟是什么。

原来这也是个代理对象,由PreparedStatementLogger实现了InvocationHandler接口,去代理PrepareedStatement这个类。继续调用handler.query(statement,resultHandler),将statement强转为PreparedStatement对象ps,执行ps.execute(),这里就是执行sql语句了,进入PreparaedStatementLogger的invoke()方法,打印些日志,通过反射,去执行sql语句。接着调用resultSetHandler.handlerResultSets(ps)去处理结果集。

处理结果集是在DefaultResultSetHandler类中,先new一个ArrayList为multipleResults,最后就是用它来存放处理过的结果集,接着调用getFirstResultSet(stmt)方法来获取第一个ResultSet,得到一个ResultSetWrapper对象rsw,取出resultMaps,一般也就一个。继续调用handleResultSet(rsw,resultMap,multipleResults,null),来完成映射,将结果集放入multipleResults中。

进入handlerResultSet(rsw,resultMap,multipleResults,null)中,继续调用handleRowValues(rsw,resultMap,defaultResultHandler,rowBounds,null)

继续调用handleRowValuesForSimpleResultMap(rsw,resultMap,defaultResutlHandler,rowBounds,null)来进行简单映射

从这可以看出,在getRowValue(rsw,rsultMap)方法,返回一个Object对象,这正是我们所要查出来的结果,里面具体是怎如何把查询的结果集给映射到具体的对象,这里我就不再点下去了。接着调用storeObject(resultHandler,resultContext,rowValue,parentMapping,rsw.getResultSet()),将resultContext中的resultObject取出保存在list中并返回。然后就开始后续的连接关闭,将查询的list保存在本地缓存中localCache,返回list中第一条数据。

好了,这样代理对象的一个简单的查询就结束了,这里面还有不少地方没有进行详细的讲解,读者有兴趣的话可以自行研究一下源码。

猜你喜欢

转载自blog.csdn.net/rz_0212/article/details/81205290