# Data Model (数据模型)

# 介绍

创建数据模型是二次开发的第一步。

Alfresco已经预设了通用的数据模型,具体可参考Alfresco源码 (opens new window)中的 contentModel.xml

因此,如果只是简单的扩展,可能不会涉及到创建新的数据模型,但了解数据模型的相关只是,有助于了解Alfresco的思想与底层设计,对二次开发是很有帮助的。

但如果涉及到业务扩展,例如:档案管理,上传档案文件后,还想管理档案的更多属性,例如:档案编号、归档人员、有效期限等,就需要创建新的数据模型来对应。

# 建模基础

数据模型定义存储在存储库中的数据。数据模型很关键,没有它,Alfresco 只不过是一个文件系统。以下是数据模型为 Alfresco 提供的关键信息列表:

  • Fundamental data types 基本数据类型,以及这些数据类型应如何保存到数据库中。例如:“String” 和 “Date” 类型等。
  • Higher order data types 高阶数据类型,例如:“content” 和 “folder” 等。
  • Aspects 属性组,例如:“auditable” 和 “classifiable”等。
  • Properties 特定于每种数据类型的属性(或元数据)。
  • Constraints 对属性值施加的约束(例如必须匹配特定模式的属性值或必须来自特定可能值列表的属性值)。
  • Index 如何索引内容以供搜索。
  • Associations 节点之间的关联关系。

数据模型是使用一小组构建块构建的:Types(类型)、Properties(属性)、Property types(属性类型)、Constraints(约束)、Associations(关联)、Aspects(属性组)。

# Types(类型)

类型就像面向对象中的类型或类。它们可用于对业务对象建模,它们具有属性,并且它们可以从父类型继承。“Content”、“Person”和“Folder”是既有的三种重要类型。

可以根据业务需求自定义类型。例如包括诸如“费用报告”、“病历”、“电影”、“歌曲”和“评论”之类的内容。

请注意,类型、属性、约束、关联和属性组都有名称。通过使用特定于模型的命名空间,名称在整个存储库中是唯一的。命名空间有一个缩写。本教程假设我们正在为一家名为 SomeCo 的虚构公司实施。因此,SomeCo 可能会定义一个自定义模型,该模型声明一个命名空间,其 URI 为“http://www.someco.com/model/content/1.0”,前缀为“sc”。定义为该模型一部分的任何类型的名称都会以“sc:”为前缀。数据模型实际上是使用 XML 定义的,拥有特定的命名空间和前缀。当定义了多个数据模型时,以这种方式使用命名空间有助于防止名称冲突。

# Properties(属性)

属性是与特定类型关联的元数据片段。例如,支出报告的属性可能包括“员工姓名”、“提交日期”、“项目”、“客户”、“支出报告编号”、“总金额”和“货币”等内容。支出报告还可能包含一个“内容”属性来保存实际支出报告文件(例如,可能是 PDF 或 Excel 电子表格)。

# Property types(属性类型)

属性类型(或数据类型)描述了存储库将用于存储属性的基本数据类型。属性的基本类型主要包括:

  • d:text 文本
  • d:mltext 多语言文本
  • d:content 内容(文件)
  • d:int 整数
  • d:long 长整型
  • d:float 浮点型
  • d:double 双精度浮点型
  • d:date 日期
  • d:datetime 时间
  • d:boolean 布尔型

# Constraints(约束)

可以选择使用约束来限制属性中的值。有四种常用的约束类型:REGEX、LIST、MINMAX和LENGTH。

  • REGEX 用于确保属性值与正则表达式模式匹配
  • LIST 用于定义属性的可能值列表
  • MINMAX 为属性值提供数值范围
  • LENGTH 对字符串的长度设置限制

约束可以定义一次并在整个模型中重复使用。例如,Alfresco 源码中的contentModel.xml定义了一个名为 cm:filename 的约束,它约束了文件名中不得含有特殊字符。如果自定义类型中的属性需要将值限制为与文件名模式匹配的值,则自定义模型不必再次定义约束,而只需要引用约束 cm:filename 即可。

<!-- contentModel.xml -->
<constraints>
  <constraint name="cm:filename" type="REGEX">
      <parameter name="expression"><value><![CDATA[(.*[\"\*\\\>\<\?\/\:\|]+.*)|(.*[\.]?.*[\.]+$)|(.*[ ]+$)]]></value></parameter>
      <parameter name="requiresMatch"><value>false</value></parameter>
  </constraint>
  <constraint name="cm:userNameConstraint" type="org.alfresco.repo.dictionary.constraint.UserNameConstraint" />
  <constraint name="cm:authorityNameConstraint" type="org.alfresco.repo.dictionary.constraint.AuthorityNameConstraint" />
  <constraint name="cm:storeSelectorConstraint" type="REGISTERED">
      <parameter name="registeredName"><value>defaultStoreSelector</value></parameter>
  </constraint>
</constraints>

# Associations(关联)

关联定义类型之间的关系。例如合同,合同可能会与其他类型(比如招投标资料、资质证明材料、发票等)产生关联关系。

关联两种形式:

  • Parent-Child 父子关联关系
  • Source-Target 同级关联关系

一个既有的的关联是cm:contains。该关联定义了文件夹和文件夹内部对象(子文件夹和文件)之间的父子关联关系。

# Aspects(属性组)

  • Aspects(属性组)中可以定义Properties(属性)和Associations(关联)
  • Aspects(属性组)可以被绑定到Types(类型)上,从而使类型拥有Aspects(属性组)中定义的Properties(属性)和Associations(关联)
  • Aspects(属性组)可以复用,也就是可以被同时绑定到不同的Types(类型)上

为了更好地说明Aspects(属性组)的概念和作用,我们举一个例子:

例如,我们为了管理合同文件,增加了一个新的类型:cus:contract,cus:contract 继承了 cm:content 类型,从而拥有了一般文档的属性(名称、标题、内容、创建者、创建时间等)。 但这是不够的,对于合同,我们需要管理新的属性:合同编号 和 有效日期。

如果不使用Aspects(属性组),有两种做法:

  1. 在父类型(cm:content)上增加属性:合同编号 和 有效日期

    这样做会使所有的 cm:content类型,以及继承 cm:content类型 的其他类型都拥有了 合同编号 和 有效日期 的属性。 这样做不是很好的选择,会产生属性冗余,因为并不是所有的类型都需要 合同编号 和 有效日期 的属性。

  2. 在合同类型(cus:contract)上增加属性:合同编号 和 有效日期

    这种做法相对上一种做法会稍好一些,但也不是最好的选择。 因为虽然 合同编号 是合同的特有属性,但 有效日期 明显并不是,其他的类型,例如 档案、体系文件等,有可能也需要管理 有效日期 属性。

因此,我们需要用到属性组:

  1. 将特有属性 合同编号 定义在合同类型(cus:contract)上。
  2. 将共有属性 有效日期 定义新的在Aspects(属性组),例如: com:valid 上。
  3. Aspects(属性组)是可以复用的,因此可以将 com:valid 绑定到需要的类型上,例如:合同、档案、体系文件等。

# 内容建模最佳实践

以下是内容建模时应该考虑的一些最佳实践,或者说是对内容建模的一些建议:

  1. 不要更改即有的数据模型

    尽量不要更改既有的数据模型,也就是说不要直接修改源码中的数据模型定义文件。 相反,使用您自己的自定义数据模型对其进行扩展。如果需要管理不同类型的内容,请为每一种内容类型创建一个数据类型,该内容类型可以继承cm:content或继承自定义的根内容类型。

  2. 尽量保守,只添加已知且已确定的属性

    从前期的业务调研到最后的系统实施,数据模型中的属性应该是不断增多,直到慢慢稳定下来的一个过程,而不是一开始就将不确定的属性都定义到数据模型中。 一旦激活了数据模型,添加属性很容易,但删除属性就不容易了。可能会报“完整性错误”,因为即使决定以后不用的属性,很有可能之前就已经被使用。当遇到这种情况时,选择可以是:

    1. 保留旧模型
    2. 尝试导出内容,修改 ACP XML 文件,然后重新导入
    3. 删除 Alfresco 表,清除数据目录,然后重新开始

    只要团队中的每个人都意识到这一点,开发中就不是什么大问题。但要确保制定相应的规则和规范的流程,以应对在投入生产后处理数据模型的更改。

  3. 避免不必要的数据模型深度

    复杂的数据模型深度很可能会造成后期维护的困难。

  4. 充分利用Aspect(属性组)

    利用Aspect(属性组),除了可以提升潜在的性能和节省开销外,还促进了模型、业务逻辑和表示层的复用。 当发现两个或多个内容类型具有共同的属性时,可以考虑将这些属性抽取建模成Aspect(属性组)。

  5. 可以适当定义没有特定属性和关联的类型

    至少有两种情况,可以考虑定义一个类型,该类型从父类型继承所需的一切,而没有自己的特定属性和关联:

    1. 出于搜索目的,将子类型和父类型区分开来
    2. 可以定义适用于子类型实例的专用行为
  6. 请记住,文件夹也是一种类型

    与模型中的其他所有内容一样,文件夹是类型,这意味着它们可以被继承。 当遇到一个类型可能要包含其他类型的时候,可以将这个类型建模为继承文件夹类型(cm:folder)。

  7. 不要害怕定义多个数据模型的XML文件

    将数据模型分割成多个命名空间,也就是多个XML文件是有意义的,例如在单独的XML文件中定义合同、档案的数据模型。 XML文件名称最好是有意义的,尽量避免名为“customModel.xml”或“myModel.xml”的数据模型文件。

  8. 使用既有的数据模型

    在自定义模型前,最好先了解Alfresco既有的数据模型。这样不仅对理解数据模型有帮助,而且可以避免重复定义。

# 既有的数据模型

Alfresco源码 (opens new window)是不可或缺的参考工具,下表描述了几个既有的数据模型文件。

文件 命名空间 前缀 说明
dictionaryModel.xml model/dictionary/1.0 d 定义基本数据类型
systemModel.xml model/system/1.0 sys 定义系统根类型
system/registry/1.0 reg
system/modules/1.0 module
contentModel.xml model/content/1.0 sys 最常用的数据模型,定义文件、文件夹等基本类型
model/exif/1.0 exif
model/audio/1.0 audio
model/webdav/1.0 webdav
bpmModel.xml model/bpm/1.0 bpm 定义工作流相关数据类型
forumModel.xml model/forum/1.0 fm 定义论坛相关数据类型

为了简洁起见,省略了大约 25 个其他模型文件,具体可参考源码。

除了模型文件之外,modelSchema.xsd 文件也是一个很好的参考。顾名思义,它定义了 Alfresco 内容模型 XML 文件必须遵守的 XML 词汇表。

更新时间: 2024年7月4日星期四晚上9点17分