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

返回目录列表

 

字段查询/筛选查询

需求

1.根据 帖子标题、发言者、发言日期和标签tag查询。

2.根据自定义查询后,如果是管理者和版主,提供编辑删除或批量删除的功能。

3.提供查询用户所有帖子功能。

4.根据关键字查询,每个帖子都应该有自己的关键字。

5.设计内容关键字,也就是标签tag的设计。

 

模型设计

我们可以将查询条件建模,取名为QueryCriterial,下面首先进行一些建模的理论准备和简单考虑。

查询模型设计是第一步,建立QueryCriterial,按照PoEAA”Query Object”模式:http://www.martinfowler.com/eaaCatalog/queryObject.htmlQueryCriterial封装的查询字段不能够是数据表名或字段名,而应该是模型对象名或字段。

QueryCriterial中,我们封装了查询的条件,以便最后成为SQL语句的where条件,这个过程也就是解释器解释过程。

这个步骤看似简单,实则有一些问题需要反复权衡考量:

第一:需要一个翻译层面,查询条件的字段应该是对象字段,如果我们没有使用HibernateORM,那么这些字段就不能直接成为SQL语句的where条件,需要经过对象字段到数据表字段的转换,这个转换过程必须和其他部分松耦合,是否使用取决于具体项目的持久层选用技术。

还有:页面查询条件有可能不是简单直接针对某个字段查询,不能直接转换到SQL语句的where条件中,而是需要一个解释翻译成SQL语句能够辨识的查询条件。

这个问题在没有ORM情况下考虑起来比较麻烦,在JiveJdon3中,由于我们已经采取模型类字段名和数据表字段名基本相同的策略。

第二:关于查询模型中的字段设计,模型中有哪些字段,根据需求,我们经常需要组合下面几个字段条件查询:

forumId; //帖子所在论坛

publishUser;  //帖子发言者 userId username替代

publishDateDomain; //发言范围日期  查询日期:从某年某月某日到某年某月某日

考虑到日期字段几乎是查询的最基本的条件,而且通过日期约束查询,可以减少查询范围,甚至可以将结果提取到内存中,再在内存中进行各种条件筛选,因此我们将日期字段作为查询模型QueryCriterial基本字段。

查询结果需要排序,我们设计一个ResultSort来表达排序算法:升序 还是降序。

查询模型设计类图如下:

3

QueryCriterial有两个子类,HotMessageCriteria是查询热门帖子的查询条件,MultiCriteria是根据userIdforumIdusername进行组合查询的条件,很显然,如果我们以后有新的查询条件组合,可以继承QueryCriterial实现新的子类。

有了查询条件模型,如何翻译成SQL语句,从而通过SQL实现查询呢?当然,如果我们有ORM作为持久层,就可能不需要这个SQL翻译,但是由于JiveJdon3中翻译还结合批量分页查询功能,可谓一举两得,简洁有效,还是值得在很多场合使用。

 

SQL解释

QueryCriterial转为SQL语句,这是在DAO层实现的,如下代码:

public PageIterator getQueryCriteriaResult(String keyName, QueryCriteria mqc, int start, int count){

    logger.debug("enter getQueryCriteriaResult" );

    try {

    //转换日期作为查询条件

                 StringBuffer where = new StringBuffer( " WHERE modifiedDate >= ? and modifiedDate <= ? ");

                            Collection params = new ArrayList(6);

                            String fromDate = ToolsUtil.dateToMillis(mqc.getFromDate().getTime());

                            String toDate = ToolsUtil.dateToMillis(mqc.getToDate().getTime());

                            params.add(fromDate);

                            params.add(toDate);

                           

   //如果是多字段查询

                   if (mqc instanceof MultiCriteria){

                            MultiCriteria mmqc = (MultiCriteria)mqc;

                            if (mmqc.getUserID() != null){//如果userID不为空,将userID作为查询条件

                                               where.append(" and userID = ?");

                                               params.add(mmqc.getUserID());                  

                            }

                                    

                            if (mmqc.getForumId() != null){//如果forumId不为空,将forumId作为查询条件

                                               where.append(" and forumID = ? ");

                                               params.add(new Long(mmqc.getForumId()));

                            }

                   }

                   //JdonFramework批量查询需要的SQL语句

                   String GET_ALL_ITEMS_ALLCOUNT =

                              "SELECT count(1)  FROM jiveMessage " + where.toString();

                   ///JdonFramework批量查询需要的SQL语句

    //返回主键,主键名称可选,如messageIDthreadID

    //我们也可以将jiveMessage作为变量传入。

                   String GET_ALL_ITEMS =

  "SELECT "+ keyName  +" FROM jiveMessage " + where.toString() + mqc.toResultSortSql();

                            logger.debug("GET_ALL_ITEMS=" + GET_ALL_ITEMS);

            return pageIteratorSolver.getPageIterator(GET_ALL_ITEMS_ALLCOUNT,

                                   GET_ALL_ITEMS, params, start, count);

                   } catch (Exception e) {

                            e.printStackTrace();

                            return new PageIterator();

                   }

    }

   

以上是QueryCriterial转为SQL语句的过程,可以做得更抽象,形成模板。

 

表现层实现

   表现层通过ThreadQueryViewAction来推出查询页面,在这个查询页面有一个scopesessionthreadQueryForm threadQueryForm是查询模型QueryCriterial在界面的映射,threadQueryForm可以在多个Jsp中保存用户输入的查询条件,用户感受比较好,threadQueryForm主要是为查询页面服务的。

<action path="/forum/threadViewQuery"

type="com.jdon.jivejdon.presentation.action.ThreadQueryViewAction"

      name="threadQueryForm" scope="session"

      validate="false">

      <forward name="view" path="/forum/queryView.jsp"/>

      <forward name="result" path="/forum/threadQueryAction.shtml"/>

    </action>      

   查询页面是/forum/queryView.jsp,提交到/forum/threadQueryAction.shtml,/forum/threadQueryAction.shtml中,生成具体的QueryCriterial类型,也就是将threadQueryForm转换为QueryCriterial,然后提交Service及其后台的SQL翻译进行执行。

  <action path="/forum/threadQueryAction"

type="com.jdon.jivejdon.presentation.action.ThreadQueryAction"

      name="threadListForm" scope="request"

      validate="false">

                   <forward name="success" path="/forum/queryView.jsp"/>

         </action>

查询模型深度设计

    之前设计建立了一个查询筛选的初步架构,该架构总体是可伸缩的,灵活的。

    现在在功能上有一个实现问题,需求是想实现查询某段时间内最热门的帖子,也就是回复数最多的帖子,这个功能主要是依靠下面SQL语句实现的:

SELECT threadID, count(1) AS msgCount FROM jiveMessage WHERE modifiedDate >= ? and modifiedDate <= ? GROUP BY threadID ORDER BY msgCount DESC

    这个SQL语句其实不能返回我们所要的真正结果,同时,在设计上也不符合我们要求:这样一条复杂SQL会加重数据库服务器的负载;另外,Evans DDD中的规则筛选设计也无法使用了。

    很显然,我们在这里应该使用Evans DDD的规则筛选设计,在Java服务器内存中进行一次筛选,而不依靠一个复杂的SQL语句一步获得。

    以上SQL语句中有两个条件:一个是时间限制;一个是按回复数大小排列,这两个条件中我们应该选择哪个条件呢?

    如果选择第2个条件,会发生SQL语句将数据库中所有数据都读取的现象,大量数据在内存中再进行时间筛选,会造成性能低下;我们希望从数据库读取到内存中的数据量尽可能的少。

    数据库SQL使用第一个查询条件限制可以实现我们这个目的,通过时间范围的限制,减少了所有数据都出现到内存中的可能。

    实现步骤:

1.       SQL改写为:

SELECT threadID FROM jiveMessage WHERE modifiedDate >= ? and modifiedDate <= ?

这样输出的ForumThread是在某段时间范围内发生修改的主题。

 

2.       在上述结果中进行排序,将主题回复数最多的排在前面,排序功能我们可以使用CollectionComparator实现。如下:

 

class ThreadReplyComparator implements Comparator {

               public int compare(Object x, Object y) {

                  ForumThread a = (ForumThread) x, b = (ForumThread) y;

                 

                  int replya = a.getForumThreadState().getMessageCount();

                  int replyb = b.getForumThreadState().getMessageCount();

                  

                  if (replya == replyb) {

                        return 0;

                  } else if (replya > replyb) {

                     return 1;

                  } else if (replya < replyb) {

                     return -1;

                  }

                  return 0;

               }

            }

     现在的问题是:排序后的Collection是一个从大到小的排列过程,如何将这个CollectionJdonFramework的自动分页功能结合在一起呢?

   JF自动分页功能实现主要围绕一个PageIterator展开的,所以只要我们在业务层Service中根据上述排列结果创建一个新的PageIterator就可以了。

3.我们开发一个管理组件QueryManager专门实现制造专门的PageIterator,另外考虑到性能,我们需要结合缓存结果,也就是排序后的ID结果。

public PageIterator getHotThreadPageKeys(QueryCriteria qc, int start,

                            int count) {

                   List resultSortedIDs = getHotThreadKeys(qc); //从缓存中获得结果

                   if (resultSortedIDs.size() > 0){

                            List pageIds = new ArrayList(resultSortedIDs.size());

                            for (int i = start; i < start + count; i++) { //分页查询

                                     if (i < resultSortedIDs.size()){

                                               pageIds.add(resultSortedIDs.get(i));       

                                     }else

                                               break;

                            }                         

         //构造一个PageIterator

                            return new PageIterator(resultSortedIDs.size(), pageIds.toArray());

                   }else

                       return new PageIterator();

         }

 

 

 

 

 

搜索

搜索主要使用Compass+Lucene实现,Compass的好处就是能够直接对模型Model实现象Hibernate那样的基于Lucene的增删改查,从而使得我们的整个搜索设计是一个面向模型对象的设计。

com.jdon.jivejdon.dao.search.MessageSearchProxy是实现基于Lucene的增删改查,语法非常类似HibernateMessageSearchProxy是嵌入MessageDaoDecorate中被调用的。

另外,为了实现对以往旧数据建立Luncene索引,com.jdon.jivejdon.dao.search.ReBuildIndex就是实现这个重新建立索引作用的。

 

 

站内消息

模型设计图:

3

 

ShortMessage为主模型,站内消息分收到的消息和发出的消息两种,根据这两种,分别建立ShortMessage的两个子类FromShortMessageToShortMessageFromShortMessage中目前没有任何与收消息相关的特殊特征;而ToShortMessage则有发出消息的特征,就是当对方阅读该发出的消息时,必须标记该发出消息为已读。

对应模型的Jdon框架配置就如下,分别为FromShortMessageToShortMessage建立相应CRUD:

  <model key="msgId"

           class="com.jdon.jivejdon.model.FromShortMessage">

           <actionForm name="fromShortMessageForm"/>

           <handler>

              <service ref="shortMessageService">             

                  <getMethod    name="getFromShortMessage"/>

                  <createMethod name="saveShortMessage"/>

                  <updateMethod name="updateShortMessage"/>

                  <deleteMethod name="deleteShortMessage"/>

              </service>

           </handler>

       </model>         

      

       <model key="msgId"

           class="com.jdon.jivejdon.model.ToShortMessage">

           <actionForm name="toShortMessageForm"/>

           <handler>

              <service ref="shortMessageService">

                   <initMethod   name="initShortMessage"/>                   

                  <getMethod    name="getToShortMessage"/>

                  <createMethod name="saveShortMessage"/>

                  <updateMethod name="updateShortMessage"/>

              </service>

           </handler>

       </model>         

Struts-config.xmlActionForm的配置

<form-bean name="fromShortMessageForm"  type="com.jdon.jivejdon.presentation.form.ShortMessageForm"/>

    <form-bean name="toShortMessageForm"  type="com.jdon.jivejdon.presentation.form.ShortMessageForm"/>

在界面表现层,虽然命名为两个ActionForm : fromShortMessageFormtoShortMessageForm,但是它们实则是同一个Form

那么我们在界面进行处理时,需要分清楚当前是收件箱还是发件箱,如果是收件信息列表,那么对应的是fromShortMessageForm

以上消息模型设计可以总结一些规律:

很多情况下:同一个实体在不同情况下表现为不同形式,尽管这些形式不同,但他们实体内容基本一致,那么是否为他们的不同表现形式建立不同实体子类呢?关键是:这种形式不同是否会影响业务不同的操作,比如收到消息和发送消息初一看是否两者消息内容是一样的,我们很容易使用抽象思维,用一个消息模型来抽象他们,可以达到精炼目的;但是这种抽象消息在不同状态情况下就有不同形式,在收件箱中就是收到消息;在发件箱中就是发送的消息,它们又从抽象落实具象的不同,所以,我们就设计上述模型图,体现抽象和具象区别不同。做到收发有余。

 

 

更多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
_×
您有新消息