Delphi将数据库表作为类

时间:2011-09-21 15:12:15

标签: delphi oop ado

我的一个朋友问我如何在运行时创建一个“映射”数据库表的类。他正在使用ADO连接到数据库。

我的回答是,他可以使用'table_name中的第一行'填充ADOQuery,设置与数据库的连接,打开查询,然后在ADOQuery.Fields上使用循环,他可以获得FieldName和FieldType从表中的所有字段。通过这种方式,他可以将表中的所有字段及其类型作为类的成员。

他的问题有其他解决方案吗?

5 个答案:

答案 0 :(得分:6)

@RBA,一种方法是定义要映射为“已发布”的类的属性,然后使用RTTI循环浏览属性并将数据集行分配给每个属性。

示例:

TMyClass = class
private
  FName: string;
  FAge: Integer;
published
  property Name: string read FName write FName;
  property Age: Integer read FAge write FAge;
end;

现在,进行查询:

myQuery.Sql.Text := 'select * from customers';
myQuery.Open;
while not myQuery.Eof do
begin
  myInstance := TMyClass.create;
  for I := 0 to myQuery.Fields.Count - 1 do
    SetPropValue(myInstance, myQuery.Fields[I].FieldName, myQuery.Fields[I].Value);
  // now add myInstance to a TObjectList, for example
  myObjectList.Add(myInstance);  
  Next;
end;

此简单示例仅在查询返回的所有字段在类中具有完全匹配时才有效。

一个更精彩的例子(由你决定)应首先获得类中的属性列表,然后检查返回的字段是否存在于类中。

希望这有帮助, 莱昂纳多。

答案 1 :(得分:3)

不是真正的阶级,而是非常相似的东西。前段时间我在博客上写了一个可能符合你需求的解决方案。它使用可调用的自定义变量进行字段映射,使您可以访问类的属性等字段。

可以找到Delphi帮助here,两部分博客文章是herehere。源代码可以在CodeCentral 25386

中找到

答案 2 :(得分:2)

这就是所谓的ORM。也就是Object-relational mapping。您有几个可用于Delphi的ORM框架。例如,请参阅this SO question

当然,不要忘记查看我们对Delphi 6到XE2的小mORMot - 它能够直接使用OleDB(没有ADO层)或其他提供程序连接到任何数据库。有很多文档可用(超过600页),包括一般设计和架构方面。

例如,使用mORMot,数据库Baby表在Delphi代码中定义为:

/// some enumeration
// - will be written as 'Female' or 'Male' in our UI Grid
// - will be stored as its ordinal value, i.e. 0 for sFemale, 1 for sMale
TSex = (sFemale, sMale);

/// table used for the Babies queries TSQLBaby = class(TSQLRecord)   private     fName: RawUTF8;     fAddress: RawUTF8;     fBirthDate: TDateTime;     fSex: TSex;   published     property Name: RawUTF8 read fName write fName;     property Address: RawUTF8 read fAddress write fAddress;     property BirthDate: TDateTime read fBirthDate write fBirthDate;     property Sex: TSex read fSex write fSex; end;

通过将此TSQLBaby类添加到客户端和服务器通用的TSQLModel实例,将创建相应的 Baby 表由Framework中的数据库引擎。然后,通过RESTful链接(通过HTTP,使用JSON进行传输),客户端和服务器端都可以使用这些对象。所有SQL工作('CREATE TABLE ...')都由框架完成。只需用Pascal编写代码,一切都为您完成。甚至所需的索引也将由ORM创建。你不会错过任何'或;在你的SQL查询中。

我的建议不是从头开始编写自己的ORM。

如果您只想将某些数据库表与对象映射,则可以轻松完成。但是你花在它上面的时间越多,你的解决方案就越复杂,你就会彻底重新发明轮子!因此,对于小型应用程序,这是一个好主意。对于将来可能增长的应用程序,请考虑使用现有(仍然维护)的ORM。

答案 3 :(得分:2)

代码生成工具(例如O / RM解决方案中使用的代码生成工具)可以为您构建类(这些被称为很多东西,但我称之为模型)。

您的需求并不完全清楚(也已经阅读了您的评论),但您可以使用这些工具来构建它,而不仅仅是模型。您可以构建包含字段/属性关联列表或数据库架构标志的类,例如“字段X< - >主键标志”等。

已经有一些,但如果你想自己建立一个完整的O / RM,你可以(我做)。但这是一个更大的问题:)它通常涉及添加知道如何在数据库中查询,插入,删除和更新模型的代码生成(称为CRUD方法)。这并不难,但是随后你就会失去与Delphi数据控件集成的能力,你必须为此制定解决方案。虽然您不必生成CRUD方法,但需要CRUD支持以完全消除手动更改的需要,以便以后适应数据库模式更改。

您的一条评论表明您希望在不使用数据库连接的情况下进行一些架构查询。是对的吗?我在模型中通过使用我可以在运行时查询的属性进行装饰来完成此操作。这需要Delphi 2010及其新的RTTI。例如:

[TPrimaryKey]
[TField('EmployeeID', TFieldType.Integer)]
property EmployeeID: integer read GetEmployeeID write SetEmployeeID;

使用RTTI,我可以获取模型的实例,并通过查找具有TPrimaryKeyAttribute属性的字段来询问哪个字段代表主键。使用上面的TField属性在属性和数据库字段之间提供了一个链接,它们不必具有相同的名称。它甚至可以提供转换类作为参数,因此它们不需要具有相同的类型。有很多种可能性。

我使用MyGeneration并为此编写自己的模板。它很容易,甚至可以在O / RM之外为您打开一个完整的可能性世界。

MyGeneration(免费代码生成工具) http://www.mygenerationsoftware.com/ http://sourceforge.net/projects/mygeneration/

MyGeneration教程(我的博客) http://interactiveasp.net/blogs/spgilmore/archive/2009/12/03/getting-started-with-mygeneration-a-primer-and-tutorial.aspx

我花了大约15分钟编写了一个MyGeneration脚本,可以完成您想要的操作。您必须为您在XML中使用的数据库定义Delphi类型,但此脚本将完成剩下的工作。我没有对它进行过测试,它可能会想要扩展它,但它会让你知道你遇到了什么。

<%# reference assembly = "System.Text"%><%

public class GeneratedTemplate : DotNetScriptTemplate
{
    public GeneratedTemplate(ZeusContext context) : base(context) {}

    private string Tab()
    {
        return Tab(1);
    }

    private string Tab(int tabCount)
    {
        System.Text.StringBuilder sb = new System.Text.StringBuilder();
        for (int j = 0; j < 1; j++)
            sb.Append("  ");    // Two spaces
        return sb.ToString();
    }

    //---------------------------------------------------
    // Render() is where you want to write your logic    
    //---------------------------------------------------
    public override void Render()
    {
        IDatabase db = MyMeta.Databases[0];

%>unit ModelsUnit;

interface

uses
  SysUtils;

type
<%
        foreach (ITable table in db.Tables)
        {
%>
<%=Tab()%>T<%=table.Name%>Model = class(TObject)
<%=Tab()%>protected
<%          foreach (IColumn col in table.Columns)
            {
%><%=Tab()%><%=Tab()%>f<%=col.Name%>: <%=col.LanguageType%>;
<%          }%>
<%=Tab()%>public
<%          foreach (IColumn col in table.Columns)
            {
%><%=Tab()%><%=Tab()%>property <%=col.Name%>: <%=col.LanguageType%> read f<%=col.Name%> write f<%=col.Name%>;
<%          }%>
<%=Tab()%><%=Tab()%>
<%=Tab()%>end;<%
        }
%>

implementation

end.

<%
    }

}
%>

以下是上述脚本生成的表类之一:

TLOCATIONModel = class(TObject)
  protected
    fLOCATIONID: integer;
    fCITY: string;
    fPROVINCE: string;

  public
    property LOCATIONID: integer read fLOCATIONID write fLOCATIONID;
    property CITY: string read fCITY write fCITY;
    property PROVINCE: string read fPROVINCE write fPROVINCE;


  end;

答案 4 :(得分:1)

根据数据库的不同,您可以查询INFORMATION_SCHEMA表/视图以了解所需内容。我在我创建的架构中完成了这项工作,并且仍在数据库应用程序中使用。首次连接到数据库时,它会查询“数据字典”类型信息并将其存储以供应用程序使用。