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 cacheModel
id CDATA #REQUIRED
type CDATA #REQUIRED
readOnly (true | false) #IMPLIED
serialize (true | false) #IMPLIED
>

再看每個子元素的相關屬性:

<!ELEMENT flushInterval EMPTY>
<!ATTLIST flushInterval
milliseconds CDATA #IMPLIED
seconds CDATA #IMPLIED
minutes CDATA #IMPLIED
hours CDATA #IMPLIED
>
<!ELEMENT flushOnExecute EMPTY>
<!ATTLIST flushOnExecute
statement CDATA #REQUIRED
>
<!ELEMENT property EMPTY>
<!ATTLIST property
name CDATA #REQUIRED
value 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 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 settings
classInfoCacheEnabled (true | false) #IMPLIED
lazyLoadingEnabled (true | false) #IMPLIED
statementCachingEnabled (true | false) #IMPLIED
cacheModelsEnabled (true | false) #IMPLIED
enhancementEnabled (true | false) #IMPLIED
errorTracingEnabled (true | false) #IMPLIED
useStatementNamespaces (true | false) #IMPLIED
useColumnLabel (true | false) #IMPLIED
forceMultipleResultSetSupport (true | false) #IMPLIED
maxSessions CDATA #IMPLIED
maxTransactions CDATA #IMPLIED
maxRequests CDATA #IMPLIED
defaultStatementTimeout 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 >
  <result property="id" column="ID" />
  <result property="name" column="NAME" />
  <result property="age" column="AGE" />
 </resultMap>

 <select resultMap="UserResult"
  cacheModel="user-cache">
  SELECT * FROM USER
 </select>

 <insert parameterClass="User">
  INSERT INTO USER ( NAME, AGE, PASSWORD) VALUES ( #name#, #age#, #password# )
 </insert>

</sqlMap>

sql-map-config文件的配置: SqlMapConfig.xml

<?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;
 }
}

還有其他一些代碼或配置,在此省略!

將工程部署成功以後,按照上面的測試方法即可進行測試!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章