首    页 建模架构 设计模式 培训咨询 jdon框架 论坛

返回目录列表

缓存和状态设计

根据前面架构设计,缓存和状态都在Dao持久层实现,我们分别实现Forum ForumMessage相应的DAO类,数据库保持原来的Jive数据库设计,因为数据库字段和FormModel设计字段不是完全一致的,这涉及到一些设计技巧。

 

缓存设计

在持久层MessageDaoSql类的getMessage方法中,使用下面SQL语句从数据库获得数据:

"SELECT threadID, forumID, userID, subject, body, modValue, rewardPoints, "+

            "creationDate, modifiedDate FROM jiveMessage WHERE messageID=?";

数据库中获得的是threadID forumID,那么在将这些数据组合成ForumMessage对象时,是否根据forumID将其对应的Forum查询出来,然后打包进入ForumMessage中?

这涉及到嵌入对象的设计方法,按照一般思路是需要这样做的,但是考虑缓存问题,因为目前Jdon框架提供的缺省缓存是扁平式,不是嵌入式或树形的,因此,一个ModelA对象被嵌入到另外一个ModelB对象中,那么这个ModelA对象随着ModelB对象被一起缓存,而ModelA有可能还有另外一个对象在缓存中,这样就有两份,当你进行增删改时,只能更新后者缓存对象,对于被嵌入在ModelB的对象则无法更新,如果前台调用ModelB,看到ModelB中的ModelA是未被更新的对象。

通过设计,我们可以避免这种情况,在持久层组合ForumMessage时,我们不再查询数据库立即获得Forum对象,只是将forumID赋值到ForumForumId中,这样ForumMessage中的Forum基本是一个空对象,那么要求在表现层Jdon框架将Forum对象数据拷贝到ActionFormForumForm时,需要将Forum补充完整。

问题关键,如何和Jdon框架互动,在表现层能够将ForumMessageForum补充完整,而且Forum的获得最好先访问缓存,缓存有则从缓存获得。

有两种方式访问缓存:

利用Jdon框架的缓存拦截器,那么就需要在表现层完成Forum补充动作;如果是在服务层,Jdon框架拦截器就不起作用了,需要自己手工访问缓存。

手工访问缓存。

上面两个方式取依据要根据具体场景,这里取决于ForumMessage派什么用处,也就是MessageDaoSql类的getMessage方法将被用于哪些地方,如果getMessage只是被表现层使用,那么我们采取第一种方式;如果getMessage还被服务器其他服务访问,那么只好采取第2种方式了。

因为在前面论坛Forum查询中,需要知道本论坛最后一个帖子,因此在ForumService中也需要调用getMessage方法,那么这里我们采取第2种方案。

那么我们在服务层ForumService中将ForumMessage的嵌入对象补充完全,ForumServicegetMessage方法中结合缓存和数据库DaoMessageDaoSql

ForumMessage中有两个嵌入对象ForumForumThreadForum前面已经实现,ForumThread前面还没有编码,这里我们将其Dao顺便在这里和ForumMessage一起实现了。

由于ForumThreadForumMessageForumMessageForumThread互相包含,怎样确保它们互相包含的对象都是从缓存里获得同一个实例呢?

唯一一致性

这里涉及到缓存放在什么位置的问题,是放在Service层还是Dao层?

根据前面的架构设计,缓存是设置在持久层Dao中,并且持久层的缓存和Jdon框架的AOP缓存拦截器所操作的缓存都是同一个缓存。

这样可以保证无论是表现层 还是Service层,同一个ID获得的都是同一个Model对象,而且ModelA对象中包含的ModelB对象与缓存里的ModelB对象是同一个,这样表现层更改ModelB时,实际也是更新了ModelA中的ModelB.如图:

1

为了保证Dao层的缓存和Jdon框架的缓存是同一个缓存,只要在Dao中直接引用Jdon框架的CacheManager就可以,我们使用ContainerUtil总包装与Jdon相关相关的组件,CacheManager就在其中,这样,我们使用Decorator模式在Dao层加一个缓存层,并引入ContainerUtil

ForumDao为例子,创建缓存ForumDaoCache类,它是继承ForumDaoSql,但是重新实现了其中getForum方法如下:

public Forum getForum(String forumId) {

    //从缓存中获得Forum,这个缓存就是和Jdon框架内置缓存是同一个

       Forum forum = (Forum) containerUtil.getModelFromCache(forumId, Forum.class);

        if (forum == null) {

            forum = super.getForum(forumId); //使用父类getForum

            containerUtil.addModeltoCache(forumId, forum);

        }

        return forum;

}

jdonframework,xml配置ForumDao如下:

<pojoService name="forumDao"

                            class="com.jdon.jivejdon.dao.sql.ForumDaoCache"/>

这样,ForumDaoCache就开始起作用,服务层获得的接口ForumDao的实例其实就是ForumDaoCache的实例。

相类似,我们也做一个MessageDaoCache,它是继承MessageDaoSql的,这样每个真正SQL数据库操作类外面包裹着一个缓存壳。

 

Model数据填充

每个Model都包含嵌套其他Model对象,通过缓存的唯一一致性,我们可以保证互相嵌套的Model都能够是从同一个缓存中获得,减少相同Model对象重复,节省内存,提高效率。下面以ForumThread这个Model为例子来说明Model互相引用填充的详细情况。

ForumThread对象中嵌入了ForumForumMessage(根贴主题贴)以及 ForumMessage ForumThreadState(最后跟贴等状态)三个对象。

ForumThread从数据查询出来时,这三个嵌入对象并没有在持久层充实,而是在服务层实现充实,这样保证了ForumThread中三个对象与缓存中对象是指向同一个对象(原理见上节)。

注意,ForumThreadState实现原理和方法和ForumState类似,这里就不详细描述。

所以在服务层的getThread方法中,要逐个充实上述三个对象,基本代码如下:

 

ForumThread forumThread = messageDao.getThread(threadId);

//1.充实Forum

Forum forum = forumDao.getForum(forumThread.getForum().getForumId());

forumThread.setForum(forum);

       

//2.充实Root ForumMessage

String rootmessageId = forumThread.getRootMessage().getMessageId();

ForumMessage rootforumMessage = getMessage(rootmessageId, forumThread);       

//2. 建立FormThreadRoot ForumMessage之间关系

forumThread.setRootMessage(rootforumMessage);

       

//3.充实ForumThreadState

ForumThreadState forumThreadState =  messageQueryDao.getForumThreadState(forumThread);

forumThread.setForumThreadState(forumThreadState);

if (forumThreadState.getLastPost() != null){

            Long messageId = forumThreadState.getLastPost().getMessageId();

            ForumMessage forumMessage = getMessage(messageId);

            forumThreadState.setLastPost(forumMessage);

}

Forum状态

每个论坛Forum有自己的状态,如当前论坛的最新帖子;发表人以及发表时间等;当前论坛的帖子总数等等,这些都属于论坛Forum的一种状态。

解决状态保存也不外乎两种方案:

使用数据库保存状态,这是最朴素的选择

使用缓存保存状态。

使用缓存和使用数据库并不矛盾,缓存通常位于数据库前端,正象本论坛系统设计一样。但是,论坛和论坛状态这两个对象在数据库中都有持久化数据,在缓存中却是以对象形式存在的,而且这两个对象之间是一个父子(聚合)关系,包括缓存的更新等,这些都稍微有些绕人复杂。状态和数据库关系可见本文:http://www.jdon.com/artichect/state.htm

这里有一个特殊情况,有些状态是不必一定对应有数据库持久的,对比ForumDomain Model设计和数据表字段设计,下面三个字段无法直接从数据表获得:

本论坛的主题数目: private int threadCount;

本论坛的帖子数目:  private int messageCount;

本论坛的最后一贴:  private ForumMessage lastPost;

当然,也可以更改数据表设计,将这三个字段加入数据表,但是我们知道,这三个值还是可以通过现有的数据表字段经过一定运算得到,因此,可以通过中间层业务设计来获得这三个字段,这里必然使用到缓存。

ForumServiceImp类的getForum方法原来只需要直接委托ForumDao实现就可以,如:

public Forum getForum(String id) {

       return forumDao.getForum(id);

}

现在我们需要在ForumServiceImpgetForum这里做些文章了,因为forumDao.getForum(id)只完成了数据表相应的字段值,Forum中还缺少上面三个字段,我们在这里实现,首先从缓存获取,如果没有则再调用相应的Dao访问数据库。

这三个字段属于论坛状态的一种实时更新,和论坛Forum相关性不大,如果这三个字段作为Forum的三个字段,因为经常更新,所以Forum整个对象经常需要清除重新生成,为了使的论坛状态更新和论坛Forum本身脱离关系,因此,我们创建一个新的对象ForumStateForumState中包含这三个字段, ForumState包含在Forum中。这样,ForumState更新时,不必影响Forum

ForumServiceImpgetForum方法中生成Forum对象,Forum ForumState根据调用ForumDaoSqlgetForumState方法查询数据库,在调用ForumDaoSql之前,首先需要经过一个Proxy: ForumDaoCache,在ForumDaoCache的相同方法getForumState中,首先查询缓存,如果有则从缓存中获取ForumState

ForumState更新则是在ForumMessageServiceImpclearForumState方法(StateUtilclearForumState)中实现,当有帖子新增时,则清除缓存中的ForumState对象,重新从数据库中查询创建ForumState

最后还有一个棘手问题,ForumState中有包含一个lastPost对象,lastPost对象就是ForumMessage对象,那么是否是象其他字段threadCount/ messageCount直接通过查询数据库获得呢?

因为ForumMessage对象是本系统主要对象,一般会被保存在缓存中,当用户发出一个帖并显示这个帖子后,这个帖子就会装载到内存中,这样,其他人刷新论坛列表时,会看到这个论坛的最后一个帖子,看来这个帖子在很大概率上是可以从缓存中获取的。

因此,ForumStatelastPost对象我们从缓存中获取,因为ForumState创建是在Dao层实现的,从以后篇章中缓存设计可以得知,缓存是介于服务层和Dao层之间的,因此,在ForumDaoSql这个类中很难直接获得而获得lastPost对象(ForumMessage对象)缓存,那么我们只能在生成ForumState时,创建一个虚ForumMessage对象,只包含一个MessageId

   Long lastMessageId = getLastPostMessageId(forumId);   

   if (lastMessageId == null)           

     logger.warn("maybe first running, not found lastMessageId for forumId: " + forumId);

   else{

            ForumMessage forumMessage = new ForumMessage();

            forumMessage.setMessageId(lastMessageId);

            forumState.setLastPost(forumMessage);

   }

ForumService获得ForumState后,再将lastPost对象(ForumMessage对象)取出,根据messageId从缓存中获得完整的ForumMessage:

   ForumState forumState = forumDao.getForumState(forumId);

   if (forumState.getLastPost() != null){

     Long messageId = forumState.getLastPost().getMessageId();

     ForumMessage forumMessage = forumMessageService.getMessage(messageId);

     forumState.setLastPost(forumMessage);

   }

以后的ForumThread的状态ForumThreadState也参考这个方式完成。

缓存更新

一旦发生帖子修改或帖子删除,就要实现缓存更新,缓存更新的粒度不必太细,也就是说:当一个帖子删除,有可能其子贴也删除,那么是否每个子贴删除时,也要将其每个子贴的缓存删除?回答是肯定的,但是没有必要在子贴删除时也执行缓存删除,只要该贴修改或删除,和其相关的论坛状态、ForumThread状态及其树形结构等缓存全部清除,这样删除灵敏度很高;也就是粒度粗一些;这样可能更简便一些。

设计

 

起初,缓存更新是放在Service每个方法中,这会和业务逻辑混合在一起,因此需要使用过滤器或拦截器与业务逻辑分离,上面章节我们已经设计了缓存拦截,那么我们在MessageDaoDecorator(com.jdon.jivejdon.dao.sql.MessageDaoDecorator)中实现缓存更新。

一旦帖子被删除或修改,将要触发其相关的所有缓存的更新,当前与帖子相关的缓存设计有下面几个对象:

论坛状态;

ForumThread状态;

帖子本身缓存

与本贴相关的ForumThread本身

与本贴相关的子贴其他ForumMessage

ForumMessage创建 修改或删除时,实际就有必要清除缓存,清除缓存的方式有很多实现,我们采取一种较为简单的方式,Jdon框架的Model中有一个isModifiedsetModified,如果我们认为某个Model必须更新,可以调用setModified为真,这样,当在其他任何地方只要调用这个Model,我们首先检查这个ModelisModified是否为真,如果是表示被修改过,我们就不再从缓存中获得。

使用setModified可使得整个Model重新从数据库中获取,但是如果只是一个Model中嵌入的另外一个对象需要刷新,如果将整个父Model都设置为刷新,这样做好像效率不高,下面我们以Forum这个Model和其嵌入对象ForumState为例子,ForumState是经常更新的,而Forum一般只有管理员才会更新,不常更新,因此,没有必要因为ForumState经常更新就经常刷新整个Forum对象。

ForumForumState其自对象都是Model类型,既然是状态更新,就是将ForumState通过setModified设置为真,因为ForumState是一个被嵌入的对象,Jdon框架可能无法知道一个Model中嵌入的另外一个Model更新,如果不涉及到这个被嵌入的Model操作的话,因此我们有必要手工强迫更新一下。

当帖子ForumMessage这个Model发生新增删除修改时,将Forum这个Model中嵌套ForumState对象通过setModified设置为真,然后当即手工编码重新获取一次ForumState,这次必然时最新的,将这个新的ForumState设置到父对象Forum中。

private void refreshForumState(Long forumId){

    ForumState forumState = (ForumState)containerUtil.getModelFromCache(forumId, ForumState.class);       

    if (forumState != null) {      //forumState在缓存中

       forumState.setModified(true);

   }   

   //下面代码是手工更新Forum中的ForumState

  Forum forum = (Forum)containerUtil.getModelFromCache(forumId, Forum.class);

   if (forum != null){//使得当前缓存中的ForumforumState是最新的。

         forum.setForumState(forumDao.getForumState(forumId));

   }

 }

 

总结:一般对象Model缓存更新有两种方式:

直接从缓存中清除Model,前提是你需要引入Jdon框架的缓存机制;

无需和缓存机制打交道,直接将该Model通过setModified设置为修改标志。

实现

在前面业务架构设计中,缓存机制我们是捆绑在持久层Dao中,那么Model对象缓存更新也无疑是放在这个层面,以ForumMessage这个Model为例子,在持久层缓存类MessageDaoDecorator中有如下代码:

public void deleteMessage(ForumMessage  forumMessage){

        //删除数据库数据

        super. deleteMessage (forumMessage);

        //清除Model对象缓存,采取缓存更新的第一种方式

        containerUtil.clearCache(forumMessage.getMessageId());

        //更新批量查询的缓存

        pageIteratorSolver.clearCache();

    }

但是,状态缓存更新和Model对象缓存更新还有所区别,状态缓存更新属于我们业务系统的一部分,不能依靠Jdon框架来实现更新,必须手工代码实现。

以论坛状态ForumState为例子,ForumState的更新方式代码在上面章节已经有讨论,那么这段更新代码应该放在哪里呢?

首先确认状态更新的触发者,这个触发者应该也是deleteMessage这类对ForumMessage进行CRUD的动作,但是不应该和Model对象缓存更新放在持久层MessageDaoDecorator中,而是应该在业务类服务ForumMessageServiceupdateMessage之类方法中。

com.jdon.jivejdon.service.imp. ForumMessageServiceImpdeleteMessage有代码如下:

  public void deleteMessage(EventModel em) {

     ……

    messageDao.deleteThread(forumMessage.getForumThread());

     //更新论坛Forum ForumThread的状态

    refreshState(forumMessage.getForumThread());

}

注意,帖子修改updateMessage并不影响论坛状态,因为只修改内容,内容修改不影响论坛状态。

 

更多Jdon框架专题讨论

JdonFramework作为一个免费开源软件开发平台,可以商用开发大多数数据库应用软件和管理软件: 电子商务软件 在线教育软件 税务软件 Web快速开发软件 财务软件 购物车软件 医院帐务软件 crm software medical software 人事薪资软件payroll software 在线购物软件 销售软件 项目管理软件 房产不动产管理软件 生产软件 PDM软件 制造业软件 仓库软件 采购软件 进销存软件 危险源监控软件 物流软件 超市软件 银行软件 保险软件 汽车软件 医疗软件 电子软件 自动化软件 服装软件 烟草软件 分销管理软件 供应商管理软件

下载源码

框架文档

框架应用系统

演示运行

JiveJdon3

性能测试

Q&A 问答

技术支持

 

 

更多标签...



Jdon框架演示

JiveJdon
源码下载

VIP收费区

历史热点讨论排行榜




google yahoo 新浪ViVi 365Key网摘 天极网摘 CSDN网摘 添加到百度搜藏 POCO网摘





手机 add to google add to yahoo
联系我们 | 关于我们 | 广告联系 | 网站地图 | 设为首页

沪ICP证08026060 如有意见请与我们联系 Powered by JdonFramework
_×
您有新消息