用户操作
[即时聊天] [发私信] [加为好友]
michaelID:MikeDogSong
2818次访问,排名2万外好友0人,关注者0
abc
MikeDogSong的文章
原创 10 篇
翻译 1 篇
转载 0 篇
评论 1 篇
最近评论
fdytxz:www.meinv880.cn
www.jipinjiading36.cn
文章分类
    收藏
      相册
      books
      存档
      软件项目交易
      订阅我的博客
      XML聚合  FeedSky
      订阅到鲜果
      订阅到Google
      订阅到抓虾
      订阅到BlogLines
      订阅到Yahoo
      订阅到GouGou
      订阅到飞鸽
      订阅到Rojo
      订阅到newsgator
      订阅到netvibes

      原创 数据库查询的设计收藏

      新一篇: javax.swing.text | 旧一篇: netbeans Explorer API overview

      查询的设计方法介绍

      大家知道,基本上大部分的业务系统或产品的业务逻辑实现,都可以总结为数据库的CRUD和查询。对于数据库的CRUD操作,这些都在webber.core.frame.db中有体现,这里就不再赘述。下面我想说一下对于数据库查询的设计思路。

      举例子仍然使用用户系统,我要实现一个根据用户姓名,账号,年龄段来查询用户信息的查询:

      按照平时的做法,我们会有这么一个Command

      public class UserCommand extends AbstractCommand

      {

            Adapter adapter = AdapterFactory.createAdapter( “dbName”,true );

            public String processSearch()

            {

                  //

                  // 获取查询条件

                  String name = request.getParameter( “name” );

                  String account = request.getParameter( “account” );

                  String creationDate = request.getParameter( “creationDate” );

                 

                  //

                  // 拼查询sql语句,对获取的参数进行判断

                  String sql = “select * from user where 1=1 “;

                  if( StringHelper.isEmpty(name) )

      sql += “and name like ‘%” + name + “%’ ”;

                  if( StringHelper.isEmpty(account) )

                         sql += “and account like ‘%” + account + “%’ ”;

                  if( StringHelper.isEmpty(creationDate) )

                  {

                         sql += “and creationdate=” + creationDate + “ “;

                  }

                  //

                  // 查询数据库,把得到的结果发给页面显示

                  List result = adapter.find( User.class,sql,null );

                  request.setAttribute( “QUERY_RESULT”,result );

                  return “userList.jsp”;

            }

      }

      这个例子我们可以看到,虽然使用Command模式把jsp页面的代码减少了,可是我们很容易把jsp的代码写到command中,实际上MagicJSP的问题没有解决,只是转移到MagicCommand了!

      如果我们增加一个业务层来处理查询的问题,这样Command会瘦很多:

      public class UserManager

      {

            public List find( String name,String creationDate,String account );

      }

       

      这个时候,只需要在Command中调用这个方法既可:

      UserManager um = new UserManager();

      List result = um.find(

      request.getParameter(“name”),

      request.getParameter(“creationDate”),

      request.getParameter(“account”) );

      这样的解决方法很简单,但有一个缺点,参数太多,不好记,更不好用。为了解决这个问题,我使用一个单独的类来包含这些参数,在提交查询条件的时候构造这个参数类,然后把这个参数类提交给方法:

      public interface ResultFilter

      {

            public String getFilteredStatement();

      }

      其中getFilteredStatement()就是专门根据查询条件来生成sql语句的,然后上面的find方法就可以写成:

      public List find( ResultFilter filter );

      多简单,在实现时,我只需要调用filtersetName(),setAccount(),setCreationDate()方法就可以完成参数的赋值:

      public class UserFilter implements ResultFilter

      {

            private String name = “”;

            private String creationDate = “”;

            private String account = “”;

           

            public void setAccount( String acc ){ account = acc; }

            public void getAccount() { return account; }

              //

              // 省略其他两个属性的settersgetters

             

              //

              // 刚才写的拼串的代码可以放到这里

              // 注意:这个方法中并没有select语句,from语句,因为我们封装的

      // 是查询条件而不是查询内容,如果加了select语句,

      // 那么这个filter的重用的可能就会降低,甚至不能重用

              public String getFilteredStatement()

      {

            String sql = “”;

                  if( StringHelper.isEmpty(name) )

      sql += “and name like ‘%” + name + “%’ ”;

                  if( StringHelper.isEmpty(account) )

                         sql += “and account like ‘%” + account + “%’ ”;

                  if( StringHelper.isEmpty(creationDate) )

                  {

                         sql += “and creationdate=” + creationDate + “ “;

                  }

      return sql;

      }

      }

      方法UserManager.find可以用下面的方式来实现,请注意方法的参数定义:

      List find( UserFilter filter )

      {

            List result = adapter.find(

                  User.class,

      “select * from user

      where 1=1 “ + filter.getFilteredStatement(),null );

            return result;

      }

       

      然后对Command就可以这样

      public String processSearch()

      {

            UserFilter uf = new UserFilter();

            uf.setAccount( request.getParameter(“account”) );

            uf.setName( request.getParameter(“name”) );

            uf.setCreationDate( request.getParameter(“creationDate”) );

            UserManager um = new UserManager();

            request.setAttribute( “QUERY_RESULT”,um.find(uf) );

            return “userList.jsp”;

      }

       

      我们还可以在UserFilter 中增加排序、部分选择的功能:

      class UserFilter

      {

            public int getStart(){}

            public int getOffset(){}

            public boolean isDesc(){}

            public void setOrderBy( String orderBy ){}

            public String getOrderBy(){}

      }

      考虑到所有的查询基本上都会有这些功能,因此,把这些功能抽象出来,形成一个独立的类,

      webber.core.frame.db.list.AbstractFilter,如果要实现你自定义的查询条件,可以继承这个类来实现。

      ResultFilter的重用机制:

      大家都知道:如果在查询条件中包含日期,那么日期条件的格式会随数据库的不同有不同的变化,我们还是考虑刚才的UserFilter,我们要查询某个时间段内注册的用户:

      public abstract UserFilter extends AbstractFilter // 我继承了AbstractFilter

      {

            private String name = “”;

      private DateRange [mike1] range = null;

            public void setDateRange( Date dateStart,Date dateEnd )

            {

                  range = new DateRange( dateStart,dateEnd );

            }

           

            //

            // 这个方法我们让他抽象,继承我的类必须实现它来满足不同数据库查询语句的要求

            public abstract String getDateRangeStatement( DateRange range );

            public String getFilteredStatement()

            {

                  StringBuffer sb = new StringBuffer();

                  sb.append( range == null

                         ? “” : getDateRangeStatement( range ) + “ “ );

                  sb.append( StringHelper.isEmpty(name)

                         ? “” : “and username like ‘%” + name + “%’ “);

                  //

                  // super.getFilteredStatement()返回按照某个字段进行排序的sql语句

                  return sb.append( super.getFilteredStatement() ).toString();

            }

      }

      对于MySql的查询条件,我们可以这么写:

      public class MySqlUserFilter extends UserFilter

      {

            public String getDateRangeStatement( DateRange range ){

                  return “and creationdate between ‘” +

      DateHelper.parseString( range.getStart() ) + “’ and ‘“ +

      DateHelper.parseString( range.getEnd() ) + “’ “;

            }

      }

       

      对于Access数据库,我们就可以这么写

      public class AccessUserFilter extends UserFilter

      {

            public String getDateRangeStatement( DateRange range ){

                  return “and creationdate between #” +

      DateHelper.parseString( range.getStart() ) + “# and #“ +

      DateHelper.parseString( range.getEnd() ) + “# “;

            }

      }

      大家可能看到了,由于从UserManager.find()方法返回的是个List(或Iterator),这样的方式和当前的BaseList翻页机制不兼容,也就是说没有办法使用BaseList的翻页机制,因此我提供了webber.core.frame.db.list.Title这个类来专门生成翻页信息:

      Title title = new Title( request,”formName”,total,rowOfPage );

      Ø       第一个参数大家都知道,request

      Ø       第二个是页面表单的名称,和BaseList.setFormName()一样的

      Ø       total是指Title显示的数据总量是多少

      Ø       rowOfPage是指每页显示多少条,会根据total来计算当前页面和页面数量。

      需要显示的时候,可以使用Title的方法:

      Title.show( out,””,”” );既可

      Ø       out:javax.servlet.jsp.JspWriter 就是jsp页面中的out

      Ø       后两个参数是显示翻页时按钮的样式和文字的样式

      要获取当前页面的记录开始编号和记录数量可以使用

      Title.getStart()

      Title.getOffset(),获取每页的记录条数

      象上面的用户查询的例子我们可以这样:

      class UserManager

      {

            //

            // 为了使用翻页机制,我们需要在find方法中根据

      // UserFilterstartoffset来选择特定的记录返回,

      // 而不是上面例子的不加限制的返回

      // 这个时候就需要使用webber.core.frame.util.list.IteratorProvider

      public Iterator [mike2] find( UserFilter filter )

      {

            // 和上面相同,返回包含User.class的对象

            List result = adapter.find( ... );

            return new IteratorProvider(

      result,filter.getStart(),filter.getOffset() ){

                  public Object fetch( Object key ){

                         return ( User ) key;

                  }

      }

      [mike3] }

      //

      // 返回某个查询结果的数量

      // 看到这个,我们就会明白为什么不能在UserFilter中包含select语句了

      public int total( UserFilter filter )

      {

            List result = adapter.find(

      “select count(1) as c from user “ +

      “where 1=1 “ + filter.getFilteredStatement(),null );

      return StringHelper.parseInt(

            String.valueOf(

            ((HashMap)result.get(0)).get(“c”)) );

      }

      }

      对于Command可以这样来写

      class UserCommand extends AbstractCommand

      {

            public String processSearch()

            {

                  UserManager um = new UserManager();

                  //

                  // create filter

                  // 先查总数,根据总数和每个页面的记录数量来计算页数,

                  UserFilter uf = ...;

                  int total = um.total( uf );

       

                  //

                  // 分页开始

                  Title t = new Title( request,”users”,total,10 );

       

      //

      // 根据分页来选择当前页面的数据:

      uf.setOffset( t.getOffset() );

      uf.setStart( t.getStart );

      Iterator it = um.find( uf );

       

      request.setAttribute( “QUERY_RESULT”,it );

      return “users.jsp”;

      }

      }

      对于jsp页面来说,直接获取迭代显示就可以了

      <%

            int i = 0;

            Iterator users = ( Iterator ) request.getAttribute( “QUERY_RESULT” );

            while( users!=null && users.hasNext() ){

                  User user = ( User ) users.next();

                  out.println( “<tr “ +

      (i%2==0?”listContentDark”:”listContentBright”) + “>” +

      “<td>” + user.getName() + “</td>” +

      ....

      );

      i++;

      }

      %>

      代码分散的比较均匀,而且一个类只关心一块,就这样把复杂问题简单化!享受编程。。。享受生活!!


       [mike1]DateRange:时间段对象,包含一个开始日期和结束日期,DateRange.getStart();DateRange.getEnd()

      详细信息请参见webber.core.frame.util中的定义

       [mike2]我使用迭代

       [mike3]会产生一个根据result的子序列为内容的迭代,开始于filter.getStart()的位置结束于filter.getStart()+filter.getOffset()的位置

      发表于 @ 2007年04月09日 14:47:00|评论(loading...)|编辑

      新一篇: javax.swing.text | 旧一篇: netbeans Explorer API overview

      评论:没有评论。

      发表评论  


      登录
      Csdn Blog version 3.1a
      Copyright © michael