yxhxj2006

常用链接

统计

最新评论

ibatis 开发指南 3

接 ibatis 开发指南 2 

Cache 
在特定硬件基础上(同时假设系统不存在设计上的缺漏和糟糕低效的 SQL 语句) 
Cache往往是提升系统性能的最关键因素)。 
相对 Hibernate 等封装较为严密的 ORM 实现而言(因为对数据对象的操作实现 
了较为严密的封装,可以保证其作用范围内的缓存同步,而 ibatis 提供的是半封闭 
的封装实现,因此对缓存的操作难以做到完全的自动化同步)。 
ibatis 的缓存机制使用必须特别谨慎。特别是 flushOnExecute 的设定(见 
“ibatis配置”一节中的相关内容),需要考虑到所有可能引起实际数据与缓存数据 
不符的操作。如本模块中其他Statement对数据的更新,其他模块对数据的更新,甚 
至第三方系统对数据的更新。否则,脏数据的出现将为系统的正常运行造成极大隐患。 
如果不能完全确定数据更新操作的波及范围,建议避免 Cache的盲目使用。 
结合cacheModel来看: 
<cacheModel >
id="product-cache" 
type ="LRU" 
readOnly="true" 
serialize="false"> 
</cacheModel> 

可以看到,Cache有如下几个比较重要的属性: 
ÿ readOnly 
ÿ serialize 
ÿ type 

readOnly 
readOnly值的是缓存中的数据对象是否只读。 这里的只读并不是意味着数据对象一 
旦放入缓存中就无法再对数据进行修改。而是当数据对象发生变化的时候,如数据对 
象的某个属性发生了变化,则此数据对象就将被从缓存中废除,下次需要重新从数据 
库读取数据,构造新的数据对象。 
而 readOnly="false"则意味着缓存中的数据对象可更新,如 user 对象的 name 
属性发生改变。 
只读Cache能提供更高的读取性能,但一旦数据发生改变,则效率降低。系统设计 
时需根据系统的实际情况(数据发生更新的概率有多大)来决定 Cache的读写策略。 

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


Cache Type: 
与hibernate类似,ibatis通过缓冲接口的插件式实现,提供了多种 Cache的实现机 
制可供选择: 
1. MEMORY 
2. LRU 
3. FIFO 
4. OSCACHE 

MEMORY类型Cache与 WeakReference 
MEMORY 类型的 Cache 实现,实际上是通过 Java 对象引用进行。ibatis 中,其实现类 
为com.ibatis.db.sqlmap.cache.memory.MemoryCacheController, MemoryCacheController 内部, 
使用一个HashMap来保存当前需要缓存的数据对象的引用。 

这里需要注意的是Java2中的三种对象引用关系: 
a SoftReference 
b WeakReference 
c PhantomReference 
传统的Java 对象引用,如: 
public void doSomeThing(){ 
User user = new User() 
…… 

当doSomeThing方法结束时,user 对象的引用丢失,其所占的内存空间将由JVM在下 
次垃圾回收时收回。如果我们将user 对象的引用保存在一个全局的 HashMap中,如: 
Map map = new HashMap(); 

public void doSomeThing(){ 
User user = new User(); 
map.put("user",user); 

此时,user 对象由于在 map 中保存了引用,只要这个引用存在,那么 JVM 永远也不会 
收回user 对象所占用的内存。 
这样的内存管理机制相信诸位都已经耳熟能详,在绝大多数情况下,这几乎是一种完美 的解决方案。但在某些情况下,却有些不便。如对于这里的 Cache 而言,当 user 对象使用 
之后,我们希望保留其引用以供下次需要的时候可以重复使用,但又不希望这个引用长期保 
存,如果每个对象的引用都长期保存下去的话,那随着时间推移,有限的内存空间将立即被 
这些数据所消耗殆尽。最好的方式,莫过于有一种引用方式,可以在对象没有被垃圾回收器 
回收之前, 依然能够访问此对象,当垃圾回收器启动时, 如果此对象没有被其他对象所使用, 
则按照常规对其进行回收。 

SoftReference、WeakReference、PhantomReference为上面的思路提供了有力支持。 

这三种类型的引用都属于“非持续性引用”,也就是说,这种引用关系并非持续存在, 
它们所代表的引用的生命周期与JVM 的运行密切相关,而非与传统意义上的引用一样依赖 
于编码阶段的预先规划。 
先看一个SoftReference的例子: 
SoftReference ref; 

public void doSomeThing(){ 
User user = new User(); 
ref = new SoftReference(user); 


public void doAnotherThing(){ 
User user = (User)ref.get();//通过SoftReference获得对象引用 
System.out.println(user.getName()); 



假设我们先执行了 doSomeThing 方法,产生了一个 User 对象,并为其创建了一个 
SoftReference引用。 
之后的某个时刻,我们调用了doAnotherThing方法,并通过 SoftReference获取 User 对 
象的引用。 
此时我们是否还能取得user 对象的引用?这要看JVM的运行情况。对于 SoftReference 
而言,只有当目前内存不足的情况下,JVM 在垃圾回收时才会收回其包含的引用(JVM 并 
不是只有当内存不足时才启动垃圾回收机制,何时进行垃圾回收取决于各版本 JVM 的垃圾 
回收策略。如某这垃圾回收策略为:当系统目前较为空闲,且无效对象达到一定比率时启动 
垃圾回收机制,此时的空余内存倒可能还比较充裕)。这里可能出现两种情况,即: 

ÿ JVM 目前还未出现过因内存不足所引起的垃圾回收,user 对象的引用可以通过 
SoftReference从JVM Heap中收回。 
ÿ JVM已经因为内存不足启动了垃圾回收机制,SoftReference所包含的 user 对象的 
引用被JVM所废弃。此时 ref.get方法将返回一个空引用(null),对于上面 
的代码而言,也就意味着这里可能抛出一个 NullPointerException。 

WeakReference比SoftReference在引用的维持性上来看更加微弱。 无需等到内存不足的 
情况, 只要JVM启动了垃圾回收机制, 那么WeakReference所对应的对象就将被JVM回收。 
也就是说,相对SoftReference而言,WeakReference 被 JVM回收的概率更大。 

PhantomReference 比 WeakReference 的引用维持性更弱。与 WeakReference 和 
SoftReference不同,PhantomReference所引用的对象几乎无法被回收重用。它指向的对象实 
际上已经被JVM销毁(finalize方法已经被执行),只是暂时还没被垃圾回收器收回而已。 
PhantomReference主要用于辅助对象的销毁过程,在实际应用层研发中,几乎不会涉及。 

MEMORY类型的Cache正是借助SoftReference、 WeakReference以及通常意义上的Java 
Reference实现了对象的缓存管理。 
下面是一个典型的MEMORY类型 Cache配置: 
<cacheModel id="user_cache" type="MEMORY"> 
<flushInterval hours="24"/> 
<flushOnExecute statement="updateUser"/> 
<property name="reference-type" value="WEAK" /> 
</cacheModel> 
其中 flushInterval 指定了多长时间清除缓存,上例中指定每 24 小时强行清空缓存 
区的所有内容。 
reference-type属性可以有以下几种配置: 
1. STRONG 
即基于传统的Java对象引用机制,除非对Cache显式清空(如到了flushInterval 
设定的时间;执行了flushOnExecute所指定的方法;或代码中对Cache执行了清除 
操作等),否则引用将被持续保留。 
此类型的设定适用于缓存常用的数据对象,或者当前系统内存非常充裕的情况。 
2. SOFT 
基于 SoftReference 的缓存实现,只有 JVM 内存不足的时候,才会对缓冲池中的数 
据对象进行回收。 
此类型的设定适用于系统内存较为充裕,且系统并发量比较稳定的情况。 
3. WEAK 
基于 WeakReference 的缓存实现,当 JVM 垃圾回收时,缓存中的数据对象将被 JVM 
收回。 
一般情况下,可以采用WEAK的MEMORY型Cache配置。 

LRU型 Cache 
当 Cache达到预先设定的最大容量时,ibatis会按照“最少使用”原则将使用频率最少 
的对象从缓冲中清除。 
<cacheModel id="userCache" type="LRU"> 
<flushInterval hours="24"/> 
<flushOnExecute statement="updateUser"/> 
<property name="size" value="1000" /> 
</cacheModel> 
可配置的参数有: 
u flushInterval 
指定了多长时间清除缓存, 上例中指定每24小时强行清空缓存区的所有内容。 
u size Cache的最大容量。 

FIFO型 Cache 
先进先出型缓存,最先放入Cache中的数据将被最先废除。可配置参数与 LRU型相同: 
<cacheModel id="userCache" type="FIFO"> 
<flushInterval hours="24"/> 
<flushOnExecute statement="updateUser"/> 
<property name="size" value="1000" /> 
</cacheModel> 

OSCache 

与上面几种类型的Cache不同,OSCache来自第三方组织 Opensymphony。可以通过以 
下网址获得OSCache的最新版本(http://www.opensymphony.com/oscache/)。 

在生产部署时,建议采用 OSCache,OSCache 是得到了广泛使用的开源 Cache 实现 
(Hibernate 中也提供了对 OSCache 的支持),它基于更加可靠高效的设计,更重要的是, 
最新版本的 OSCache 已经支持 Cache 集群。如果系统需要部署在集群中,或者需要部署在 
多机负载均衡模式的环境中以获得性能上的优势,那么OSCache在这里则是不二之选。 

Ibatis中对于OSCache的配置相当简单: 
<cacheModel id="userCache" type="OSCACHE"> 
<flushInterval hours="24"/> 
<flushOnExecute statement="updateUser"/> 
<property name="size" value="1000" /> 
</cacheModel> 

之所以配置简单,原因在于,OSCache拥有自己的配置文件(oscache.properties)J。 
下面是一个典型的OSCache配置文件: 
#是否使用内存作为缓存空间 
cache.memory=true 

#缓存管理事件监听器,通过这个监听器可以获知当前Cache的运行情况 
cache.event.listeners=com.opensymphony.oscache.plugins.clustersupport.JMSBroa 
dcastingListener 

#如果使用磁盘缓存(cache.memory=false),则需要指定磁盘存储接口实现 
#cache.persistence.class=com.opensymphony.oscache.plugins.diskpersistence.Disk 
PersistenceListener 

# 磁盘缓存所使用的文件存储路径 
# cache.path=c:\\myapp\\cache 

# 缓存调度算法,可选的有LRU,FIFO和无限缓存(UnlimitedCache) # cache.algorithm=com.opensymphony.oscache.base.algorithm.FIFOCache 
# cache.algorithm=com.opensymphony.oscache.base.algorithm.UnlimitedCache 
cache.algorithm=com.opensymphony.oscache.base.algorithm.LRUCache 

#内存缓存的最大容量 
cache.capacity=1000 

# 是否限制磁盘缓存的容量 
# cache.unlimited.disk=false 

# 基于JMS的集群缓存同步配置 
#cache.cluster.jms.topic.factory=java:comp/env/jms/TopicConnectionFactory 
#cache.cluster.jms.topic.name=java:comp/env/jms/OSCacheTopic 
#cache.cluster.jms.node.name=node1 

# 基于JAVAGROUP的集群缓存同步配置 
#cache.cluster.properties=UDP(mcast_addr=231.12.21.132;mcast_port=45566;ip_ 
ttl=32;mcast_send_buf_size=150000;mcast_recv_buf_size=80000):PING(timeout 
=2000;num_initial_members=3):MERGE2(min_interval=5000;max_interval=10000 
):FD_SOCK:VERIFY_SUSPECT(timeout=1500):pbcast.NAKACK(gc_lag=50;retransm 
it_timeout=300,600,1200,2400,4800):pbcast.STABLE(desired_avg_gossip=20000): 
UNICAST(timeout=5000):FRAG(frag_size=8096;down_thread=false;up_thread=fal 
se):pbcast.GMS(join_timeout=5000;join_retry_timeout=2000;shun=false;print_loc 
al_addr=true) 
#cache.cluster.multicast.ip=231.12.21.132 

配置好之后,将此文件放在 CLASSPATH 中,OSCache 在初始化时会自动找到此 
文件并根据其中的配置创建缓存实例。 
ibatis in Spring 
这里我们重点探讨Spring框架下的ibatis应用,特别是在容器事务管理模式下的ibatis 
应用开发。 

关于Spring Framework,请参见笔者另一篇文档: 
《Spring 开发指南》http://www.xiaxin.net/Spring_Dev_Guide.rar 

针对ibatis,Spring配置文件如下: 

Ibatis-Context.xml: 
<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" >
"http://www.springframework.org/dtd/spring-beans.dtd"> 

<beans> 

<bean id="dataSource" >
class="org.apache.commons.dbcp.BasicDataSource" 
destroy-method="close"> 
<property name="driverClassName"> 
<value>net.sourceforge.jtds.jdbc.Driver</value> 
</property> 
<property name="url"> 
<value>jdbc:jtds:sqlserver://127.0.0.1:1433/Sample</value> 
</property> 
<property name="username"> 
<value>test</value> 
</property> 
<property name="password"> 
<value>changeit</value> 
</property> 
</bean> 

<bean id="sqlMapClient" >
class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> 
<property name="configLocation"> 
<value>SqlMapConfig.xml</value> 
</property> 
</bean> 

<bean id="transactionManager" >
class="org.springframework.jdbc.datasource.DataSourceTransactio 
nManager"> <property name="dataSource"> 
<ref local="dataSource"/> 
</property> 
</bean> 

<bean id="userDAO" class="net.xiaxin.dao.UserDAO"> 
<property name="dataSource"> 
<ref local="dataSource" /> 
</property> 
<property name="sqlMapClient"> 
<ref local="sqlMapClient" /> 
</property> 
</bean> 

<bean id="userDAOProxy" >
class="org.springframework.transaction.interceptor.TransactionPro 
xyFactoryBean"> 

<property name="transactionManager"> 
<ref bean="transactionManager" /> 
</property> 

<property name="target"> 
<ref local="userDAO" /> 
</property> 

<property name="transactionAttributes"> 
<props> 
<prop key="insert*">PROPAGATION_REQUIRED</prop> 
<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop> 
</props> 
</property> 
</bean> 
</beans> 

可以看到: 
1. sqlMapClient节点 
sqlMapClient节点实际上配置了一个sqlMapClient的创建工厂类。 
configLocation属性配置了ibatis映射文件的名称。 

2. transactionManager节点 
transactionManager采用了Spring中的DataSourceTransactionManager。 

3. userDAO节点 对应的,UserDAO需要配置两个属性,sqlMapClient和DataSource, 
sqlMapClient将从指定的DataSource中获取数据库连接。 

本例中Ibatis映射文件非常简单: 

sqlMapConfig.xml: 
<sqlMapConfig> 
<sqlMap resource="net/xiaxin/dao/entity/user.xml"/> 
</sqlMapConfig> 

net/xiaxin/dao/entity/user .xml 
<sqlMap namespace="User"> 
<typeAlias alias="user" type="net.xiaxin.dao.entity.User" /> 

<insert id="insertUser" parameterClass="user"> 
INSERT INTO users ( username, password) VALUES ( #username#, 
#password# ) 
</insert> 

</sqlMap> 

UserDAO.java: 
public class UserDAO extends SqlMapClientDaoSupport implements 
IUserDAO { 

public void insertUser(User user) { 
getSqlMapClientTemplate().update("insertUser", user); 


SqlMapClientDaoSupport(如果使用ibatis 1.x版本,对应支持类是 
SqlMapDaoSupport)是Spring中面向ibatis的辅助类,它负责调度DataSource、 
SqlMapClientTemplate(对应ibatis 1.x版本是SqlMapTemplate)完成ibatis操作, 
而DAO则通过对此类进行扩展获得上述功能。上面配置文件中针对UserDAO的属性设 
置部分,其中的属性也是继承自于这个基类。 

SqlMapClientTemplate对传统SqlMapClient调用模式进行了封装,简化了上层访问 
代码。 

User .java: 
public class User { 

public Integer id; 

public String username; public String password; 

public Integer getId() { 
return id; 


public void setId(Integer id) { 
this.id = id; 

public String getPassword() { 
return password; 


public void setPassword(String password) { 
this.password = password; 


public String getUsername() { 
return username; 


public void setUsername(String username) { 
this.username = username; 



测试代码: 
InputStream is = new FileInputStream("Ibatis-Context.xml"); 
XmlBeanFactory factory = new XmlBeanFactory(is); 
IUserDAO userdao = (IUserDAO)factory.getBean("userDAOProxy"); 

User user = new User(); 
user.setUsername("Sofia"); 
user.setPassword("mypass"); 

userdao.insertUser(user); 

对比前面 ibatis 程序代码,我们可以发现 UserDAO.java 变得异常简洁,这也正是 
Spring Framework为我们带来的巨大帮助。 

Spring以及其他IOC 框架对传统的应用框架进行了颠覆性的革新, 也许这样的评价有 
点过于煽情,但是这确是笔者第一次跑通 Spring HelloWorld 时的切身感受。有兴趣的 
读者可以研究一下Spring framework,enjoy it! 

posted on 2012-09-20 00:56 奋斗成就男人 阅读(558) 评论(0)  编辑  收藏 所属分类: J2EE


只有注册用户登录后才能发表评论。


网站导航: