`

iBATIS缓存

 
阅读更多

 

为了提高应用程序性能,一种比较通用的方法是使用缓存技术来减少与数据库之间的交互。缓存技术是一种“以空间换时间”的设计理念,利用内存空间资源来提高数据检索速度的有效手段之一。

     iBATIS以一种简单、易用、灵活的方式实现了数据缓存。下面,首先看一下iBATIS关于缓存部分的核心类图:

 


 

 

 

关于这些类的用途,在注释中做了比较概括性的说明,下面就来仔细的讲一下这些类的用途以及它们是如何工作的。

     在iBATIS中,可以配置多个缓存,每个cacheModel的配置对应一个CacheModel类的一个对象。其中包括id等配置信息。iBATIS通过这些配置信息来定义缓存管理的行为。

     缓存的目的是为了能够实现数据的高速检索。在程序中,数据是用对象表示的;为了能够检索到以缓存的数据对象,每个数据对象必须拥有一个唯一标识,在iBATIS中,这个唯一标识用CacheKey来表示。

     那么,缓存的数据保存到什么地方了呢?如何实现数据的快速检索呢?答案在CacheController的实现类中。每个CacheController中都有一个Map类型的属性cache来保存被缓存的数据,其中key为CacheKey类型,value为Object类型;需要关注的是CacheKey对象的hashCode的生成算法,每次调用CacheKey对象的update方法时,都会更新它的hashCode值,关于hashCode值的计算方法后续在给出详细说明。

     在拥有了数据缓存区后,就可以向其中存放数据和检索数据了。在iBATIS中,有多种的缓存管理策略,也可以自定义缓存管理策略。

     关于缓存的功能,主要有两种类型:一种是对外提供的功能:数据存储和数据检索;另外一种是内部管理的功能:缓存对象标识的生成,缓存区刷新,数据检索算法等。下面就逐一介绍这些功能的代码实现。

     1. 数据存储

         首先看一下CacheModel中的putObject方法是如何实现的

 

         public void putObject(CacheKey key, Object value) {

    if (null == value) value = NULL_OBJECT;  
    //关于缓存的操作,需要互斥   
    synchronized ( this )  {  
     if (serialize && !readOnly && value != NULL_OBJECT) {  
       //需要序列化,并且非只读,则需要将缓存对象序列化到内存,以供后续检索使用   
        //readOnly为false时,不能直接将对象引用直接返回个客户程序   
       try {  
         ByteArrayOutputStream bos = new ByteArrayOutputStream();  
         ObjectOutputStream oos = new ObjectOutputStream(bos);  
         oos.writeObject(value);  
         oos.flush();  
         oos.close();  
         value = bos.toByteArray();  
       } catch (IOException e) {  
         throw new RuntimeException("Error caching serializable object.  Cause: " + e, e);  
       }  
     }  
     //如果执行了内存序列化,则保存的是它的字节数组   
     controller.putObject(this, key, value);  
     if ( log.isDebugEnabled() )  {  
       log("stored object", true, value);  
     }  
   }  
 }  
  

因为真正缓存数据对象的地方是在CacheController中,所以CacheModel的putObject方法中会调用CacheController的putObject方法执行真正的数据存储。由于不同的CacheController实现的缓存管理方式不同,所以putObject实现也各不相同。下面分别介绍不同的CacheController实现的putObject方法

1) FifoCacheController

     public void putObject(CacheModel cacheModel, Object key, Object value) {

  //保存到Map中   
  cache.put(key, value);  
  //保存key到keyList   
  keyList.add(key);  
  //如果当前key的数量大于缓存容量时,移除keyList和cache中的第一个元素,达到先进先出的目的   
  if (keyList.size() > cacheSize) {  
    try {  
      Object oldestKey = keyList.remove(0);  
      cache.remove(oldestKey);  
    } catch (IndexOutOfBoundsException e) {  
      //ignore   
    }  
  }  
}  
 

2)LruCacheController

 

public void putObject(CacheModel cacheModel, Object key, Object value) {  
  cache.put(key, value);  
  keyList.add(key);  
  if (keyList.size() > cacheSize) {  
    try {  
      //取得keyList中的第一个元素作为最近最少用的key,为什么呢?   
       //这个问题等到讲解它的getObject方法时别会知晓   
      Object oldestKey = keyList.remove(0);  
      cache.remove(oldestKey);  
    } catch (IndexOutOfBoundsException e) {  
      //ignore   
    }  
  }  
}
 

3)MemoryCacheController

 

public void putObject(CacheModel cacheModel, Object key, Object value) {  
  Object reference = null;  
  //根据配置创建响应的引用类型,此种缓存管理方式完全交给jvm的垃圾回收器来管理   
   //创建好引用后,将数据对象放入到引用中   
  if (referenceType.equals(MemoryCacheLevel.WEAK)) {  
    reference = new WeakReference(value);  
  } else if (referenceType.equals(MemoryCacheLevel.SOFT)) {  
    reference = new SoftReference(value);  
  } else if (referenceType.equals(MemoryCacheLevel.STRONG)) {  
    reference = new StrongReference(value);  
  }  
  //在缓存中保存引用   
  cache.put(key, reference);  
} 
 4)OSCacheController

      这个缓存管理使用了OSCache来管理缓存,这里就不做仔细的介绍了。

 

2. 数据检索

     在数据被放置到缓存区中以后,程序需要根据一定的条件进行数据检索。首先看一下CacheModel类的getObject方法是如何检索数据的

public Object getObject(CacheKey key) {  
    Object value = null;  
  //互斥访问缓冲区   
  synchronized (this) {  
    if (flushInterval != NO_FLUSH_INTERVAL  
        && System.currentTimeMillis() - lastFlush > flushInterval) {  
      //如果到了定期刷新缓冲区时,则执行刷新   
      flush();  
    }  
    //根据key来从CacheController中取得数据对象   
    value = controller.getObject(this, key);  
    if (serialize && !readOnly &&  
            (value != NULL_OBJECT && value != null)) {  
      //如果需要序列化,并且非只读,则从内存中序列化出一个数据对象的副本   
      try {  
        ByteArrayInputStream bis = new ByteArrayInputStream((byte[]) value);  
        ObjectInputStream ois = new ObjectInputStream(bis);  
        value = ois.readObject();  
        ois.close();  
      } catch (Exception e) {  
        throw new RuntimeException("Error caching serializable object.  Be sure you're not attempting to use " +  
                                         "a serialized cache for an object that may be taking advantage of lazy loading.  Cause: " + e, e);  
      }  
    }  
    //下面的两个操作是用来计算缓存区数据检索的命中率的   
    //对于缓冲区的数据检索请求加一操作   
    requests++;  
    //如果检索到数据,则命中数加一   
    if (value != null) {  
      hits++;  
    }  
    if ( log.isDebugEnabled() )  {  
        if ( value != null )  {  
          log("retrieved object", true, value);  
        }  
        else  {  
            log("cache miss", false, null);  
        }  
    }  
  }  
  return value;  
} 
 真正的数据检索操作是在CacheController的实现类中进行的,下面就分别来看一下各个实现类是如何检索数据的。

 1) FifoCacheController

public Object getObject(CacheModel cacheModel, Object key) {  
  //直接从Map中取得   
  return cache.get(key);  
} 
 

2) LruCacheController

 

public Object getObject(CacheModel cacheModel, Object key) {  
  Object result = cache.get(key);  
  //因为这个key被使用了,如果检索到了数据,则将其移除并重新放置到队尾   
   //这样的目的就是保持最近使用的key放在队尾,而对头为最近未使用的   
   //如果没有检索到对象,则直接将该key移除   
  keyList.remove(key);  
  if (result != null) {  
    keyList.add(key);  
  }  
  return result;  
}  
 

3) MemoryCacheController

 

public Object getObject(CacheModel cacheModel, Object key) {  
  Object value = null;  
  //取得引用对象   
  Object ref = cache.get(key);  
  if (ref != null) {  
    //从引用对象中取得数据对象   
    if (ref instanceof StrongReference) {  
      value = ((StrongReference) ref).get();  
    } else if (ref instanceof SoftReference) {  
      value = ((SoftReference) ref).get();  
    } else if (ref instanceof WeakReference) {  
      value = ((WeakReference) ref).get();  
    }  
  }  
  return value;  
}  
 

3 唯一标识的生成

      在iBATIS中,用CacheKey来标识一个缓存对象,而CacheKey通常是作为Map中的key存在,所以CacheKey的hashCode的计算方法异常重要。影响hashCode的值有很多方面的因素,对每一个影响hashCode的元素,都需要调用CacheKey的update方法来重新计算hashCode值。下面我们就来看一下CacheKey的创建以及计算的相关过程。

      首先CacheKey是在BaseDataExchange类的getCacheKey方法中被创建的。

 

public CacheKey getCacheKey(StatementScope statementScope, ParameterMap parameterMap, Object parameterObject) {  
  CacheKey key = new CacheKey();  
  //取得parameterObject中的数据,这个parameterObject就是客户端传递过来的参数对象   
  Object[] data = getData(statementScope, parameterMap, parameterObject);  
  //根据parameterObject中的数据去重计算hashCode   
  for (int i = 0; i < data.length; i++) {  
    if (data[i] != null) {  
      key.update(data[i]);  
    }  
  }  
  return key;  
}  
 

这个方法被MappedStatement中的getCacheKey调用

 

public CacheKey getCacheKey(StatementScope statementScope, Object parameterObject) {  
  Sql sql = statementScope.getSql();  
  ParameterMap pmap = sql.getParameterMap(statementScope, parameterObject);  
  CacheKey cacheKey = pmap.getCacheKey(statementScope, parameterObject);  
  //statement id对hashCode有影响   
  cacheKey.update(id);  
  cacheKey.update(baseCacheKey);  
  //sql语句对hashCode有影响   
  cacheKey.update(sql.getSql(statementScope, parameterObject)); //Fixes bug 953001   
  return cacheKey;  
}
 

真正需要CacheKey对象的地方是在CacheStatement类中

 

public CacheKey getCacheKey(StatementScope statementScope, Object parameterObject) {  
  CacheKey key = statement.getCacheKey(statementScope, parameterObject);  
  //如果不可读并且不被序列化,那么当前的SessionScope也对hashCode有影响   
   //而真正起作用的是SessionScope的id属性   
   //也就是说这个缓存与调用线程的会话有关,当前线程所存储的数据不能被其他线程使用   
  if (!cacheModel.isReadOnly() && !cacheModel.isSerialize()) {  
    key.update(statementScope.getSession());  
  }  
  return key;  
} 
 

经过上述一系列的getCacheKey调用,将对CacheKey有影响的因素施加给了hashCode。其中对CacheKey的hashCode起影响作用的因素主要有:baseCacheKey,sql语句,参数值,statement id。可能产生影响的因素是session id。

    现在我们知道了决定CacheKey的相关因素,也就知道了iBATIS是如何唯一的确定一个缓存对象。

    经过以上的代码分析,可以掌握iBatis如何生成CacheKey对象和计算其hashCode值,以及存储和检索数据对象。这些正是iBATIS缓存的基础,掌握了这些实现原理,有助于我们更高效的使用iBATIS缓存功能,或者是开发自己的缓存系统。

 

===========原文:http://blog.csdn.net/prince2270/article/details/5977512======

 

 

具体配置参考:

http://exceptioneye.iteye.com/blog/1054351http://exceptioneye.iteye.com/blog/1054355

 

 

 

 

  • 大小: 109 KB
分享到:
评论

相关推荐

    034-基于AT89C52的矩阵键盘扫描proteus仿真设计.rar

    51单片机

    双级式储能模型,可做充放电转以及低电压故障穿越,含有负序抑制模块,可做对称故障与不对称故障

    双级式储能模型,可做充放电转以及低电压故障穿越,含有负序抑制模块,可做对称故障与不对称故障

    郑州升达大学2024-2025第一学期计算机视觉课程期末试卷,

    郑州升达大学2024-2025第一学期计算机视觉课程期末试卷,原版。配套教材为《OpenCV计算机视觉基础教程》夏帮贵主编。

    金工实习线上考试线切割课后试题.docx

    线切割课后试题

    网络原理课程设计【校园网规划】+思科模拟器,包含pkt文件及完整实验报告,附录含有源码

    目录 摘 要 1 一、设计任务概述 3 1.1 设计目的 3 1.2 项目任务和要求 3 1.3 参考资料 3 二、项目开发环境 4 三、项目需求分析 5 四、 项目设计和实现 5 4.1 总体设计 5 4.2 功能设计 6 4.3 系统实现 7 五、系统运行和测试 12 六、设计总结 15 七、附录 16 7.1 程序清单 16 7.2 其他需要说明的内容 23。内容来源于网络分享,如有侵权请联系我删除。另外如果没有积分的同学需要下载,请私信我。

    智慧物联网系统发展战略研究

    智慧物联网系统发展战略研究

    基于springboot+vue的大创管理系统2(Java毕业设计,附源码,部署教程).zip

    该项目包含完整的前后端代码、数据库脚本和相关工具,简单部署即可运行。功能完善、界面美观、操作简单,具有很高的实际应用价值,非常适合作为Java毕业设计或Java课程设计使用。 所有项目均经过严格调试,确保可运行!下载后即可快速部署和使用。 1 适用场景: 毕业设计 期末大作业 课程设计 2 项目特点: 代码完整:详细代码注释,适合新手学习和使用 功能强大:涵盖常见的核心功能,满足大部分课程设计需求 部署简单:有基础的人,只需按照教程操作,轻松完成本地或服务器部署 高质量代码:经过严格测试,确保无错误,稳定运行 3 技术栈和工具 前端:HTML + Vue.js 后端框架:Spring Boot 开发环境:IntelliJ IDEA 数据库:MySQL(建议使用 5.7 版本,更稳定) 数据库可视化工具:Navicat 部署环境:Tomcat(推荐 7.x 或 8.x 版本),Maven

    基于springboot+vue的网上点餐系统(Java毕业设计,附源码,部署教程).zip

    该项目包含完整的前后端代码、数据库脚本和相关工具,简单部署即可运行。功能完善、界面美观、操作简单,具有很高的实际应用价值,非常适合作为Java毕业设计或Java课程设计使用。 所有项目均经过严格调试,确保可运行!下载后即可快速部署和使用。 1 适用场景: 毕业设计 期末大作业 课程设计 2 项目特点: 代码完整:详细代码注释,适合新手学习和使用 功能强大:涵盖常见的核心功能,满足大部分课程设计需求 部署简单:有基础的人,只需按照教程操作,轻松完成本地或服务器部署 高质量代码:经过严格测试,确保无错误,稳定运行 3 技术栈和工具 前端:HTML + Vue.js 后端框架:Spring Boot 开发环境:IntelliJ IDEA 数据库:MySQL(建议使用 5.7 版本,更稳定) 数据库可视化工具:Navicat 部署环境:Tomcat(推荐 7.x 或 8.x 版本),Maven

    直流电机的电枢回路串电阻启动的计算

    电机与拖动技术三级项目报告,直流电动机是电机的主要类型之一,具有调速范围广、调速特性平滑、过载能力强等优点,在生产生活中具有广泛的应用。此次课程项目阐述了直流电动机的结构、应用、并着重对电枢回路串电阻分级启动进行深入研究,MATLAB仿真软件对直流电动机分级启动进行仿真。

    Java Spring Boot实现基于URL + IP访问频率限制(源代码)

    详细说明:https://blog.csdn.net/a342874650/article/details/144989766 在 Web 应用中,恶意用户可能会通过频繁刷新接口或进行暴力请求来攻击系统,导致服务器负载过高或服务不可用。为了应对这一问题,本文将详细介绍如何使用 Spring Boot 结合拦截器(Interceptor)和 Redis 来实现基于 URL 和 IP 的访问频率限制。具体实现包括拦截器拦截请求、Redis 存储访问记录、检测访问频率并在达到限制时禁用 IP 的完整过程。通过本文的详细实现过程和完整源代码,读者可以快速掌握如何在自己的项目中应用这一机制来增强系统的安全性和稳定性。

    JavaEE核心技术:Web框架与持久层设计方案解析(主观题考试题库)

    内容概要:本文详细介绍了JavaEE核心技术,涵盖多个重要的Web框架和持久层技术,以及其应用场景和实施方案。具体内容包括:①Struts框架的特点和功能,特别是其对MVC架构的支持,以及如何应用于薪资管理系统;②MVC架构的基本概念和如何通过JSP、JavaBean及Servlet实现成绩管理系统;③Spring IoC容器的工作原理,强调其控制反转和依赖注入功能,展示了整合Struts和JPA的具体案例,如通讯管理系统Web层设计方案;④Spring MVC结构及其XML配置方法,并提出一种针对图书管理系统的Spring MVC实现思路;⑤深入探讨Spring AOP原理,介绍如何使用XML配置进行统一事务处理的应用方案;⑥分析Hibernate核心接口及设备管理系统持久层设计方案;⑦整合Hibernate和Spring IoC实现的成绩管理系统持久层设计方案。 适合人群:具备一定Java基础的初、中级JavaEE开发者,对JavaWeb开发有兴趣的学习者。 使用场景及目标:①帮助开发者理解JavaEE关键技术和框架的实际运用,提高项目开发技能;②指导实际项目的架构设计和技术选型;③促进团队协作,提高代码复用性和维护效率。 阅读建议:建议读者根据自身经验和兴趣选择重点章节仔细研读,并结合实际情况尝试实践,逐步掌握各知识点。此外,还应该结合最新的API文档和技术论坛资料不断跟进更新。

    easy-interceptor修改请求头和响应头.zip

    easy-interceptor修改请求头和响应头.zip

    Prime-Series-Level-1.z10

    Prime_Series_Level-1.z10 别下,这个是分卷压缩,笔者用来备份的

    基于springboot+vue的教师工作量管理系统(Java毕业设计,附源码,部署教程).zip

    该项目包含完整的前后端代码、数据库脚本和相关工具,简单部署即可运行。功能完善、界面美观、操作简单,具有很高的实际应用价值,非常适合作为Java毕业设计或Java课程设计使用。 所有项目均经过严格调试,确保可运行!下载后即可快速部署和使用。 1 适用场景: 毕业设计 期末大作业 课程设计 2 项目特点: 代码完整:详细代码注释,适合新手学习和使用 功能强大:涵盖常见的核心功能,满足大部分课程设计需求 部署简单:有基础的人,只需按照教程操作,轻松完成本地或服务器部署 高质量代码:经过严格测试,确保无错误,稳定运行 3 技术栈和工具 前端:HTML + Vue.js 后端框架:Spring Boot 开发环境:IntelliJ IDEA 数据库:MySQL(建议使用 5.7 版本,更稳定) 数据库可视化工具:Navicat 部署环境:Tomcat(推荐 7.x 或 8.x 版本),Maven

    CST0402B+跟岗实习提交资料.zip

    CST0402B+跟岗实习提交资料.zip

    基于yolov5的医学影像肺结节检测项目源码+文档说明(高分项目)

    基于yolov5的医学影像肺结节检测项目源码+文档说明(高分项目),个人大三大设计项目、经导师指导并认可通过的高分设计项目,评审分99分,代码完整确保可以运行,小白也可以亲自搞定,主要针对计算机相关专业的正在做毕设的学生和需要项目实战练习的学习者,也可作为毕业设计、课程设计、期末大作业。 基于yolov5的医学影像肺结节检测项目源码+文档说明(高分项目)基于yolov5的医学影像肺结节检测项目源码+文档说明(高分项目)基于yolov5的医学影像肺结节检测项目源码+文档说明(高分项目)基于yolov5的医学影像肺结节检测项目源码+文档说明(高分项目)基于yolov5的医学影像肺结节检测项目源码+文档说明(高分项目)基于yolov5的医学影像肺结节检测项目源码+文档说明(高分项目)基于yolov5的医学影像肺结节检测项目源码+文档说明(高分项目)基于yolov5的医学影像肺结节检测项目源码+文档说明(高分项目)基于yolov5的医学影像肺结节检测项目源码+文档说明(高分项目)基于yolov5的医学影像肺结节检测项目源码+文档说明(高分项目)基于yolov5的医学影像肺结节检测项目源码+文

    循环法和对数法计算利息

    本金1W利息0.0325,几年能double?

    matlab机械臂关节空间轨迹规划,3-5-3分段多项式插值法,六自由度机械臂,该算法可运用到仿真建模机械臂上实时运动,可视化轨迹,有角度,速度,加速度仿真曲线 也可以有单独角度,速度,加速度仿真曲

    matlab机械臂关节空间轨迹规划,3-5-3分段多项式插值法,六自由度机械臂,该算法可运用到仿真建模机械臂上实时运动,可视化轨迹,有角度,速度,加速度仿真曲线。 也可以有单独角度,速度,加速度仿真曲线。 可自行更程序中机械臂与点的参数。 谢谢大家 (程序中均为弧度制参数)353混合多项式插值

    2011-2023年各省金融监管水平数据(含原始数据+计算过程+计算结果)

    2011-2023年各省金融监管水平数据(含原始数据+计算过程+计算结果) 1、时间:2011-2023年 2、来源:国家统计J、统计NJ 3、指标:金融业增加值、金融监管支出、金融监管水平 4、计算方法:金融监管水平=金融监管支出/金融业增加值

    简易手写汉字表.pdf

    本表名称为简易手写识字表,收录了21000多个汉字,每个汉字后面附上了简易手写笔画和输入编码。独体字是一个主笔画和一个字母编码,双码字是两个主笔画组合和两个字母编码,多码字是两个主笔画组合和三个字母编码。可用于识字、简易手写和大键盘汉字输入等参考。

Global site tag (gtag.js) - Google Analytics