`
hnylj
  • 浏览: 209773 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

选择缓存,让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 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 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 id="UserResult" class="User">
		<result property="id" 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" 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;
	}
}

 

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

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

 

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

据我所知:

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

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

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

 

一个Java技术交流群,一起交流,共同进步,扣扣群号:513086638

 

 

 

10
0
分享到:
评论
6 楼 TonyLian 2015-01-29  
只能逐个sql-map设置缓存吗?

不能从sql-map-config文件统一为所有的sql启用缓存吗?

sql-map-config中的 <settings cacheModelsEnabled="true"> 是起到什么作用呢?
5 楼 guojigjkill 2014-04-24  
111
4 楼 guojigjkill 2014-04-24  
     1
3 楼 csuzm0613 2010-12-11  
很不错,曾经用ibatis做过项目,缓存还没用过,悲剧。。。
2 楼 勇者无敌 2010-11-01  
写的不错
1 楼 yemaomao80 2009-08-18  
不错,目前正在做一个ibatis的项目,正好可以借鉴一下。

相关推荐

    Spring面试题

    ☆ Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。 ☆ Spring Web 模块:...

    Java面试宝典2010版

    8.用一条SQL语句 查询出每门课都大于80分的学生姓名 9.所有部门之间的比赛组合 10.每个月份的发生额都比101科目多的科目 11.统计每年每月的信息 12.显示文章标题,发帖人、最后回复时间 13.删除除了id号不同,...

    最新Java面试宝典pdf版

    8.用一条SQL语句 查询出每门课都大于80分的学生姓名 100 9.所有部门之间的比赛组合 100 10.每个月份的发生额都比101科目多的科目 101 11.统计每年每月的信息 102 12.显示文章标题,发帖人、最后回复时间 103 13.删除...

    Java面试笔试资料大全

    8.用一条SQL语句 查询出每门课都大于80分的学生姓名 100 9.所有部门之间的比赛组合 100 10.每个月份的发生额都比101科目多的科目 101 11.统计每年每月的信息 102 12.显示文章标题,发帖人、最后回复时间 103 13.删除...

    JAVA面试宝典2010

    8.用一条SQL语句 查询出每门课都大于80分的学生姓名 100 9.所有部门之间的比赛组合 100 10.每个月份的发生额都比101科目多的科目 101 11.统计每年每月的信息 102 12.显示文章标题,发帖人、最后回复时间 103 13.删除...

    Java面试宝典-经典

    8.用一条SQL语句 查询出每门课都大于80分的学生姓名 100 9.所有部门之间的比赛组合 100 10.每个月份的发生额都比101科目多的科目 101 11.统计每年每月的信息 102 12.显示文章标题,发帖人、最后回复时间 103 13.删除...

    java面试题大全(2012版)

    8.用一条SQL语句 查询出每门课都大于80分的学生姓名 100 9.所有部门之间的比赛组合 100 10.每个月份的发生额都比101科目多的科目 101 11.统计每年每月的信息 102 12.显示文章标题,发帖人、最后回复时间 103 13.删除...

    Java面试宝典2012版

    8.用一条SQL语句 查询出每门课都大于80分的学生姓名 100 9.所有部门之间的比赛组合 100 10.每个月份的发生额都比101科目多的科目 101 11.统计每年每月的信息 102 12.显示文章标题,发帖人、最后回复时间 103 13...

    java面试宝典2012

    8.用一条SQL语句 查询出每门课都大于80分的学生姓名 109 9.所有部门之间的比赛组合 109 10.每个月份的发生额都比101科目多的科目 110 11.统计每年每月的信息 111 12.显示文章标题,发帖人、最后回复时间 112 13.删除...

    Java面试宝典2012新版

    8.用一条SQL语句 查询出每门课都大于80分的学生姓名 100 9.所有部门之间的比赛组合 100 10.每个月份的发生额都比101科目多的科目 101 11.统计每年每月的信息 102 12.显示文章标题,发帖人、最后回复时间 103 13.删除...

    Java 面试宝典

    21、写 clone()方法时,通常都有一行代码,是什么? ............................................. 16 22、面向对象的特征有哪些方面 ...........................................................................

Global site tag (gtag.js) - Google Analytics