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

制作企业网站页面多少钱seo是指

制作企业网站页面多少钱,seo是指,如何通过html做网站,松江建设管理中心网站🏆MyBatis延迟加载原理剖析 1. 延迟加载的介绍及使用 本文将针对MyBatis提供的延迟加载(懒加载)原理剖析。 1.1 延迟加载是什么? 简单的来说延迟加载就是,在需要用到数据的时候进行加载,不需要用到数据…

🏆MyBatis延迟加载原理剖析

1. 延迟加载的介绍及使用

本文将针对MyBatis提供的延迟加载(懒加载)原理剖析。

1.1 延迟加载是什么?

简单的来说延迟加载就是,在需要用到数据的时候进行加载,不需要用到数据就不进行加载。

假设数据库中涉及两张表,用户表AND订单表(一对多的关系),假设一个有很多订单,那么在查询用户的时候,需不需要当前用户关联的订单数据查询出来?

通常来说查询用户信息的时候,肯定是需要用到用户订单的时候在查询为好,尤其是一对多的多表查询,通常都建议采用延迟加载,因为单表查询肯定要比多表关联查询速度要快,先从单表查询,接着需要时在关联表进行关联查询,会提高数据库性能。

1.2 如何启用延迟加载

MyBatis默认不启用延迟加载,启用延迟加载则需要我们进行一些简单的配置即可。

首先可以MyBatis SqlMapper核心配置文件中设置setting属性 设置全局懒加载

 <settings><!-- 开启全局延迟加载,默认值是true --><setting name="lazyLoadingEnabled" value="true" /><!-- 设置全局积极懒加载,默认值是true --><setting name="aggressiveLazyLoading" value="false"/></settings>

或者在mapper配置文件中配置fetchType标签 设置局部懒加载


<resultMap id="userMap" type="com.mryan.pojo.User"><id property="id" column="id"></id><result property="username" column="username"></result><!--fetchType="lazy"  懒加载策略  fetchType="eager"  立即加载策略 --><collection property="orderList" ofType="com.mryan.pojo.Order"select="com.mryan.mapper.IOrderMapper.findOrderByUid" column="id" fetchType="lazy"><id property="id" column="uid"/><result property="orderTime" column="ordertime"/><result property="total" column="total"/></collection>
</resultMap><select id="findOrderByUid" resultType="com.mryan.pojo.Order">
select *
from orders
where uid = #{uid}
</select>

注意这里 局部的懒加载策略优先级药高于全局的懒加载策略

1.3 测试延迟加载的作用

为测试多表关联,表结构及pojo结构如下:

User

在这里插入图片描述

public class User implements Serializable {private Integer id;private String username;// 用户关联的订单数据private List<Order> orderList;
//省略

Orders

在这里插入图片描述

public class Order implements Serializable {private Integer id;private String orderTime;private Double total;// 表明该订单属于哪个用户private User user;
//省略

在完成上述配置之后,跑一个单元测试,试一下延迟加载是否生效

    /*测试 延迟加载查询*/
@Test
public void TEST_QUERY_LAZY()throws IOException{InputStream inputStream=Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory factory=new SqlSessionFactoryBuilder().build(inputStream);SqlSession sqlSession=factory.openSession();User user=sqlSession.selectOne("com.mryan.mapper.IUserMapper.findById",1);//延迟加载生效 下方输出语句 不涉及到orders表 于是不会打印orders相关日志System.out.println("user:"+user.getUsername());//涉及orders表 才会执行相关SQL语句 加载orders执行日志 (延迟加载 什么时候用什么时候查)System.out.println("orders:"+user.getOrderList());}

在这里插入图片描述

可以发现user.getUsername()的时候只触发了查询用户的sql语句,而user.getOrderList()时才触发了查询订单的sql语句,由此可见延迟加载生效,做到了不用不查,用到在查。

2. 延迟加载源码剖析

下面将对源码进行阅读,剖析Mybatis延迟加载的原理。

在MyBatis系列文章第一篇浅析MyBatis执行SQL流程
就对SQL的执行流程进行了讲解。简单复习一下大致流程,SQL查询语句的执行是由SqlSession分发交由Executor托管执行,调度StatementHandler负责JDBC
statement操作,之后下发给ParameterHandler负责对用户传递参数进行转化处理SQL参数,再接着执行SQL语句,最后通过ResultSetHandler对返回结果进行封装处理返回。

根据刚刚对延迟加载功能的测试,我们也能大致找到突破入口,通过最后的ResultSetHandler对结果封装处理返回的时候,根据调用的getting方法的实例名称,来相对应的加载目标对象结果,不就实现了延迟加载的功能吗。

MyBatis其实就是这么做的,接下来根据翻看源码来证实我们的猜想

2.1 源码剖析


/*** MyBatis 配置** @author Clinton Begin*/
public class Configuration {/*** 当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载(参考lazyLoadTriggerMethods)*/protected boolean aggressiveLazyLoading;/*** 指定哪个对象的方法触发一次延迟加载。*/protected Set<String> lazyLoadTriggerMethods = new HashSet<>(Arrays.asList("equals", "clone", "hashCode", "toString"));/*** 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。*/protected boolean lazyLoadingEnabled = false;}

Configuration的lazyLoadingEnabled对应mapper配置文件中配置fetchType标签


因为有了分析SQL执行流程的基础,所以这次直接定位到关键代码处。

DefaultResultSetHandler#handleResultSets()

此方法就是在执行完SQL查询语句后处理封装结果集的核心代码

    // 处理 {@link java.sql.ResultSet} 结果集@Overridepublic List<Object> handleResultSets(Statement stmt) throws SQLException {ErrorContext.instance().activity("handling results").object(mappedStatement.getId());// 多ResultSet的结果集合,每个 ResultSet 对应一个Object 对象。而实际上,每个Object 是List<Object> 对象。// 在不考虑存储过程的多ResultSet的情况,普通的查询,实际就一个 ResultSet ,也就是说,multipleResults最多就一个元素。final List<Object> multipleResults = new ArrayList<>();int resultSetCount = 0;// 获得首个ResultSet对象,并封装成ResultSetWrapper对象ResultSetWrapper rsw = getFirstResultSet(stmt);// 获得ResultMap数组// 在不考虑存储过程的多ResultSet的情况,普通的查询,实际就一个 ResultSet ,也就是说,resultMaps就一个元素。List<ResultMap> resultMaps = mappedStatement.getResultMaps();int resultMapCount = resultMaps.size();validateResultMapsCount(rsw, resultMapCount); // 校验while (rsw != null && resultMapCount > resultSetCount) {// 获得ResultMap对象ResultMap resultMap = resultMaps.get(resultSetCount);// 关键代码!!!处理 ResultSet ,将结果添加到multipleResults中handleResultSet(rsw, resultMap, multipleResults, null);// 获得下一个 ResultSet 对象,并封装成 ResultSetWrapper 对象rsw = getNextResultSet(stmt);// 清理cleanUpAfterHandlingResultSet();// resultSetCount ++resultSetCount++;}// 因为 `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++;}}// 如果是 multipleResults 单元素,则取首元素返回return collapseSingleResultList(multipleResults);}

直接看最主要的方法handleResultSet(rsw, resultMap, multipleResults, null);

这个方法主要是为了处理ResultSet,将结果添加到multipleResults中

// 处理 ResultSet ,将结果添加到 multipleResults 中private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {try {// 暂时忽略,因为只有存储过程的情况,调用该方法,parentMapping为非空if (parentMapping != null) {handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);} else {// 如果没有自定义的 resultHandler,则创建默认的 DefaultResultHandlerif (resultHandler == null) {DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);// 核心代码!!! 处理ResultSet返回的每一行RowhandleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);// 添加defaultResultHandler的处理的结果,到multipleResults中multipleResults.add(defaultResultHandler.getResultList());} else {//核心代码!!! 处理ResultSet返回的每一行 RowhandleRowValues(rsw, resultMap, resultHandler, rowBounds, null);}}} finally {// issue #228 (close resultsets)// 关闭 ResultSet 对象closeResultSet(rsw.getResultSet());}}

由handleRowValues方法处理ResultSet返回的每一行Row,调用了createResultObject()方法创建结果对象,就是在这个方法中完成了懒加载相关处理逻辑。

 // 创建映射后的结果对象private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {// useConstructorMappings ,表示是否使用构造方法创建该结果对象。此处将其重置this.useConstructorMappings = false; // reset previous mapping resultfinal List<Class<?>> constructorArgTypes = new ArrayList<>(); // 记录使用的构造方法的参数类型的数组final List<Object> constructorArgs = new ArrayList<>(); // 记录使用的构造方法的参数值的数组// 创建映射后的结果对象Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {// 如果有内嵌的查询,并且开启延迟加载,则创建结果对象的代理对象final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();for (ResultMapping propertyMapping : propertyMappings) {// issue gcode #109 && issue #149if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy())  {resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);break;}}}// 判断是否使用构造方法创建该结果对象this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty(); // set current mapping resultreturn resultObject;}

对上述代码做一些精简方便找到重点

  // 创建映射后的结果对象private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {//省略代码if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {// 如果有内嵌的查询,并且开启延迟加载,则创建结果对象的代理对象final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();for (ResultMapping propertyMapping : propertyMappings) {// issue gcode #109 && issue #149//如果当前开启懒加载配置if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {//创建代理对象resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);break;}}}// 判断是否使用构造方法创建该结果对象this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty(); // set current mapping resultreturn resultObject;}

其实定位到这里,就拨开了一些云雾,在创建映射后的结果对象流程中会判断是否开启了延迟加载配置,如果开启了延迟加载配置会创建一个代理对象,将结果返回。

那么程序如何知道是否开启了延迟加载的配置呢?

通过ResultMapping#lazy方法

而ResultMapping正是每一个结果字段的映射

为什么需要创建一个代理对象呢?

这里我们做一个猜想,在章节1.3中的单元测试中为了验证延迟加载功能。

    /*测试 延迟加载查询*/
@Test
public void TEST_QUERY_LAZY()throws IOException{InputStream inputStream=Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory factory=new SqlSessionFactoryBuilder().build(inputStream);SqlSession sqlSession=factory.openSession();User user=sqlSession.selectOne("com.mryan.mapper.IUserMapper.findById",1);//下方输出语句 不涉及到orders表 于是不会打印orders相关日志System.out.println("user:"+user.getUsername());//延迟加载生效 涉及orders表 才会执行相关SQL语句 加载orders执行日志 (延迟加载 什么时候用什么时候查)System.out.println("orders:"+user.getOrderList());}

在执行完sqlSession.selectOne语句之后,如果创建了一个代理对象,动态的判断代理对象的getting方法是否是需要延迟加载的局部属性,例如getOrderList被判定延迟加载配置生效进入代理对象拦截器方法,那么就会单独发送实现保存好的查询语句将orderList查询上来,然后通过反射将orderList设置进去,然后完成getorderList的调用,实际上我们猜测的延迟加载的基本原理,

总结一下猜想:延迟加载通过动态代理实现,通过代理拦截指定getting方法,执行数据加载。

那么MyBatis是如何处理的呢?如下:

在这里插入图片描述

MyBatis代理工厂接口的两种实现,其中默认使用的是Javassits模式,具体实现如下:

/*** 代理工厂接口** @author Eduardo Macarron*/
public interface ProxyFactory {// 设置属性,目前是空实现, 暂时跳过void setProperties(Properties properties);// 创建代理对象Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs);}
/*** 基于 Javassist 的 ProxyFactory 实现类** @author Eduardo Macarron*/
@SuppressWarnings("Duplicates")
public class JavassistProxyFactory implements org.apache.ibatis.executor.loader.ProxyFactory {@Overridepublic Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {return EnhancedResultObjectProxyImpl.createProxy(target, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);}}

代理类的拦截方法如下(省略无关代码方便阅读):

 @Overridepublic Object invoke(Object enhanced, Method method, Method methodProxy, Object[] args) throws Throwable {if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) {// 加载所有延迟加载的属性if (aggressive || lazyLoadTriggerMethods.contains(methodName)) {lazyLoader.loadAll();// 如果调用了 setting 方法,则不在使用延迟加载} else if (PropertyNamer.isSetter(methodName)) {final String property = PropertyNamer.methodToProperty(methodName);lazyLoader.remove(property); // 移除// 如果调用了 getting 方法,则执行延迟加载} else if (PropertyNamer.isGetter(methodName)) {final String property = PropertyNamer.methodToProperty(methodName);if (lazyLoader.hasLoader(property)) {lazyLoader.load(property);}}}}}// 继续执行原方法return methodProxy.invoke(enhanced, args);} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}}

在拦截方法中,判断了当前执行的方法是否拥有延迟加载属性,如果启用了延迟加载并且是getting方法,那么执行load加载方法,如果不是则继续执行原方法逻辑。

而load加载方法其实也就是去执行一次SQL查询方法将结果set到代理对象中

   public void load(final Object userObject) throws SQLException {if (this.metaResultObject == null || this.resultLoader == null) {//省略代码// 获得 Configuration 对象final Configuration config = this.getConfiguration();// 获得 MappedStatement 对象final MappedStatement ms = config.getMappedStatement(this.mappedStatement);// 获得对应的 MetaObject 对象this.metaResultObject = config.newMetaObject(userObject);// 创建 ResultLoader 对象this.resultLoader = new ResultLoader(config, new ClosedExecutor(), ms, this.mappedParameter,metaResultObject.getSetterType(this.property), null, null);}//关键代码!!resultLoader.loadResult();this.metaResultObject.setValue(property, this.resultLoader.loadResult());}

关键代码resultLoader.loadResult() 去主动调用了一次查询方法并将结果进行提取。

    /*** 加载结果** @return 结果*/public Object loadResult() throws SQLException {// 查询结果List<Object> list = selectList();// 提取结果resultObject = resultExtractor.extractObjectFromList(list, targetType);// 返回结果return resultObject;}

最终通过setValue方法将加载结果设置到代理对象中。

到此整个延迟加载的流程就完结了。

3. 总结

事实上和之前我们的猜想是一致的,

延迟加载主要就是通过动态代理模式实现,通过代理拦截指定方法,从而现用现查,将结果设置到代理对象中返回,执行数据加载。

撒花🌹🌹🌹🌹但未完结,MyBatis作为一个优秀的框架,用到了相当多的设计模式,相当优美,下一篇文章,让我们走进MyBatis的设计模式。

预告

下篇文章:🏆MyBatis设计模式

本文已收录到CodeWars系列,欢迎各位Star,持续输出高质量技术文章
链接点我!

更多技术文章,请关注公众号,让我们一起进步吧!

在这里插入图片描述

http://www.lbrq.cn/news/2549989.html

相关文章:

  • 提供购物网站建设新闻发布最新新闻
  • 做网站就是做服务成都百度推广电话号码是多少
  • 奢侈品网站设计沈阳seo网站关键词优化
  • 钦州网站建设广东企业网站seo哪里好
  • 关于做公司网站网络推广项目外包公司
  • 网站被恶意关键字访问重庆seo网络推广关键词
  • 股票专业网站seo电商运营是什么意思
  • wordpress主题 破解主题下载地址seo查询工具网站
  • 东莞阳光网appseo搜索优化公司排名
  • .net如何做网站游戏行业seo整站优化
  • 高端定制网站设计上海网络营销seo
  • 外贸公司都是在什么网站做推广镇江抖音seo
  • 厦门网站建设培训机构免费网站友情链接
  • 犀牛云做网站怎么这么贵外贸平台有哪些?
  • php网站开发 实战教程aso优化吧
  • 口碑好的网站定制公司关键词林俊杰mp3下载
  • 化妆品做网站流程百度广告投放收费标准
  • 上海自助建站企业免费b2b网站推广渠道
  • 购物网站的首页是静态百度广告太多
  • 做网站弄关键词多少钱禁止搜索引擎收录的方法
  • 动漫视频网站开发网上引流推广怎么做
  • 修改wordpress标签大小seo权重优化软件
  • 温州建设网站公司搜索引擎优化英文简称
  • 贸易公司寮步网站建设太原网络推广公司
  • wordpress手机端菜单设置哪里能搜索引擎优化
  • 石家庄手机网站制作多少钱游戏代理300元一天
  • 做内贸哪个网站好网络广告投放公司
  • 深圳招聘网官方网站杭州余杭区抖音seo质量高
  • 青岛中嘉建设集团网站百度提交工具
  • 佛山网站建设推广厂商排名西安百度推广电话
  • Kafka——关于Kafka动态配置
  • 驾驶场景玩手机识别:陌讯行为特征融合算法误检率↓76% 实战解析
  • FFmpeg+javacpp中纯音频播放
  • 【python】转移本地安装的python包
  • 面试实战,问题二十二,Java JDK 17 有哪些新特性,怎么回答
  • Kubernetes Gateway API 详解:现代流量路由管理方案