1. 简介

在本文中,我们将探讨 OData(Open Data Protocol),这是一个用于通过 RESTful API 访问数据集的标准协议。它提供了一种标准化的方式来查询、操作和导航数据,非常适合构建统一的数据服务接口。

2. 什么是 OData?

OData 是一个由 OASIS 和 ISO/IEC 标准化的协议,用于通过 RESTful API 访问数据。它允许客户端通过标准的 HTTP 方法发现并操作数据集。

比如,我们可以使用 curl 快速访问一个公开的 OData 服务:

curl -s https://services.odata.org/V2/Northwind/Northwind.svc/Regions
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<feed xml:base="https://services.odata.org/V2/Northwind/Northwind.svc/" 
  xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" 
  xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" 
  xmlns="http://www.w3.org/2005/Atom">
    <title type="text">Regions</title>
    <id>https://services.odata.org/V2/Northwind/Northwind.svc/Regions</id>
... 其他 XML 内容省略

当前最新版本是 OData 4.01。虽然 OData V4 在 2014 年就已成为 OASIS 标准,但它的历史可以追溯到微软的 Astoria 项目,后来改名为 ADO.NET Data Services。

使用 OData 有很多优势:

✅ 允许使用 Excel 等工具直接访问数据
✅ 丰富的 REST 客户端支持
✅ 支持安全机制、日志、监控等 HTTP 特性

正因为如此,很多政府机构在构建公共数据服务时都选择了 OData,例如 OData 公共服务目录

3. OData 核心概念

OData 的核心是 Entity Data Model(EDM),它描述了服务提供者所暴露的数据结构。EDM 通过 $metadata 接口提供元数据文档,其中包含:

  • 实体类型(Entity Type)及其属性和主键(如 Person、Customer、Order)
  • 实体之间的关系
  • 复杂类型(如嵌入在 Customer 中的 Address 类型)
  • 实体集合(Entity Set),用于聚合特定类型的实体

3.1. EntityType 元素

定义实体的属性和主键,例如:

<EntityType Name="CarMaker">
    <Key>
        <PropertyRef Name="Id"/>
    </Key>
    <Property Name="Id" Type="Edm.Int64" Nullable="false"/>
    <Property Name="Name" Type="Edm.String" Nullable="true" MaxLength="255"/>
    <NavigationProperty Name="CarModelDetails" Relationship="default.CarModel_CarMaker_Many_One0" FromRole="CarMaker" ToRole="CarModel"/>
</EntityType>

这个 CarMaker 实体有两个属性:IdName,还有一个导航属性 CarModelDetails,表示与 CarModel 的关联关系。

3.2. Association 元素

定义两个实体之间的关联关系,包括多重性(如一对多)和引用完整性约束:

<Association Name="CarModel_CarMaker_Many_One0">
    <End Type="default.CarModel" Multiplicity="*" Role="CarModel"/>
    <End Type="default.CarMaker" Multiplicity="1" Role="CarMaker"/>
    <ReferentialConstraint>
        <Principal Role="CarMaker">
            <PropertyRef Name="Id"/>
        </Principal>
        <Dependent Role="CarModel">
            <PropertyRef Name="Maker"/>
        </Dependent>
    </ReferentialConstraint>
</Association>

该示例定义了 CarModelCarMaker 的一对多关系。

3.3. EntitySet 元素

代表某一实体类型的集合,类似于数据库中的“视图”:

<EntityContainer Name="defaultContainer" m:IsDefaultEntityContainer="true">
    <EntitySet Name="CarModels" EntityType="default.CarModel"/>
    <EntitySet Name="CarMakers" EntityType="default.CarMaker"/>
</EntityContainer>

每个 EntitySet 对应一个实体类型,我们可以定义多个 EntitySet 来展示不同的数据子集。

4. OData URL 与方法

OData 使用标准 HTTP 方法来操作数据:

方法 作用
GET 获取一个或多个实体
POST 添加新实体
PUT 替换整个实体
PATCH 替换部分属性
DELETE 删除实体

URL 示例:

http://example.org/odata/CarMakers

这个 URL 的结构如下:

  • http://example.org/odata/:服务根路径(Service Root)
  • CarMakers:对应服务元数据中的某个 EntitySet

访问该 URL 将返回一个 XML 文档,包含所有 CarMakers 实体的列表。

<feed>
    <entry>
        <id>http://example.org/odata/CarMakers(1L)</id>
        <title type="text">CarMakers</title>
        <content type="application/xml">
            <m:properties>
                <d:Id>1</d:Id>
                <d:Name>Special Motors</d:Name>
            </m:properties>
        </content>
    </entry>
</feed>

其中:

  • id:指向该实体的链接
  • titleupdated:元数据信息
  • link:编辑或关联实体的链接
  • content:实体的属性值

注意:CarMaker(1L) 中的 L 表示这是一个 long 类型的主键,可以省略。

5. 查询选项(Query Options)

OData 提供了多种查询参数,用于控制返回数据的格式和内容。

5.1. $top$skip

用于分页查询:

.../CarMakers?$top=10&$skip=10
  • $top=10:返回前 10 条记录
  • $skip=10:跳过前 10 条记录

获取集合总数:

.../CarMakers/$count

⚠️ 注意:OData V2 和 V4 的 $count 使用方式不同:

  • V2:.../CarMakers/$count
  • V4:.../CarMakers?$count=true

5.2. $filter

用于过滤数据,支持逻辑表达式和函数:

.../CarMakers?$filter=startswith(Name,'B')

复合条件查询:

.../CarModels?$filter=Year eq 2008 and CarMakerDetails/Name eq 'BMW'

5.3. $expand

默认情况下,OData 不会返回关联实体的数据。使用 $expand 可以一并获取:

.../CarModels(1L)?$expand=CarMakerDetails

返回结果中将包含 CarMakerDetails 的完整数据。

5.4. $select

用于指定返回的字段,避免返回冗余数据:

.../CarModels(1L)?$select=Name,Sku

返回内容仅包含 NameSku 字段。

5.5. $orderBy

控制返回数据的排序方式,支持多个字段和排序方向:

.../CarModels?$orderBy=Name asc,Sku desc

⚠️ 注意:排序方向 ascdesc 必须使用小写。

5.6. $format

指定返回数据的格式(优先于 Accept 请求头):

.../CarModels?$format=json

服务将返回 JSON 格式数据。如果未指定,将根据 Accept 头决定返回格式。

6. 总结

本文介绍了 OData 协议的基本概念和使用方式,包括 EDM、HTTP 方法、URL 结构以及常用查询参数。OData 提供了一种标准化的数据访问方式,适合构建统一的数据服务接口。

后续文章将介绍如何使用 Olingo 库实现 OData 服务。


原始标题:OData Protocol Guide | Baeldung

« 上一篇: JPA @Basic 注解详解
» 下一篇: Groovy 中的模板引擎