JdonFramework的缓存设计
JdonFramework的缓存设计
内嵌对象(Embedded Object)缓存设计
请看下面这段代码:
public class Category extends Model{
String Id;
Product product; //内嵌包含了一个Product对象
}
Category这个Model内嵌了Product这个Model,属于一种关联关系。
目前Jdon框架提供的缺省缓存是扁平式,不是嵌入式或树形的(当然也可以使用JbossCache等树形缓存替代),因此,一个ModelB对象(如Product)被嵌入到另外一个ModelA对象(如Category)中,那么这个ModelB对象随着ModelA对象被Jdon框架一起缓存。
假设实现ModelA已经在缓存中,如果客户端从缓存直接获取ModelB,缓存由于不知道ModelB被缓存到ModelA中(EJB3实体Bean中是通过Annotation标注字段),那么缓存可能告知客户端没有ModelB缓存。那么有可能客户端会再向缓存中加一个新的ModelB,这样同一个ID可能有两份ModelB,当客户端直接调用的ModelB进行其中字段更新,那么包含在ModelA中的ModelB可能未被更新(因为ModelA没有字段更新)。
有两种解决方案:
第一.最直接方式,通过手工清除ModelA缓存方式来更新,或者耐心等待ModelA缓存自动更新。手工清除缓存见下章。
注意:下面这种做法将也会导致不一致现象发生:
在DAO层读取数据库。生成ModelA时,直接读取数据库将ModelB充填。
第二.在进行ModelA和ModelB的相关操作服务设计时,就要注意保证这两种情况下ModelB指向的都是同一个。如下图:
为达到这个目的,只要在Service层和Dao层之间加一个缓存Decorator,服务层向Dao层调用的任何Model对象都首先经过缓存检查,缓存中保存的ModelA中的ModelB是一个只有ModelB主键的空对象,在服务层getModelA方法中,再对ModelA的ModelB进行充实,填满,根据ModelA中的ModelB的主键,首先再到缓存查询,如果有,则将缓存中ModelB充填到ModelA的ModelB中,这样上图目的就可以实现了。
相关实现可参考JiveJdon 3.0的代码,Forum/ForumMessage都属于这种情况。
Model缓存使用
Jdon框架通过两种方式使用Model缓存:
CRUD框架内部使用,如果你使用Jdon框架提供的CRUD功能,那么其已经内置Model缓存,而且会即时清除缓存。
通过CacheInterceptor缓存拦截器,如果你不使用Jdon框架的CRUD功能,缓存拦截器功能将激活,在向Service获取Model之前,首先查询当前缓存器中是否存在该Model,如果有从缓存中获取。当你的Model中数值更改后,必须注意自己需要手工清除该Model缓存,清除方法如下介绍。
Jdon框架除了提供单个Model缓存外,还在持久层Dao层提供了查询条件的缓存,例如如果你是根据某个字段按照如何排列等条件进行查询,这个查询条件将被缓存,这样,下次如果有相同查询条件,该查询条件将被提出,与其相关的满足查询条件一些结果(如符合条件总数等)也将被从缓存中提出,节省翻阅数据库的性能开销。
手工访问缓存
在一般情况下,前台表现层通过getService方法访问服务层一个服务,然后通过该服务Service获得一个Model,这种情况Jdon框架的缓存拦截器将自动首先从缓存读取。
但是,有时我们在服务层编码时,需要获得一个Model,在这种情况下,Jdon框架的缓存拦截器就不起作用,这时可能我们需要手工访问缓存。
因为所有服务类POJO都属于Jdon框架的容器内部组件,这实际是在容器内访问容器组件的问题。
使用com.jdon.container.finder. ContainerCallback,同时,该服务POJO类以ContainerCallback作为构造参数,当该POJO服务类注册到容器中时,容器的Ioc特性将会找到事先以及注册的ContainerCallback类。
通过ContainerCallback获得ContainerWrapper容器实例,然后通过ContainerWrapper下面方法:
public Object lookup(String name);
从容器中获得container.xml中注册的组件实例,这种方法每次调用获得的是同一个实例,相当于单例方式获得。
ModelManager modelManager =
(ModelManager)containerWrapper. lookup (“modelManager”);
其中“modelManager”字符串名称是从Jdon框架的jdonFramework.jar包中META-INF的container.xml中查询获知的。
获得ModelManager后,我们基本可以访问Model有关的功能安排,如ModelManager的getCache方法。
下节的手工清除缓存中,我们是通过WebAppUtil.getComponentInstance获得ModelManager实例,这是一种从容器外获得容器组件的方式,本节介绍从容器内获得容器组件的方式,这两种方式可根据我们实际需要灵活使用,关键是弄清除你需要在哪里触发组件调用?
手工清除缓存
注意:手工清除缓存不是必要的,因为缓存中对象是有存在周期的,这在Cache.xml中设置的,过一段时间缓存将自动清除那些超过配置时间不用的对象,这样你修改的数据将被从数据库重新加载。如果你等不及这些内在变化,可以手工处理:
有两种情况需要手工清除缓存,首先,在持久层的Dao类中,总是需要手工清除查询条件的缓存,只要在相应的增删改方法中调用PageIteratorSolver的clearCache方法既可。
如果你不实行这种缓存清除,那么你更改一个Model数据或新增一个新的Model数据,你在批量查询时,将看不到任何变化:Model数据没有被修改;新的Model没有出现在查询页面中。
其次,单个Model缓存在不使用Jdon框架的CRUD功能下也必须手工清除,如果你使用1.2.3以后版本,可以调用com.jdon.strutsutil.util. ModelUtil类的clearModelCache方法,该方法一般是Action中调用后台增删改服务之前被激活调用:
注意,手工清除Model缓存代码关键是:
modelManager.removeCache(keyValue);
keyValue是Model的主键值,例如User的主键userId值是”2356”,那么keyValue就是”2356”。简化代码如下:
//获得ModelManager实例
ModelManager modelManager = (ModelManager)
WebAppUtil.getComponentInstance(ComponentKeys.MODEL_MANAGER, request);
modelManager.removeCache(keyValue);
上面代码是在容器外访问获得ModelManager,使用上节容器内访问组件方式也可以获得ModelManager;前者适合在表现层使用;后者适合在服务层使用。
最后,介绍一下清除全部缓存的方式,调用ModelManager的clearCache方法(Jdon框架1.3以上版本),这样实际上将整个Jdon框架缓存全部清零。
前面两种清除缓存方式前提是首先获得ModelManager,特别是服务层需要清除缓存时,需要以容器内访问组件方式获得ModelManager,这只适合POJOService构成的服务层,如果我们使用EJB的Session Bean作为服务层实现,这时当前版本的Jdon框架容器不会在EJB容器中加载,因此,在Session Bean中无法访问到容器,无法获得ModelManager了,在这种情况下,可以通过设置Model的setModified属性为True,表示该Model已经修改更新,这样当表现层获取该Model时,Jdon框架缓存拦截器拦截时,发现该Model已经被修改,也就不会从缓存中获取。
Model还有另外一个方法setCacheble,当设置为false时,该Model将不会被保存到缓存中。如果你不希望某个Model被框架自动存入缓存,那么使用此功能,setCacheble和setModified区别是,前者一旦设置为真,相当于缓存失效,以后再也不能用缓存;而后者则是当前Model表示被修改过,这样当有任何再次(限一次)试图从缓存中读取这个Model时,都被会阻挡,从而可直接从数据库获得,然后再保存到缓存中,这样缓存中的Model数据就是新鲜的了。
明白Jdon框架setModified这个神奇作用,当你设置一个Model的setModified为真,那么你再读取缓存时,Jdon框架内部将忽略你这个读取,返回一个null;这样你就根据返回是否为空,再从数据库直接获得,获得后,别忘记再保存到缓存中,已覆盖前次修改的旧数据,保证以后每次从缓存中读取的都是新鲜数据。
=总结如下:手工缓存清除共有两种方式:
直接操作缓存,从缓存中清除;
如果无法操作到缓存体系,那么设置Model的setModified。
浙公网安备 33010602011771号