zoukankan      html  css  js  c++  java
  • Delphi的ORM工具:InstantObjects 初探

    前言:

    说起来,用InstantObjects(以下简称IO)编程还是走了不少弯路的,因为网上资料太少,好在有IO自带了几个demo,尤其是那个Primer.dpr例程,里面有若干IQL的示例,再加上chm文档的参考,大体上还是有一个基本的概念的。


    0.缘起

    今天写一个博客搬家程序,写着写着,就觉得哪里不对了,因为涉及到文章及图片,需要用到数据库将这些内容的相关信息保存起来,以便增量下载。程序要用到 用户/文章/图片 三级对象的模型,两两是一对多的关系。正要用熟悉的ADO方式来做,还是觉得太笨了。要是有ORM的方式就好了。想起来曾经看到过有类似的工具介绍,上网一查,果然找到了,就是InstantOjbects。

    下载,安装。一边看着文档,一边在IDE中新建一个工程,再新建一个unit,起名TestModel.pas,在菜单中选择view,进入设计器。很快的就设计好了三级对象各自属性及互相关系。

    轮到生成数据库的时候有问题了,怎么new的时候没有反应呢,为什么文档中的截图里有那么多种数据库可以支持呢?重新看安装文档,原来自己漏掉了好几步。

    补上所有的安装步骤,再次建立数据库,成功了。

    下一步就是用IO进行编程了。今天太晚了,明天继续。

    1.建模

    之所以打算用博客搬家的程序做为第一个IO编程的试验,是因为它的业务逻辑及数据结构都超简单,而且涉及了一对多的典型的master-detail模型,正好拿来练手。

    言归正传,开始介绍我心目中的数据库结构:

    ------------------------------

    Blogger表:

        BlogID: string

        Articles: (该用户发表的所有文章的集合)

    Article表:

        Title: string

        URL: string

        Filename: string

        Datetime1: (发表时间)

        Pictures: (该文章下所有图片的集合)

    Picture表:

        URL: string

        Filename: string (该图片在本地保存的文件名)

        downloaded: boolean (用于在下次抓取时判断是否需要重新下载)

    ------------------------------

    o 新建一个unit,保存为ModalUnit.pas。再新建一个mdb数据库,取名为db1.mdb。

    o 打开InstantObjects Modal Explorer, 单击select units按钮,选择刚刚建立的ModalUnit.pas

    o 在窗口空白处右击鼠标,选择New Class,开始创建我的第一个类 TBlogger (注:原来曾经用TUser做类名,结果出了一个古怪的select语句错误,原来是和保留字冲突了,所以我取名为TBlogger。)

    o 如图填写好TBlogger的属性后,选择Attributes页片,增加各项属性。因为还没有建立TArticle类,所以暂时先不设置它的Articles属性,只设一个BlogID即好。

    o 现在我点击Build Database按钮,进入到创建数据库(Database Builder)的页面。右击窗体空白处,选择New|Ado Connection(最初我在安装IO的时候,选择了Ado做为处理要使用的broker,所以现在可以看到有相应的菜单项可用。)我命名为conn,然后右击conn,选择edit,进入Ado Connection的对话框,去掉Login Prompt前的对勾,并选择好自己要用到的数据库db1.mdb,点OK就可以了。然后,在Database Builder窗口中,单击Build按钮,就会自动为我建立好数据库的各个对应表了。

    到了这里,暂时可以告一段落。双击建好的db1.mdb,检查是否如同所期望的那样,建立了表格。

    如果没问题,继续回到前面的步骤中去,用类似方式,建立好TArticle和TPicture及其各个字段。要注意的是,TBlogger的Articles字段,以及TArticle的Pictures字段,它们的Storage Kind一栏应选External,表示将使用外部表保存相应属性。

    (注:Pictures字段是后期我在程序中编码添加的,而非象其它字段那样,纯由IO自动生成。)

    2.访问IO

    建好数据库之后,下一步就是编码工作了。以下着重展示如何存取InstantObjects对象。(至于下载和解析网页的代码,这里从略。)

    Blogger表中保存的是新浪博客帐号,如果表中不存在相应记录,则创建之;如果已有,则提取出来。

      blogger := Dm.GetAnUser('some_name');

      ... ...

    function TDM.GetAnUser(const BlogID: string): TBlogger;
    begin
      with MyConnector.CreateQuery do
      try
        Command:= 'SELECT * FROM TBlogger WHERE BlogID = "' + BlogID + '"';
        Open;
        if ObjectCount > 0 then
        begin
          result := TBlogger.Retrieve((Objects[0] as TBlogger).id);
        end
        else
        begin
          result := TBlogger.Create();
          result.BlogID := BlogID;
          result.ClearArticles;
          result.Store();
        end;
      finally
        Free;
      end;
    end;


    3. 异常

    这里有一个问题,那就是当我访问TBlogger对象的时候,会出现一个异常:

      Error storing object TBlogger('C371D6060453A84E9E169B2EBB2ECB74'): "Field 'Articles' not found"

    好蛋疼啊!为什么会这样,明明所有的内容都已创建好了,看上去一切正常的啊。。。

    既然提示Articles字段不存在,那么就试着创建一个吧。我手工建立了一个string类型的Articles字段,再次运行程序。可以了。

    我往Blogger对象中增加文章后,检查了一下Articles字段的内容,发现里面是一些二进制字符,以及新增文章的ID。可以想象,IO就是这样,向Articles字段中增加对Article表的关联的。

    string类型有长度限制,我试着改为备注型,结果运行几次后,出现数据错误。估计是字段类型仍然没有选对。最后,我选择了二进制类型,一切OK。

    我让程序在一开始的时候,就自动检查和创建相应的字段,以避免出错:

    procedure TDM.DataModuleCreate(Sender: TObject);
    begin
      conn.ConnectionString := GenAdoConnectionString(GetStartDir + 'db1.mdb');
      conn.Open;

      // 以下查找是否有Blogger.Articles字段,如没有,则增加之
      if not FieldExists(conn, 'blogger', 'articles') then
        conn.execute('alter table blogger add Articles IMAGE');  // IMAGE类型在Access中显示为"OLE对象"

      // 以下查找是否有Article.Pictures字段,如没有,则增加之
      if not FieldExists(conn, 'article', 'Pictures') then
        conn.execute('alter table article add Pictures Binary');       // Binary类型在Access中显示为"二进制",与IMAGE类型最终表现相同
    end;

    (分析:我估计是IO对ADO的支持不是特别好,又或者ADO中,Access数据库缺乏某些关键特性,造成Articles字段必须得手工添加。另外,IO替我自动创建的Blogger_Articles表和Article_Pictures表,始终没有看见有数据进入,一直都是空的。估计用bde或firebird等数据库,应该可以运行得正常些吧)


    4. 总结

    不管怎么说,我的程序最终运行成功了,数据存取正常。希望这是一个良好的开端,将来能带来更多编程上的便利。

    --------------------

    后记:关于IQL

    IQL是Instant Query Language的简称,上网居然找不到它的语法说明。。。汗。。。

    好在从Primer.dpr中找到一些示例:

    All contacts: SELECT * FROM ANY TContact

    All employees: SELECT * FROM TPerson WHERE Employer.Name <> ""

    All employers:SELECT DISTINCT Employer FROM TPerson

    Employees at customers: SELECT * FROM TPerson WHERE Employer.Category.Name = "Customer" ORDER BY Name

    My friends: SELECT * FROM TPerson WHERE Category.Name = "Friend"

    Contact from Alabama: SELECT * FROM ANY TContact WHERE City = "Alabama" ORDER BY Name

    Corporations ordered descending by city: SELECT * FROM TCompany WHERE Name LIKE "%Corp%" ORDER BY City DESC

    Employees from same city as their employer: SELECT * FROM TPerson WHERE City = Employer.City

    如果有朋友有更其它资料希望能在这里交流一下。

  • 相关阅读:
    dutacm.club_1094_等差区间_(线段树)(RMQ算法)
    dutacm.club_1087_Common Substrings_(KMP)_(结合此题通俗理解kmp的next数组)
    dutacm.club_1089_A Water Problem_(dp)
    14年第五届蓝桥杯第八题_地宫取宝_(记忆化搜索)
    14年第五届蓝桥杯第七题_蚂蚁感冒_(思维)
    dutacm.club_1085_Water Problem_(矩阵快速幂)
    HDU_2476_String painter_(区间dp)
    第五届蓝桥杯校内选拔第七题_(树型dp)
    第五届蓝桥杯校内选拔第六题_(dfs)
    15年第六届蓝桥杯第九题_(矩阵快速幂优化的动态规划)
  • 原文地址:https://www.cnblogs.com/anjo/p/1964384.html
Copyright © 2011-2022 走看看