在框架的文档中有这样一段:
在进行ModelA和ModelB的相关操作服务设计时,就要注意保证这两种情况下ModelB指向的都是同一个,为达到这个目的,只要在Service层和Dao层之间加一个缓存Decorator,服务层向Dao层调用的任何Model对象都首先经过缓存检查,缓存中保存的ModelA中的ModelB是一个只有ModelB主键的空对象,在服务层getModelA方法中,再对ModelA的ModelB进行充实,填满,根据ModelA中的ModelB的主键,首先再到缓存查询,如果有,则将缓存中ModelB充填到ModelA的ModelB中。

->缓存中保存的ModelA中的ModelB是一个只有ModelB主键的空对象,这个怎么理解?在jivejdon3中,从ForumBuilder得到的模型都是完整的啊!

[该贴被oojdon于2008-02-18 15:29修改过]
[该贴被oojdon于2008-02-18 15:39修改过]
[该贴被oojdon于2008-02-18 22:45修改过]

>针对模型聚合模型并附有状态数据的系统jdonframework可否有清除缓存的万能方法?

缓存清除主要难点主要是嵌套,因为我们不知道当前对象会被多少其他对象引用,这可能是一个树形结构关系,如果只靠手工,可能非常累,这就依赖缓存产品,JBossCache可以支持这种树形结构缓存清除,在EJB3中有专门的嵌套标签也是为这个目的设计的。

完全依赖JBossCache这样缓存产品来进行嵌套树形结构缓存对象更新,也有其缺点,就是可能效率低,所以,对于不是复杂的系统,手工清除又不失为一个可行的方法。

forum.isEmbedded是进行手工手工缓存清除一种方式,Form由ForumState组成,ForumState是经常更新的,所以我们设计为一个状态值对象,如果不设计这个值对象,ForumState中的字段都放在Forum中,那么更新这些字段会导致Forum这个大对象锁住,从事务性能等方面都是不好的,从Evans DDD的OO设计角度也是不对的。

》批量查询时新发的帖子并没有显示,回复某个帖子之后该帖子的所有回复也没有显示
这个问题和嵌套缓存清除没有关系,Jdon框架缓存清除分两种:
第一种是被缓存对象的清除,使用fourm.setModified(true)会导致这个缓存对象被更新,当然,直接访问缓存使用remove或clear也可以更新;被缓存对象如果有嵌套,就手工参与程度更高,见前面讨论;

第二种批量查询的结果ID集合,Jdon框架是将批量查询的结果ID进行缓存,如果需要清除,再进行新增删除动作时,考虑到会对那些批量查询结构ID集合有影响,就更新那些PageIteratorSolver.clear。

总之,缓存可以带来性能数十倍的提高,但是,没有一个通用的缓存框架能够处理所有业务对象。如果有,数据库产品就会集成进入这样的高效缓存框架,数据库为王时代会继续下去;实际上这是不可能的,因为不同的业务系统有不同形式业务模型对象,没有一个软件产品能够事先知道我们业务系统是什么样子的。

所以,我们需要对我们不同的业务应用系统,进行不同的DDD设计,提炼创造出不同的实体和嵌套其中的各种不同值对象,初期可以委托Hibernate这样的实体持久产品来管理缓存和数据库同步,但是如果要精确定位,提高缓存效率,还是需要手工介入,但是手工介入时,必须思路清晰,不能搞太复杂。

这些都和具体框架无关,是软件深层次开发遇到的必然问题,所以,不能说缓存清除的复杂是Jdon框架不能快速开发的,因为任何快速开发只是初期,包括Ruby on rails或PHP这样快速开发产品,它们的缓存处理都是粗粒度,最多做到和JBossCache差不多,EJB这样产品也是这样,它的实体Bean和HIbernate等持久层技术一样,都不能全自动而且高效解决嵌套缓存清除这个问题,Db4O这样对象数据库的主要技术衡量指标也是这个。

所以,目前没有万能的、全自动而且高效率的缓存更新方法,而且也不会有,只有更接近的答案。


>->缓存中保存的ModelA中的ModelB是一个只有ModelB主键的空对象,这个怎么理
>解?在JiveJdon3中,从ForumBuilder得到的模型都是完整的啊!
这句话可能有些问题,我现在看也没有想起,当初写这段文字目的想表述的是:ModelB如果事先首先被单独读取放入了缓存,那么ModelA中的ModelB不能在ModelA创建时再创建一个新的,这个嵌套的ModelB必须指向缓存中事先读取的那个ModelB。

如果使用HIbernate等框架,框架会帮助你解决这个细节,但是如果你使用JDBC+缓存,也就是象JJ3这样,那么就必须面临这个问题解决,JJ3提供了这样解决方案。

题外话:从这里可见:学习JJ3可以帮助初学者更灵活地掌握HIbernate深层次运用,很多程序员用HIbernate经常会半途而废,开始觉得简单,到后来觉得复杂,其实就是没有对其内部缓存机制有深入了解的原因。这也是当初EJB实体Bean难用的问题所在,现在这些喜欢“简单”程序员又去追风Ruby等,如果他们深入开发复杂业务,必然碰到这些问题,无法回避,因为不存在一个万能的缓存处理框架。



[该贴被banq于2008-02-19 11:42修改过]

老师,请解惑:
1,用户发帖回帖时会调用pageIteratorSolver.clearCache()清掉批量查询的缓存,后台的日志如下
cache->remove the object of BLOCK0SELECT count(1) FROM jiveMessage WHERE jiveMessage.userID=?+201 from cache
cache->remove the object of BLOCK0SELECT count(1) FROM jiveMessage WHERE jiveMessage.userID=?+1 from cache
cache->remove the object of BLOCK0select threadID from jiveThread WHERE forumId=? ORDER BY modifiedDate DESC +102 from cache
cache->remove the object of BLOCK0select count(1) from jiveThread where forumId=?+102 from cache
cache->remove the object of BLOCK0select threadID from jiveThread WHERE forumId=? ORDER BY modifiedDate DESC +102 from cache
cache->remove the object of BLOCK0select messageID from jiveMessage WHERE threadID=? ORDER BY creationDate ASC+109 from cache
cache->remove the object of BLOCK0select count(1) from jiveMessage where threadID=? +109 from cache
cache->remove the object of BLOCK0SELECT count(1) FROM jiveMessage WHERE jiveMessage.userID=?+204 from cache

问题是为什么没有清掉批量查询论坛(forum)的缓存?

2,ForumMessage的CRUD配置中,getMethod为什么要用findMessage而不用getMessage?如果这样做的话好像导致数据类缓存拦截失效,缓存数据则必须靠DAO的Decorator.

3,在jivejdon_permission.xml中配置
<method name="createTopicMessage">
<role>User</role>
</method>
<method name="createReplyMessage">
<role>User</role>
</method>
是何用意?看了《基于组件方法级别授权访问》这篇文章之后我理解为只有user角色的用户才可以发帖和回帖,可是角色admin也可以发帖和回帖呀?

>没有清掉批量查询论坛(forum)的缓存
大家要使用同一个pageIteratorSolver对象。

>要用findMessage而不用getMessage
有时就是不需要缓存,比如修改之前,就是从数据库获得最新及时的数据进行修改,否则从缓存获得的有可能和数据库数据不一致,如果同时并发读取修改就会出问题。

>角色admin也可以发帖和回帖呀
如果你希望任何角色可以发回帖,你就加入这个配置中:
<method name="createTopicMessage">
<role>User</role>
<role>Admin</role>
</method>
<method name="createReplyMessage">
<role>User</role>
<role>Admin</role>
</method>

恩,谢谢,其实第三个问题我的意思是,根据jivejdon的配置表示只有user角色才可以发回帖,可是根据程序运行结果来看admin角色也可以发回帖,这样让人感觉方法拦截器没有起作用,也许是程序问题,下载的jboss打包的jivejdon中admin是不能发回帖的。

>有时就是不需要缓存,比如修改之前,就是从数据库获得最新及时的数据进行修改,否则从缓存获得的有可能和数据库数据不一致,如果同时并发读取修改就会出问题。

findMessage(ForumMessageShell)-->getMessage(ForumBuilder)-->getMessage(MessageDaoDecorator)最终还是从缓存里面读取也?

ForumMessage进行批量查询的时候会被安装的过滤器过滤,也就是说诸如[url][img]之类之类的标签将会被滤掉,缓存中的ForumMessage将携带filteredBody和原始body,filteredBody用于显示,body用于编辑,是这样吗?如果如此,就没有得到数据库的最新数据啊,也许是哪个地方我还没有注意到。

我想在messageList.jsp显示帖子的时候同时显示每个用户是否在线,可怎么发现这个功能不好添加?
业务层的sessionContext好像做不到,真的感觉jdonframework需要高手才能够灵活使用,这样下去我是否也会慢慢变成高手,^_^

框架文档:
SessionContext是保存在Web容器的HttpSession中一个载体,当在服务层服务对象中需要缓存一些数据,这些数据的Scope是Session性质,这样可以通过SessionContext实现数据共享。Jdon框架还提供Stateful提供另外一种与Session有关状态载体选择。
目前Jdon框架通过SessionContextSetup将一些用户相关信息保存到SessionContext,当应用系统通过Jdon框架调用任何服务时,将激活SessionContextSetup,SessionContextSetup的缺省实现是com.jdon.security.web. HttpRequestUserSetup。

疑惑:

SessionContext是Session性质的对象,是否可以这样理解:每个到来的用户请求服务器都会给他生成一个SessionContext对象,jdon框架把这个对象通过拦截器注射进实现了SessionContextAcceptable接口的pojoservice业务bean中?

当应用系统通过Jdon框架调用任何服务时,将激活SessionContextSetup,如果我想替换SessionContextSetup提供更多信息给业务层,可是每次调用服务都会激活SessionContextSetup重新设值,是否存在不妥?因为SessionContext在第一次调用服务的时候就已经有值了,看了一下源码,好像每次调用服务都new了一个SessionContext,直觉告诉我好像会出问题,banq老师,望解惑,这个问题是我在jivejdon中添加用户在线状态显示时遇到的。


[该贴被oojdon于2008-03-22 13:15修改过]

>在messageList.jsp显示帖子的时候同时显示每个用户是否在线,可怎么发现这个功能不好添加

用户在线需要从HttpSession机制独立去开发,com.jdon.jivejdon.presentation.listener.UserCounterListener可提供参考。

和sessionContext无关,sessionContext只是提供业务会话方面的功能协助,比如购物车属于业务功能,每个在线用户都对应一个购物车,那么就需要使用sessionContext来帮助实现。

>看了一下源码,好像每次调用服务都new了一个SessionContext,
sessionContext是依赖HttpSession的一个会话场景,你可以使用其他缓存来替代HttpSession作为会话场景,在这个会话场景中,你可以自己保存你的会话,或者可以获取已经登录用户的令牌Principle。

Session也属于缓存一种,只不过放入其中的对象的生命周期和一般缓存生命周期有些不一样,Session的生命周期就是依赖客户端的,每一个客户端只要在线,就只有一个Session场景容器,因此,你可以在业务层将一些业务功能使用Jdon框架提供的这个SessionContext来实现,如果没有这个SessionContext,你要实现诸如购物车等Session性质业务功能,你必须跑到表现层使用HttpSession实现,很显然违反分层原则。

这部分属于高级复杂方面,这些功能就是Spring也到了2.0以后才提供。你现在如果看不懂可暂时停停。
http://www.jdon.com/jivejdon/thread/33614.html


关于前面帖子findMessage/getMessage,我查了一下,不是关于缓存,而是关于权限的,findMessage有权限检查的,而getMessage则无,适合业务层内部调用。

[该贴被banq于2008-03-24 11:33修改过]
[该贴被banq于2008-03-24 11:37修改过]

老师,jivejdon中的完整模型是在领域层通过工厂创建的,DAO通常返回只携带主键ID的空对象,复杂模型的创建根据DDD需要在领域层设工厂,那简单对象呢?可不可以在DAO层进行DAO注射然后创建一个完整模型返回给上层?

1,initReplyMessage串接initMessage进行回帖页面初始化。
2,模型构造器ForumBuilder的双参数方法getMessage(Long messageId,Forum forum)、getThread(Long threadId, Forum forum)。
3,服务层直接调用DAO引发的拦截失效,DaoDecorator避免内存中出现模型副本。
4,ForumMessage批量查询抛弃缓存拦截器,ForumMessageShell服务因为权限提供findMessage,findFilteredMessage,getMessage三个读取ForumMessage的方法。
5,组件方法调用使用AOP半路堵截。

哇塞,大有文章!

>那简单对象呢?可不可以在DAO层进行DAO注射然后创建一个完整模型返回给上层?
可以,你就想象这是Hibernate。我们用JDBC比hibernate粒度可控细,性能好

不小心逛到了上海锐道信息技术有限公司的论坛,Powered by JForum 2.1.6,让我又见识了一个用java写就的速度超快的论坛,把源码抓出来看了看,不出所料,充斥着大量的cache代码:


public void setCacheEngine(CacheEngine engine)
{
cache = engine;
}
public static void clear()
{
synchronized (FQN) {
cache.add(FQN, new HashMap());
cache.add(FQN_COUNT, LOGGED_COUNT, new Integer(0));
cache.add(FQN_COUNT, ANONYMOUS_COUNT, new Integer(0));
cache.remove(FQN_LOGGED);
cache.remove(FQN_USER_ID);
}
}

源代码目录有一个repository,呵呵,似曾相识,难道是DDD设计思想的仓储?以我现在的功力还判断不出来,不过这个repository在频繁的操作缓存,其结构远不如jivejdon的repository清晰,DAO就更别提了,不停的重复着jdbc底层的那些诸如connection的对象。

总得说来,很佩服Jforum的作者,自己实现mvc,搞数据库连接池,代码里面大量的操作cache,要我来做那是不可能的了,不过呢,想把它的某些功能性代码挑出来加到jivejdon上去!

提起缓存,不能不提到jivejdon的DAO层filter,这些filter是因为我们在业务层编码导致拦截器失效而添加的,也就是说缓存拦截器在比较复杂的模型操作系统中是没有作用的,拦截器的作用我们在DAO层的filter中实现了。

看过jivejdon代码的人都应该和我一样叹服于这个论坛程序的代码质量,一直想在上面添加额外的功能,但是总有一种如履薄冰的感觉,担心自己的不小心伤害到到它,伤害到它的架构。

不过确实有一个疑问,我们用Jdon框架,费尽心思的少访问数据库,做查询缓存等等,而在jivejdon中每次发回帖刷新状态对象的时候都要去数据库翻阅一次,每次都new了一个状态对象,是否可以直接在内存中更新原来的对象?

为了添加帖子点击量的功能,我在messageList.shtml之前串接了一个Action,这个Action更新状态对象的同时更新数据库,这样做是否有性能问题?因为每次帖子的点击都和数据库交互了一次,这似乎违背了Jdon框架的思想。

发现这样一个问题,发完帖之后,过段时间我重新编辑了这个帖子,修改了帖子的内容和主题,本网站论坛会在thread批量查询的时候显示新的主题名,而在我本地电脑上运行的jivejdon被修改的主题名则没有任何变化,属于缓存没有被清除,可是代码中是写了清除代码的啊,郁闷:


public void updateMessage(ForumMessage forumMessage) throws Exception{
super.updateMessage(forumMessage);
messageSearchProxy.updateMessage(forumMessage);
containerUtil.clearCache(forumMessage.getMessageId());
containerUtil.clearCache(forumMessage.getForumThread().getThreadId());
}


[该贴被oojdon于2008-06-07 16:12修改过]