首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

取舍缓存,让ibatis不必每次都查询

2012-10-26 
选择缓存,让ibatis不必每次都查询缓存,顾名思义,就是将从数据库中查询出来的数据在某个缓冲区域暂时保存起

选择缓存,让ibatis不必每次都查询

缓存,顾名思义,就是将从数据库中查询出来的数据在某个缓冲区域暂时保存起来,在需要数据的时候从该缓冲区中读取,而不是从数据库中读取,从而减少对数据库访问次数,达到减少系统开销,提高性能的目的。

?

在本文中,我将结合实例讲述ibatis的缓存使用及相关原理。

首先我们来看一个ibatis应用所需要的配置文件:
(注:由于我们只关注ibatis的缓存,所以在ibatis的配置文件中我们只讨论与缓存相关的配置,其它的配置我们将省略!)

?

1.sql-map的配置,查看配置文件的dtd声明:

<!ELEMENT sqlMap (typeAlias* | cacheModel* | resultMap* | parameterMap* | sql* | statement* | insert* | update* | delete* | select* | procedure*)+>

以上是sql-map配置文件可以使用的元素,其中有些元素还有子元素及属性,这些都可以通过查看ibatis的dtd文件来知晓。在这个dtd声明中有一个cacheModel元素特别耀眼,相信读者已经猜测出来了,的确,该元素即为缓存模型,再看看该元素的子元素及属性:

<!ELEMENT cacheModel (flushInterval?, flushOnExecute*, property*)+><!ATTLIST cacheModelid CDATA #REQUIREDtype CDATA #REQUIREDreadOnly (true | false) #IMPLIEDserialize (true | false) #IMPLIED>

?再看每个子元素的相关属性:

<!ELEMENT flushInterval EMPTY><!ATTLIST flushIntervalmilliseconds CDATA #IMPLIEDseconds CDATA #IMPLIEDminutes CDATA #IMPLIEDhours CDATA #IMPLIED><!ELEMENT flushOnExecute EMPTY><!ATTLIST flushOnExecutestatement CDATA #REQUIRED><!ELEMENT property EMPTY><!ATTLIST propertyname CDATA #REQUIREDvalue CDATA #REQUIRED>

于是,通过查看ibatis的dtd声明,我们即可得知在ibatis中是如何配置缓存管理的,将以上信息连接起来即可得到如下一段配置:

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE sqlMap          PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"          "http://ibatis.apache.org/dtd/sql-map-2.dtd"><sqlMap namespace="User"><cacheModel id="user-cache" type ="LRU" readOnly="true" serialize="false">   <flushInterval hours="24"/>   <flushOnExecute statement=” updateUser”/>   <flushOnExecute statement="insertUser"/>   <flushOnExecute statement="deleteUser"/>   <property value="500" name="size"/></cacheModel><!—其他配置信息 -->。。。。。。。。。。</sqlMap>

那么这些配置都是什么含义呢?继续:
id:一个标识,在下面的select语句中将引用该标识。

?

Type: cacheModel的实现类型,目前有如下4种实现:
(1). "MEMORY” (com.ibatis.sqlmap.engine.cache.memory.MemoryCacheController) ,MEMORY cache 实现使用java的软引用类型来管理cache 的行为,使用一个HashMap来保存当前需要缓存的数据对象的引用,当内存不足时,java虚拟机将回收这些引用,从而清除cache。
(2).“LRU” (com.ibatis.sqlmap.engine.cache.lru.LruCacheController) ,LRU Cache 实现用“近期最少使用”原则来确定如何从Cache中清除对象,当Cache溢出时,最近最少使用的对象将被从Cache中清除。
(3).“FIFO” (com.ibatis.sqlmap.engine.cache.fifo.FifoCacheController) ,FIFO Cache 实现用“先进先出”原则来确定如何从 Cache 中清除对象。即最先进入 Cache 的对象将从 Cache 中清除。
(4).“OSCACHE” (com.ibatis.sqlmap.engine.cache.oscache.OSCacheController)? ,OSCACHE Cache 实现是OSCache2.0缓存引擎的一个Plugin,它具有高度的可配置性,分布式,高度的灵活性(很推荐使用该类型)OSCache可以通过oscache.properties文件进行缓存的相关配置。

?

readOnly:readOnly的值表示缓存中的数据对象是否只读。若为true,则当数据对象发生变化时,数据对象就将被从缓存中废除,下次需要重新从数据库读取数据,构造新的数据对象。而若为false,则意味着缓存中的数据对象可更新,不必从数据库中读取。

?

serialize:如果需要全局的数据缓存,CacheModel的serialize属性必须被设为true。否则数据缓存只对当前Session有效,局部缓存对系统的整体性能提升有限。在serialize="true"的情况下,如果有多个Session同时从Cache 中读取某个数据对象,Cache将为每个Session返回一个对象的复本,也就是说,每个Session将得到包含相同信息的不同对象实例。因而Session可以对其从Cache获得的数据进行存取而无需担心多线程并发情况下的同步冲突。

?

<flushInterval hours=”24”>:指定多长时间清除缓存,例如指定每24小时强行清空缓存区的所有内容。

<flushOnExecute statement="insertUser"/>:在执行指定的语句时将刷新数据库。


Size:指定Cache的最大容量。

?

通过在一个SQL Map XML file配置以上信息,我们就可以在一个查询中使用该缓存管理,配置如下:

<select id=”getUserList” resultMap="UserResult" cacheModel=”user-cache”>select * from USER</select>

2.sql-map-config的配置,查看配置文件的dtd声明:

<!ELEMENT sqlMapConfig (properties?, settings?, resultObjectFactory?, typeAlias*, typeHandler*, transactionManager?, sqlMap+)+>

在这个dtd的声明中,有一个settings元素,通过继续查看该元素的属性:

<!ELEMENT settings EMPTY><!ATTLIST settingsclassInfoCacheEnabled (true | false) #IMPLIEDlazyLoadingEnabled (true | false) #IMPLIEDstatementCachingEnabled (true | false) #IMPLIEDcacheModelsEnabled (true | false) #IMPLIEDenhancementEnabled (true | false) #IMPLIEDerrorTracingEnabled (true | false) #IMPLIEDuseStatementNamespaces (true | false) #IMPLIEDuseColumnLabel (true | false) #IMPLIEDforceMultipleResultSetSupport (true | false) #IMPLIEDmaxSessions CDATA #IMPLIEDmaxTransactions CDATA #IMPLIEDmaxRequests CDATA #IMPLIEDdefaultStatementTimeout CDATA #IMPLIED>

显然,属性cacheModelsEnabled就表示是否启用SqlMapClient上的缓存机制。将其设为"true"则标识启用缓存机制,反之则不启用。

?

通过上面对两个配置文件的分析,我们已经对在ibatis中如何使用缓存有了大致的了解和认识,下面我们将通过一个实例来演示ibatis缓存的使用并检验ibatis的缓存是否已经起作用。

?

我们的测试方法如下:
1.将数据库中预先插入一些数据(通过控制台或数据库客户端插入数据),然后利用编写好的程序访问数据库,第一次访问数据库时将查询出所有的数据,但当我们通过控制台或数据库客户端(如mysql客户端)再向数据库中插入一些数据,这时再通过编写好的程序去访问数据库,发现通过控制台或数据库客户端新插入的数据并没有被查询出来,说明ibatis读取的是第一次查询所保存在缓存中的数据,这说明测试成功!
2.当我们使用程序而不是控制台或客户端再向数据库插入数据,同时又通过程序访问数据库时,新插入数据库的数据都被查询了出来,而不是从缓存中读取,这说明测试成功,因为我们配置了<flushOnExecute statement="insertUser" />当插入数据时将刷新数据库。

?

下面就开始我们的测试:
1.建立一个web工程,导入相关的jar包;
2.编写具体代码;
3.做缓存测试;

?

sql-map文件的配置:User.xml

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE sqlMap          PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"          "http://ibatis.apache.org/dtd/sql-map-2.dtd"><sqlMap namespace="User"><typeAlias alias="User" type="com.javaeye.hnylj.model.User" /><!-- 配置缓存模型 --><cacheModel id="user-cache" type="OSCache" readOnly="true"serialize="true"><flushInterval hours="24" /><flushOnExecute statement="insertUser" /><property value="500" name="size" /></cacheModel><resultMap id="UserResult" column="ID" /><result property="name" column="NAME" /><result property="age" column="AGE" /></resultMap><select id="selectAllUser" resultMap="UserResult"cacheModel="user-cache">SELECT * FROM USER</select><insert id="insertUser" parametername="code"><?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE sqlMapConfig          PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"          "http://ibatis.apache.org/dtd/sql-map-config-2.dtd"><sqlMapConfig><settings lazyLoadingEnabled="true" cacheModelsEnabled="true"maxSessions="30" maxTransactions="100" maxRequests="1000"defaultStatementTimeout="15" /><transactionManager type="JDBC" commitRequired="false"><dataSource type="SIMPLE"><property name="JDBC.Driver" value="com.mysql.jdbc.Driver" /><property name="JDBC.ConnectionURL"value="jdbc:mysql://127.0.0.1:3306/ibatis" /><property name="JDBC.Username" value="root" /><property name="JDBC.Password" value="123" /></dataSource></transactionManager><sqlMap resource="com/javaeye/hnylj/model/User.xml" /></sqlMapConfig>

接下来就是编写DAO,Action以及jsp页面,在这里,Action层使用了struts2。

Dao代码:UserDAO

package com.javaeye.hnylj.dao;import java.io.IOException;import java.io.Reader;import java.sql.SQLException;import java.util.List;import com.ibatis.common.resources.Resources;import com.ibatis.sqlmap.client.SqlMapClient;import com.ibatis.sqlmap.client.SqlMapClientBuilder;import com.javaeye.hnylj.model.User;public class UserDAO {private static SqlMapClient sqlMapper;static {try {Reader reader = Resources.getResourceAsReader("com/javaeye/hnylj/model/SqlMapConfig.xml");sqlMapper = SqlMapClientBuilder.buildSqlMapClient(reader);reader.close();} catch (IOException e) {throw new RuntimeException("building the SqlMapClient instance error."+ e, e);}}public List<?> getAllUser() throws SQLException {List<?> list = sqlMapper.queryForList("selectAllUser");System.out.println(list.size());return list;}public void insertUser(User user) throws SQLException {sqlMapper.insert("insertUser", user);}}

Action代码:UserAction

package com.javaeye.hnylj.action;import java.util.List;import com.javaeye.hnylj.dao.UserDAO;import com.javaeye.hnylj.model.User;import com.opensymphony.xwork2.ActionSupport;public class UserAction extends ActionSupport {private static final long serialVersionUID = 4689260572038875931L;private UserDAO userDAO;private List<User> userList;private Integer id;private String name;private Integer age;private String password;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public List<User> getUserList() {return userList;}public void setUserList(List<User> userList) {this.userList = userList;}public String findAllUsers() throws Exception {userDAO = new UserDAO();userList = (List<User>)userDAO.getAllUser(); if (userList.size() > 0) {return SUCCESS;}return ERROR;}public String add() throws Exception {userDAO = new UserDAO();User user = new User();user.setName(name);user.setAge(age);user.setPassword(password);userDAO.insertUser(user);return SUCCESS;}}

?

还有其他一些代码或配置,在此省略!

将工程部署成功以后,按照上面的测试方法即可进行测试!

?

本人有一个疑惑,ibatis如何使用ehcache进行缓存管理呢?ibatis好像不支持ehcache缓存,只提供oscache的支持!

据我所知:

oscache 主要是针对页面的缓存,例如将整个页面或者指定网页某一部分缓存起来,同时指定它何时过期。

ehcache主要是针对数据库访问的缓存,相同的查询语句只需查询一次数据库,从而提高性能。

著名的hibernate默认使用的是ehcache,当然也可以使用oscache!

?

欢迎讨论交流,我的博客地址http://hnylj.iteye.com。

?

写的不错 3 楼 csuzm0613 2010-12-11   很不错,曾经用ibatis做过项目,缓存还没用过,悲剧。。。

热点排行