65.9K
CodeProject 正在变化。 阅读更多。
Home

适用于 .NET 的后关系型数据库介绍,Matisse - 第二部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (24投票s)

2004年2月29日

7分钟阅读

viewsIcon

119359

为 .NET 介绍新一代数据库的逐步指南:模式定义

引言

上一篇文章中,我介绍了一种适用于 .NET 的替代数据库解决方案,即后关系型数据库 Matisse。我使用了 SQL 编程演示来展示其面向对象的特性,例如用户定义类型、继承和多态性。

在开始编写 Matisse 的 .NET 程序之前,我们需要了解如何定义数据库模式。尽管这与关系型数据库略有不同,但并不难。如果您熟悉 UML,那么您几乎可以开始了。模式定义中的关键区别在于您定义关系的方式。关系的工作方式类似于关系型主键-外键概念,但它们具有一些特定的优势,我将在本文中进行演示。

本文是一个系列的一部分。接下来的文章将逐步介绍 .NET 编程,这是本系列文章的最终目标。

UML、SQL DDL,还是 ODL?

Matisse 支持三种类型的模式定义语言:UML、SQL DDL 和 ODL(对象定义语言)。那么,您应该使用哪一种呢?答案是您喜欢的任何一种,因为您可以根据需要随时在它们之间切换。例如,您可以先用 SQL DDL 定义数据库模式,然后将模式导出到 ODL 文件以供以后更新。

Import/Export Schema

这里有一些提示

当您需要进行一些测试时,例如添加一个带有几个属性的类,或者定义/删除属性上的索引,SQL DDL 都非常方便。DDL 在将关系型模式迁移到 Matisse 时也很有用。

如果您熟悉 C++ 或 IDL(接口定义语言),ODL 就很容易理解。使用 ODL,您可以以声明式的方式定义您的模式。

Rational Rose UML 提供了对模型更直观的理解,并有助于将大型模式分类到包中。但是,Rational Rose 不包含在 Matisse 的标准安装中。

类和属性

Matisse 的模式定义中只有三个基本元素:类、属性和关系。首先,让我们看看类和属性。

类类似于关系型表,属性类似于关系型列。主要区别在于:

  • 您可以使用类继承
  • 您可以为类定义方法
  • 属性可以是列表类型,例如整数列表

这是 DDL 中类 `Manager` 的定义,它继承自 `Employee` 并具有 `Title` 属性

CREATE CLASS Manager UNDER Employee (
  Title VARCHAR(64)
);

请注意,“CREATE CLASS”和“CREATE TABLE”是相同的。默认情况下,由 DDL 定义的属性是 NULLABLE 的。如果 Title 属性不能为 NULL,则定义应使用 NOT NULL

CREATE CLASS Manager UNDER Employee (
  Title VARCHAR(64) NOT NULL
);

您是否需要为每个类定义主键?基本上,答案是否定的,因为每个类都有一个隐式的 OID 属性。但是,如果用作主键的关系型列在应用程序域中有意义,您显然会包含这些属性在类中。

所有可用的内置数据类型都列在Matisse 数据类型参考中。

关系

模式定义的第三个基本元素是关系。关系定义了两个类之间的关联。它的工作方式类似于关系型主键-外键概念,但关系更直观、更易于维护,并且查询速度更快。

UML Diagram

我将使用与我在第一篇文章中使用的相同的模式。UML 图中有两个关系:ManagedBy/ManagesMembers/WorksIn

第一个关系 ManagedBy/ManagesProject 类与 Manager 类关联起来,并施加两个约束:

  1. 一个 Manager 管理零个或多个项目,并且
  2. 一个 Project 必须始终由一个 Manager 管理。

如果您使用 Rational Rose,您只需将图导出到 Matisse 数据库。然后,数据库就有了带约束的关系。等效的 SQL DDL 语句的相关部分如下所示:

CREATE CLASS Manager UNDER Employee (
  /* other properties here */
  Manages REFERENCES (Project) 
    INVERSE Project.ManagedBy
);
CREATE CLASS Project (
  /* other properties here */
  ManagedBy REFERENCES (Manager) 
    CARDINALITY (1, 1)
    INVERSE Manager.Manages
);

INVERSE 是一个新语法,用于指示两个引用(ManagesManagedBy)彼此相关。另一个新语法是 CARDINALITY,它指定可以参与关系的对象的最小和最大数量。CARDINALITY 可以省略,在这种情况下,关系的基数是 0 到多(Manager 类中的 Manages 的情况)。尽管最常见的基数是 (0, 1)、(1, 1)、(0, n) 和 (1, n),但您可以使用任何数字,例如 (0, 5)。

Matisse 模式定义让我非常喜欢的一些方面是,您不需要编写触发器来维护引用完整性,不必担心父行删除或更新后出现孤立行,也不必使用一些插入行的技巧。您永远不会遇到孤立行(或悬空引用)问题。Matisse 始终强制执行引用完整性。

另一个关系 Members/WorksInEmployee 类和 Project 类以多对多基数关联起来。在关系型数据库中,您在这种情况下需要引入一个中间表,但在 Matisse 中则不需要。您只需表达多对多关系。

CREATE CLASS Project (
  -- other properties here
  Members REFERENCES (Employee) 
    CARDINALITY (1, -1)   -- here -1 indicates many
    INVERSE Employee.WorksIn
);

 
CREATE CLASS Employee (
  -- other properties here
  WorksIn REFERENCES (Project) 
    INVERSE Project.Members
);

尽管上述两个关系都是双向的,但也可以使用单向关系。假设一个项目包含多个子任务(Task 类),任务对象不需要知道它属于哪个项目。换句话说,您不必从任务对象导航到项目对象。那么,DDL 中的类定义将如下所示:

CREATE CLASS Project (
  /* other properties here */
  Tasks REFERENCES (Task)
);

另一个例子

作为数据库模式的一个示例,这种模式可以用后关系型数据库更自然地表达,我将展示一个基于树结构的文档管理模型。它有两个类 DocumentFolder,其中 Folder 包含 Document 对象和 Folder 对象。

CREATE CLASS Document (
  Name VARCHAR(255) NOT NULL,
  CreationDate DATE NOT NULL,
  Content TEXT
);

CREATE CLASS Folder UNDER Document (
  Documents REFERENCES LIST(Document)
);

上述模式定义允许一个 Folder 或一个 Document 包含在多个 Folder 中。如果您需要一个 Document 或一个 Folder 只包含在一个 Folder 中,DDL 将如下所示:

CREATE CLASS Document (
  Name VARCHAR(255) NOT NULL,
  CreationDate DATE NOT NULL,
  Content TEXT,
  ContainedIn REFERENCES (Folder)
    CARDINALITY (0, 1)     -- this is the constraint
    INVERSE Folder.Documents
);

CREATE CLASS Folder UNDER Document (
  Documents REFERENCES LIST(Document)
    INVERSE Document.ContainedIn
);

您不需要中间表。

索引和入口点

完成逻辑模型后,您可能需要定义一些索引以提高性能。当您在属性(或最多四个属性)上定义索引时,有两点需要了解:

  1. 索引条件的属性必须是“NOT NULL”,即非 NULLABLE
  2. 如果属性是字符串类型,则需要使用 VARCHAR(n) 定义最大长度。

为了在 EmployeeBirthDate 上定义一个索引,并在 EmployeeName 上定义另一个索引,需要像这样定义类:

CREATE TABLE Employee (
  Name VARCHAR(255) NOT NULL,
  BirthDate DATE NOT NULL,
  -- others
);

接下来的 DDL 语句定义了 EmployeeBirthDate 属性上的索引,以及 Employee Name 属性上的另一个索引。

CREATE INDEX birthdate_idx ON Employee (BirthDate);
CREATE INDEX emp_name_idx ON Employee (Name);

Matisse 还有一个全文索引功能,称为“入口点字典”。例如,下一个 DDL 语句定义了一个入口点字典,在 ProjectDescription 属性上进行全文索引。

CREATE ENTRY_POINT DICTIONARY proj_desc_ep_dict
  ON Project (Description)
  MAKE_ENTRY "make-full-text-entry";

下一个 SELECT 查询返回描述包含“.NET”一词的项目。

SELECT * FROM Project 
 WHERE ENTRY_POINT(proj_desc_ep_dict) = '.NET';

模式模板

在 Enterprise Manager 的 Query Analyser 窗口中,您可以通过右键单击窗口来获取 SQL DDL 的基本模式模板。有定义带继承或关系的类的模板、SQL 方法和索引。请注意,同一个菜单中还有 Help,其中包含其他 DDL 语句。

Schema Templates

总结和下一篇文章

在此,我概述了 Matisse 模式定义的基础知识。

尽管关系对于某些开发人员来说是定义数据库模式的新概念,但我相信它并不难,而且实际上是直观的,特别是对于那些已经熟悉 UML 建模的人。如果您有任何疑问,请随时在此文章下发表评论。我很乐意回复。

我的下一篇文章将介绍 .NET 编程的第一步:如何从 .NET 访问数据库。

附录 1:本文使用的完整 SQL DDL

CREATE TABLE Task (
  TaskName STRING,
  StartDate DATE,
  EndDate DATE
);
 
CREATE TABLE Project (
  ProjectName VARCHAR(255),
  Budget NUMERIC(19,2),
  Description STRING,
  Members REFERENCES (Employee) 
    CARDINALITY (1, -1)
    INVERSE Employee.WorksIn,
  ManagedBy REFERENCES (Manager) 
    CARDINALITY (1, 1)
    INVERSE Manager.Manages,
  Tasks REFERENCES (Task)
);
 
CREATE TABLE Employee (
  Name VARCHAR(255) NOT NULL,
  BirthDate DATE NOT NULL,
  WorksIn REFERENCES (Project) 
    INVERSE Project.Members
);
 
CREATE TABLE Manager UNDER Employee (
  Title VARCHAR(255),
  Manages REFERENCES (Project) 
    INVERSE Project.ManagedBy
);
 
CREATE INDEX birthdate_idx ON Employee (BirthDate);
CREATE INDEX emp_name_idx ON Employee (Name);
CREATE ENTRY_POINT DICTIONARY proj_desc_ep_dict
  ON Project ( Description )
  MAKE_ENTRY "make-full-text-entry";

请注意,DDL 中的 VARCHARSTRING 是相同的,只是 VARCHAR 可以指定字符串的最大长度。

附录 2:ODL 中的等效模式

interface Employee : persistent {
  attribute String<255> Name;
  attribute Date BirthDate;
  relationship Set<Project> WorksIn
    inverse Project::Members;
  mt_index birthdate_idx
    unique_key FALSE
    criteria {Employee::BirthDate MT_ASCEND};
  mt_index emp_name_idx
    unique_key FALSE
    criteria {Employee::Name MT_ASCEND};
};
 
interface Manager : Employee : persistent {
  attribute String<64> Nullable Title;
  relationship Set<Project> Manages
    inverse Project::ManagedBy;
};
 
interface Project : persistent {
  attribute String<64> Nullable ProjectName;
  attribute Numeric(19,2) Nullable Budget;
  attribute String Nullable Description;
  mt_entry_point_dictionary proj_desc_ep_dict entry_point_of Description
    unique_key FALSE
    make_entry_function "make-full-text-entry";
  relationship Set<Employee> Members[1, -1]
    inverse Employee::WorksIn;
  relationship Manager ManagedBy
    inverse Manager::Manages;
  relationship Set<Task> Tasks;
};
 
interface Task : persistent {
  attribute String Nullable TaskName;
  attribute Date Nullable StartDate;
  attribute Date Nullable EndDate;
};

<< 上一篇 | 下一篇 >>

历史

  • 2004 年 5 月 24 日:初始版本

许可证

本文没有明确的许可附加,但可能包含文章文本或下载文件中的使用条款。如有疑问,请通过下面的讨论板联系作者。可以在此处找到作者可能使用的许可列表。

© . All rights reserved.