当前位置: 首页 > news >正文

烟台市铁路建设管理局网站石家庄抖音seo公司

烟台市铁路建设管理局网站,石家庄抖音seo公司,如何自己做网站,佛山网络公司推荐1 Mybatis流程解析概述 Mybatis框架在执行增伤改的流程基本相同#xff0c; 很简单#xff0c;这个大家只要自己写个测试demo跟一下源码,基本就能明白是怎么回事#xff0c;查询操作略有不同#xff0c; 这里主要通过查询操作来解析一下整个框架的流程设计实现。 2 Mybat…1 Mybatis流程解析概述 Mybatis框架在执行增伤改的流程基本相同 很简单这个大家只要自己写个测试demo跟一下源码,基本就能明白是怎么回事查询操作略有不同 这里主要通过查询操作来解析一下整个框架的流程设计实现。 2 Mybatis查询操作的基本流程 2.1 Mapper代理对象MapperProxy的创建流程 Mapper实际上是一个代理对象是从SqlSession中获取的 因为SqlSession的实现类DefaultSqlSession类中存在Configuration类对象 而我们在创建sqlSession对象的操作完成之后 configuration对象中已经存储了所有的mapper及其对应的代理对象之间的映射关系如下图 看看mapperRegistry如下图 MapperRegistry类中创建代理对象的核心步骤如下 MapperProxyFactory类中的newInstance方法的调用 这里通过动态代理创建出目标mapper层的代理类对象并返回。 2.2 创建Mapper代理对象MapperProxy的调用流程 总结getMapper(Class clazz)方法创建动态代理对象的调用逻辑如下图 2.3 动态代理对象执行增删改查的核心流程 这里我们使用查询接口来进行流程剖析 在应用层 我们会调用下面这行代码来实现查询 现在我们来分析一下这行代码在Mybatis的源码中是如何执行的 它究竟是如何实现查询我们想要的数据的功能的 ListLibBook libBooks mapper.selectBooksByCondition(T311.5/3-1, null, null, null);动态代理大家肯定都熟悉 如果你要看源码 连这点儿基础都没有 建议不要硬看 把java基础夯实了再来研究源码吧 我们在使用Proxy创建动态对象时比然要传递一个InvocationHandler接口的实现类或者匿名内部类对象 2.1章节我们在创建动态代理类的时候在MapperProxyFactory类中的newInstance方法中 我们传递的InvocationHandler得实现类就是我们的MapperProxy类对象 所以这个MapperProxy类必然实现了InvocationHandler接口 我们验证一下是不是 果然MapperProxy类实现了InvocationHandler接口并实现了invoke方法 下面我们调用Mapper接口中的方法selectBooksByCondition 实际上都是通过MapperProxy.invoke()方法去执行的。 2.3.1 MapperProxy.invoke()方法调用解析 Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {/*** 这里给大家解释一下这个invoke判断里面加这个判断的原因:* 大家都知道Object对象默认已经实现了很多方法 我们的Mapper接口在进行定义的时候 可能定义了静态方法、 默认方法以及抽象方法* 因此在创建了动态代理对象的时候 这个动态代理类肯定也包含了很多的方法 从Object类继承的方法 从接口继承的默认方法* 以及从接口继承抽象方法需要实现取执行SQL语句的方法** 这个if分值判断的只要目的在将无需走SQL执行流程的方法如(toString/equals/hashCode)等先过滤掉* 然后再抽象方法及默认方法中通过一个接口MapperMethodInvoker再进行一次判断找到所有需要执行SQL的方法通过PlainMethodInvoker的invoke* 方法取执行SQL语句获取结果能够加快获取 mapperMethod 的效率*/if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);} else {return cachedInvoker(method).invoke(proxy, method, args, sqlSession);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}}PlainMethodInvoker().invoke()方法会调用MapperMethod类中的execute()方法 2.3.2 MapperMethod.execute()方法调用解析 MapperMethod类中的execute()方法具体执行逻辑如下 public Object execute(SqlSession sqlSession, Object[] args) {Object result;switch (command.getType()) {// 插入操作case INSERT: {// 如果你用过Mybatis的话 你一定清楚 Mybatis的中参数传递的方式有以下几种// 第一种 [arg0,arg1,...]// 第二种 [param1,param2,...]// convertArgsToSqlCommandParam(args)方法就是实现这种映射// 举例说明我之前传递的参数是一个Book对象{bookId: val1, bookIndexNo:val2, bookName:val3}// 经过convertArgsToSqlCommandParam()方法处理之后得到{bookId: val1, bookIndexNo:val2, bookName:val3, param1: val1, param2:val2, param3:val3}Object param method.convertArgsToSqlCommandParam(args);// 执行SQL操作并对返回结果进行封装处理result rowCountResult(sqlSession.insert(command.getName(), param));break;}// 更新操作case UPDATE: {Object param method.convertArgsToSqlCommandParam(args);// 执行SQL操作并对返回结果进行封装处理result rowCountResult(sqlSession.update(command.getName(), param));break;}// 删除操作case DELETE: {Object param method.convertArgsToSqlCommandParam(args);// 执行SQL操作并对返回结果进行封装处理result rowCountResult(sqlSession.delete(command.getName(), param));break;}case SELECT:// 查询操作的情况比较复杂 需要分情况逐一处理if (method.returnsVoid() method.hasResultHandler()) {// 如果查询操作的返回为空并且方法已经指定了结果处理器时执行以下语句executeWithResultHandler(sqlSession, args);result null;} else if (method.returnsMany()) {// 如果查询操作的返回返回结果为List集合 执行以下语句result executeForMany(sqlSession, args);} else if (method.returnsMap()) {// 如果查询操作的返回返回结果为Map集合 执行以下语句result executeForMap(sqlSession, args);} else if (method.returnsCursor()) {// 如果查询操作的返回返回结果为Cursor 执行以下语句result executeForCursor(sqlSession, args);} else {Object param method.convertArgsToSqlCommandParam(args);result sqlSession.selectOne(command.getName(), param);// 如果查询操作的返回返回结果为单个对象并且指定返回类型为Optional 则将返回结果封装成Optional对象返回if (method.returnsOptional() (result null || !method.getReturnType().equals(result.getClass()))) {result Optional.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;}当前我们执行的查询语句 返回的结果列表是一个List, 所以调用的是executeForMany(sqlSession, args); private E Object executeForMany(SqlSession sqlSession, Object[] args) {ListE result;Object param method.convertArgsToSqlCommandParam(args);// 这里是判断你的mapper层接口方法是否传递了RowBounds对象根据是否传递了RowBounds对象来调用sqlSession对象的selectList的不同传参的重载方法// 这个RowBounds对象主要是来进行分页功能会将所有符合条件的数据全都查询出来加载到内存中然后在内存中再对数据进行分页(当数据量非常大的时候 就会发生OOM, 一般不推荐使用)if (method.hasRowBounds()) {RowBounds rowBounds method.extractRowBounds(args);result sqlSession.selectList(command.getName(), param, rowBounds);} else {// 我们这里没有传递RowBounds对象 所以调用的是这个方法result sqlSession.selectList(command.getName(), param);}// issue #510 Collections arrays supportif (!method.getReturnType().isAssignableFrom(result.getClass())) {if (method.getReturnType().isArray()) {return convertToArray(result);} else {return convertToDeclaredCollection(sqlSession.getConfiguration(), result);}}return result;}2.3.3 executor.query()执行器执行查询的逻辑流程解析 2.3.3.1 创建PreparedStatement对象 我们在创建configuration类对象的时候已经设置过默认的executor对象类型就是Simple 那么这里的executor应该是SimpleExecutor对象啊 但是实际上并不是 这里的executor对象是CachingExecutor对象 这里大家肯定会有疑问 在debug的过程中 我明明没有看到有创建CachingExecutor对象啊 这个对象是啥时候创建的 为啥不是SimpleExecutor对象对象啊 这里解答一下 下面这两行代码只要你使用过Mybatis框架 肯定不陌生 SqlSessionFactory factory factoryBuilder.build(ins); SqlSession sqlSession factory.openSession(true);factory.openSession(true)这个方法调用我们跟进去看看 好了以上就是executor是什么时候创建的以及为什么明明应该是simpleExecutor但是实际却是cachingExecutor对象的原因。 下面接着说查询方法的执行过程 sqlsession对象会调用执行器的查询方法开始查询 这里调用了委托者SimpleExecutor对象的query()方法但是SimpleExecutor对象没有query方法 就调用了父类BaseExecutor里面query()方法 query()方法中又调用了父类BaseExecutor里面queryFromDatabase()方法 queryFromDatabase()()方法中又调用了父类BaseExecutor的抽象方法doQuery()方法父类中没有实现该方法但是子类SimpleExecutor对该方法进行了重写 调用子类SimpleExecutor中的doQuery()方法执行 子类SimpleExecutor中的doQuery()方法如下图 在SimpleExecutor类中的doQuery()方法中StatementHandler handler configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);这行代码执行完成的时候当前代理方法的ParameterHandler对象和ResultSetHandler对象也会创建并扩展完成如果我们又在配置文件中自定义ParameterHandler、ResultSetHandler、StatementHandler在这个过程都会被通过拦截器注册并依次调用。 RoutingStatementHandler里面没有任何的实现是用来创建基本的StatementHandler的。这里会根据MappedStatement里面的statementType(一共有三种STATEMENT、PREPARED、CALLABLE)决定StatementHandler的类型MappedStatement对象默认是PREPARED。 在SimpleExecutor类中的doQuery()方法中StatementHandler handler configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);Configuration类中的如下方法将被调用 /** 拦截参数处理器通过拦截器拓展插件功能对ParameterHandler的基础功能进行增强 */ public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {ParameterHandler parameterHandler mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);parameterHandler (ParameterHandler) interceptorChain.pluginAll(parameterHandler);return parameterHandler;}/** 拦截结果映射处理器通过拦截器拓展插件功能对ResultSetHandler的基础功能进行增强 */ public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,ResultHandler resultHandler, BoundSql boundSql) {ResultSetHandler resultSetHandler new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);resultSetHandler (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);return resultSetHandler;}/** 拦截SQL语句处理器通过拦截器拓展插件功能对StatementHandler的基础功能进行增强 */ 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) interceptorChain.pluginAll(statementHandler);return statementHandler;}RoutingStatementHandler对象创建完成之后会调用prepareStatement()方法创建Statement对象 2.3.3.2 Statement中的入参处理 在SimpleExecutor对象中再创建出最终要执行的Statement对象之后 就是处理入库真实替换SQL语句中的占位符了主要是通过statementHandler.parameterize(stmt)来实现的 具体的还是调用了PreparedStatementHandler类中的.parameterize(stmt)方法来处理 这里就用到了我们的ParameterHandler对象了 通过setParameters()方法完成参数替换。 具体的方法实现有兴趣可以自行研究 2.3.3.3 ResultSet结果及处理 SimpleExecutor类的handler.query(stmt, resultHandler)方法最终会调用PreparedStatementHandler的query方法执行SQL语句并返回结果 PreparedStatementHandler的query方法会调用PreparedStatement.execute()执行SQL语句,返回查询结果集 DefaultResultSetHandler.handleResultSets()方法会将查询结果集处理成Java中的List对象返回。 3 查询方法的调用逻辑图 4 源码阅读过程中的笔记 MapperProxyinvokeMapperMethodexecute()executeForMany()DefaultSqlSessionselectList()CachingExecutorquery()1、生成二级缓存KEYcreateCacheKey()1、将缓存KEY作为参数传递调用重载query()方法query()1、从MappedStatement对象中获取Cache对象2、如果Cache对象不是null, 说明有二级缓存2.1、判断MappedStatement对象的isFlushCacheRequired属性是为true 如果为true刷新缓存2.2、判断MappedStatement对象是否使用缓存(isUseCache属性是否为true),并且resultHandler属性默认为null 如果两者都满足 直接从Cache对象中通过缓存KEY查询结果数据list2.3、判断结果数据list是否为空如果为空从数据库中查询出结果然后将查询结果放置到Cache对象中的缓存KEY下最后把查询结果list返回3、如果Cache对象是null, 直接从数据库中查询出结果(调用BaseExecutor.query())BaseExecutorcreateCacheKey()query()1、判断当前的queryStack是否为0并且MappedStatement对象的isFlushCacheRequired属性是为true满足条件就清空一级缓存否则跳过2、判断MappedStatement对象的resultHandler属性是否为null满足条件就直接从一级缓存中通过缓存KEY查询结果数据list2.1、如果结果数据list不为空调用handleLocallyCachedOutputParameters()方法对结果进行处理(主要是因为如果调用的是存储过程执行的结果需要接受并处理)2.2、如果结果数据list为空调用queryFromDatabase()直接从数据库中进行查询结果3、处理嵌套查询if (queryStack 0) {for (DeferredLoad deferredLoad : deferredLoads) {deferredLoad.load();}// issue #601deferredLoads.clear();// 嵌套查询会借助一级缓存所以一级缓存不能关闭// 嵌套查询是肯定会延迟加载的存入DeferredLoad类避免重复的查询执行// 执行嵌套查询时当有结果值就直接存入没有就存入一个占位符这样相同的嵌套查询在一级缓存中只会存在一个当所有的都处理完成以后然后再最终处理所有的延迟加载4、返回最终结果queryFromDatabase()1、首先通过缓存KEY在缓存对象中先开辟空间因为缓存结果还没有从数据库中查询先设置一个占位符告诉其他人这个坑已经有人占了2、调用doQuery()方法查询数据3、将第一步的缓存对象清除(因为它没有真正的保存数据对象只是在我查询数据还没有返回数据结果的这段时间假装已经有缓存了)4、将真正从数据中查询出来的数据对象放入一级缓存然后然后结果list SimpleExecutordoQuery()1、创建StatementHandler对象1.1 MappedStatement对象中默认的statementType对象是PREPARED, 这里会执行new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);1.2 new PreparedStatementHandler的构造方法中调用了父类BaseStatementHandler的构造方法1.3 父类BaseStatementHandler的构造方法中做了parameterHandler和resultSetHandler的初始化操作调用了configuration.newParameterHandler()和configuration.newResultSetHandler()方法进行对象创建这里默认都是创建的DefaultParameterHandler和DefaultResultSetHandler对象2、prepareStatement() 2.1 创建Connection连接对象2.2 执行statementHandler对象的prepare()方法返回Statement对象// 2.2.1 调用RoutingStatementHandler类下的prepare()方法返回Statement对象// 2.2.2 调用BaseStatementHandler类下的prepare()方法返回Statement对象2.3 执行statementHandler对象的parameterize()方法// 2.3.1 调用RoutingStatementHandler类下的parameterize()方法// 2.3.2 调用PreparedStatementHandler类下的parameterize()方法2.4 返回Statement对象3、调用StatementHandler的query()方法3.1 调用RoutingStatementHandler类的query()方法3.2 调用PreparedStatementHandler类的query()方法BaseStatementHandler这个方法在创建prepare()1、调用instantiateStatement()创建Statement对象1.1 调用PreparedStatementHandler类下的instantiateStatement()方法创建Statement对象2、调用setStatementTimeout()设置执行和事务的超时时间3、调用setFetchSize()设置驱动的结果集获取数量fetchSize4、最后返回Statement对象PreparedStatementHandlerinstantiateStatement()1、调用JDBC的connection.prepareStatement(sql)方法创建出一个PreparedStatement对象返回parameterize()1、调用parameterHandler对象的setParameters()方法将PrepareStatement对象中的SQL语句中的参数占位符都替换成传入的参数值1.1 调用DefaultParameterHandler类的setParameters()方法1.2 遍历所有的ParameterMapping对象依次替换所有的占位符为实际的传入的参数值query()1、获取到PreparedStatement对象2、调用PreparedStatement。execute()执行SQL语句3、调用DefaultResultSetHandler类的handleResultSets()方法处理查询结果集DefaultResultSetHandlerhandleResultSets()handleResultSet()handleRowValues()handleRowValuesForSimpleResultMapgetRowValue()applyPropertyMappings()方法完成从ResultSet结果集中的每一个行数据和Java对象之间的映射storeObject() 将getRowValue()处理完成的Objcet数据添加到List集合
http://www.dnsts.com.cn/news/105375.html

相关文章:

  • 原创文章对网站的好处中国十大公关公司排名
  • 网站中间内容做多大尺寸的红帽linux安装wordpress
  • 运动网站模板文山专业网站建设报价
  • 网站快速推广排名技巧企业文化墙设计网站推荐
  • 做网站跟赚钱嘛网络营销优化
  • 做网站原型的软件福田欧马可
  • 墨刀怎么做网站软件开发app制作下载
  • 怎么做网站底部版权信息wordpress的文件夹
  • 山西seo苏州seo优化排名推广
  • 西安网站建设网络公司北京网站开发哪家好薇
  • 基于网站开发的app网络管理软件app
  • 做彩票网站东莞个人做网站
  • 漳州网站建设点击博大选上海品牌网站建设公
  • wordpress 聊天莱芜新站优化
  • 单页面 网站怎么做的建设教育局官方网站
  • 查询网站所有死链接制作杂志 wordpress主题
  • 企业网站设计需要了解外贸公司名字免费起名大全
  • 中国住房与城乡建设部网站wordpress免费主题cms
  • 什么网站可以自学ps做贵宾卡瓯网
  • 上海网站建设宣传深圳公司注册地址新规定
  • 西安网站建设比较好的公司在猪八戒上做网站要注意什么
  • flash全屏网站模板深圳网站建设忧化
  • 如何做家具网站模板下载网站什么好
  • 石家庄市城乡建设学校网站长春网站优化页面
  • 做网站上传照片的尺寸移动手机网站建设
  • 网站维护外包合同中铁十二局出国招工
  • 怎么在百度上做推广上首页哪些网站可以做seo
  • 杭州网站案列广告推广一个月多少钱
  • 石河子做网站的公司网站积分解决方案
  • 免费网站提供制作触屏版网站开发