Apache Olingo 5.0.0 介绍与入门指南

Apache Olingo 5.0.0 介绍与入门:OData V4 Java 库定位与 Apache Attic 退役状态、Edm/EntityContainer/EntitySet 核心概念、odata-client-core 5.0.0 依赖引入与依赖树实测、读取 Northwind V4 元数据与 Customers 查询的 client 实测全流程及 API 排坑。

Apache OlingoOlingoApacheJavaOData2026/10/15

Apache Olingo 5.0.0 介绍与入门指南

⚠️ 项目已退役:Apache Olingo 已于 2025-12 作出退役决议、2026-06 完成向 Apache Attic 的迁移(ATTIC-251),最后版本为 5.0.0,此后不再有任何维护与安全更新。本文仅适合存量系统的维护者阅读,勿用于新项目选型。

目标:介绍 Apache Olingo——一个实现 Open Data Protocol(OData)的 Java 库,覆盖项目定位与退役状态、OData/Edm 核心概念、odata-client-core 5.0.0 依赖引入与依赖树实测,以及 client 端读取 Northwind V4 服务元数据与 Customers 实体查询的完整实测;全部命令与输出于 2026-10-15 在 Ubuntu 22.04 实测取得(client 库使用侧,Attic 状态页与 Maven Central 信息同日核实),server 端开发本文未实测。

一、环境信息

  • 操作系统:Ubuntu 22.04(x86_64)
  • JDK:OpenJDK 17(apt 安装 openjdk-17-jdk),java -version 实测:
OpenJDK Runtime Environment (build 17.0.20.1+1-1-22.04-Ubuntu)
  • 构建工具:Apache Maven 3.6.3,mvn -version 实测:
Apache Maven 3.6.3 / Java version: 17.0.20.1, vendor: Ubuntu
  • Olingo 版本:5.0.0(Maven Central 最新版,2023-12-18 上传,版本事实见 2.3 节)
  • 依赖:org.apache.olingo:odata-client-core:5.0.0(测试工程 pom.xml 唯一依赖,见 4.1 节)
  • Maven 仓库配置:/root/.m2/settings.xml 将 central 重定向至阿里云仓库(mirrorOf=central → https://maven.aliyun.com/repository/public)
  • 测试服务:https://services.odata.org/V4/Northwind/Northwind.svc (OData 官方示例服务;海外服务直连速度慢,实测经代理访问,JVM 代理参数见 5.2 节)
  • 信息核实方式:Attic 项目页与 Maven Central maven-metadata 均于 2026-10-15 抓取
  • 实测边界:完成 client 库(odata-client-core)读元数据与实体查询实测,未启用 mock、直接访问真实示例服务;server 端(odata-server-core)开发未实测,以官方文档为准

二、定位与退役状态

2.1 项目定位:OData 的 Java 实现

Attic 项目页对 Olingo 的定位原文(2026-10-15 实测抓取自 https://attic.apache.org/projects/olingo.html ):

"Java library that implements the Open Data Protocol (OData)"

(概念介绍,以官方文档为准)OData(Open Data Protocol)是描述 RESTful 数据服务的协议:服务经 $metadata 端点发布 EDM(Entity Data Model)元数据,客户端按 EntitySet/EntityType 模型配合 OData 查询选项(如 $top、$select)读写数据。Olingo 在 Java 侧提供两条产品线:

  • V4 线(本文主角):实现 OData V4,分 client(odata-client-api / odata-client-core)与 server(odata-server-api / odata-server-core)两支;
  • V2 线:另有独立的 olingo-odata2 组件线,服务旧版 OData V2 场景(细节以官方文档为准,本文未实测)。

2.2 退役状态(必须知晓)

Attic 项目页确认的时间线(2026-10-15 实测抓取):

事项时间
成为 Apache 顶级项目(TLP)2014-03
董事会作出退役决议2025-12
完成向 Apache Attic 的迁移(ATTIC-251)2026-06

退役后无任何版本发布、缺陷修复与安全补丁;评估存量依赖时应把"5.0.0 即终点"作为前提。

2.3 版本事实(Maven Central 实测,2026-10-15)

  • https://repo1.maven.org/maven2/org/apache/olingo/odata-client-api/maven-metadata.xml 实测内容:
<latest>5.0.0</latest>
<release>5.0.0</release>
versions: ... 4.9.0, 4.10.0, 5.0.0-ALPHA-01, 5.0.0-ALPHA-02, 5.0.0
<lastUpdated>20231218203627</lastUpdated>
  • odata-server-core 的 maven-metadata 同样 latest=5.0.0(lastUpdated 20231218203704);
  • 即 最新版本是 5.0.0(2023-12-18 上传);4.10.0 只是版本列表中的一项,并非最新(部分旧资料以 4.10.x 为"最新版"的说法不准确)。

三、核心概念(以官方文档为准)

(本节为概念介绍,帮助读懂第四、五章实测;细节以 Attic 托管的官方文档为准。)

  • $metadata:OData 服务的自描述端点,返回 EDM XML——描述服务有哪些 Schema、EntityType、EntityContainer、EntitySet。5.2 节实测即读取 Northwind V4 的 $metadata。
  • Edm(Entity Data Model):client 端解析 $metadata 后得到的元数据对象树;Olingo 中对应 Edm、EdmSchema、EdmEntityContainer、EdmEntityType 等接口(org.apache.olingo.commons.api.edm 包)。
  • EntityContainer 与 EntitySet:Edm 中的实体集合归属关系。一个服务有默认 EntityContainer(Northwind 示例为 NorthwindEntities),其下 EntitySet(Categories、Customers、Orders 等)对应可查询的数据集合。
  • OData 查询选项:$top(取前 N 条)、$select(挑列)等;client 端经 URI Builder 构造,5.2 节实测生成 Customers?$top=3&$select=CustomerID,CompanyName。
  • client 库分层:odata-client-api(接口)+ odata-client-core(实现),HTTP 层为 httpclient 4.5.14,JSON/XML 解析为 jackson 2.14 系(依赖树见 4.2 节)。

四、安装部署(依赖引入与工程准备)

4.1 Maven 依赖

测试工程 pom.xml 完整内容(唯一依赖 odata-client-core,JDK 17 编译):

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>demo</groupId>
  <artifactId>olingo-demo</artifactId>
  <version>1.0.0</version>
  <packaging>jar</packaging>
  <properties>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <dependencies>
    <dependency>
      <groupId>org.apache.olingo</groupId>
      <artifactId>odata-client-core</artifactId>
      <version>5.0.0</version>
    </dependency>
  </dependencies>
</project>

Maven 仓库配置(/root/.m2/settings.xml):

mirrorOf=central → https://maven.aliyun.com/repository/public

4.2 依赖树实测

mvn dependency:tree 实测 BUILD SUCCESS(Maven 3.6.3 / maven-dependency-plugin:2.8):

demo:olingo-demo:jar:1.0.0
\- org.apache.olingo:odata-client-core:jar:5.0.0:compile
   +- org.apache.olingo:odata-client-api:jar:5.0.0:compile
   |  \- org.apache.olingo:odata-commons-api:jar:5.0.0:compile
   +- org.apache.olingo:odata-commons-core:jar:5.0.0:compile
   +- commons-codec:commons-codec:jar:1.16.0:compile
   +- commons-io:commons-io:jar:2.13.0:compile
   +- org.apache.commons:commons-lang3:jar:3.13.0:compile
   +- org.apache.httpcomponents:httpclient:jar:4.5.14:compile
   |  +- org.apache.httpcomponents:httpcore:jar:4.4.16:compile
   |  \- commons-logging:commons-logging:jar:1.2:compile
   +- org.slf4j:slf4j-api:jar:1.7.36:compile
   +- com.fasterxml.jackson.core:jackson-core:jar:2.14.0:compile
   +- com.fasterxml.jackson.core:jackson-databind:jar:2.14.3:compile
   +- com.fasterxml.jackson.core:jackson-annotations:jar:2.14.0:compile
   +- com.fasterxml.jackson.dataformat:jackson-dataformat-xml:jar:2.14.0:compile
   |  +- org.codehaus.woodstox:stax2-api:jar:4.2.1:compile
   |  \- com.fasterxml.woodstox:woodstox-core:jar:6.4.0:compile
   \- com.fasterxml:aalto-xml:jar:1.3.2:compile

读法:odata-client-core 5.0.0 引入 client-api 与 commons(api + core);HTTP 层是 httpclient 4.5.14;JSON/XML 解析是 jackson 2.14 系(含 jackson-dataformat-xml 与 woodstox/aalto-xml)。

4.3 编译注意

  • 实测无 TLS/JDK17 模块化问题,mvn compile 正常产出 target/classes;
  • 坑:管道会掩盖 mvn 退出码——mvn ... | head 之类写法看不到真实成败,验证编译必须查看 target/classes 产物,或对输出明确 grep BUILD SUCCESS。

五、上手实测:client 读取 Northwind V4

5.1 示例代码

src/main/java/demo/OlingoDemo.java 完整源码:

package demo;

import org.apache.olingo.client.api.ODataClient;
import org.apache.olingo.client.api.communication.request.retrieve.EdmMetadataRequest;
import org.apache.olingo.client.api.communication.request.retrieve.ODataEntitySetRequest;
import org.apache.olingo.client.api.communication.response.ODataRetrieveResponse;
import org.apache.olingo.client.api.domain.ClientEntity;
import org.apache.olingo.client.api.domain.ClientEntitySet;
import org.apache.olingo.client.core.ODataClientFactory;
import org.apache.olingo.commons.api.edm.Edm;
import org.apache.olingo.commons.api.edm.EdmEntityContainer;
import org.apache.olingo.commons.api.edm.EdmSchema;

import java.net.URI;

public class OlingoDemo {
    static final String SERVICE = "https://services.odata.org/V4/Northwind/Northwind.svc";

    public static void main(String[] args) throws Exception {
        ODataClient client = ODataClientFactory.getClient();

        // 1) 读 $metadata
        EdmMetadataRequest mreq = client.getRetrieveRequestFactory().getMetadataRequest(SERVICE);
        ODataRetrieveResponse<Edm> mres = mreq.execute();
        System.out.println("metadata HTTP status: " + mres.getStatusCode());
        Edm edm = mres.getBody();
        int etCount = 0;
        for (EdmSchema schema : edm.getSchemas()) {
            System.out.println("Schema namespace: " + schema.getNamespace()
                    + " entityTypes=" + schema.getEntityTypes().size());
            etCount += schema.getEntityTypes().size();
        }
        System.out.println("EntityType total = " + etCount);
        EdmEntityContainer ec = edm.getEntityContainer();
        System.out.println("Default EntityContainer: " + (ec == null ? "null" : ec.getName()));
        if (ec != null) {
            ec.getEntitySets().stream().limit(6)
              .forEach(es -> System.out.println("EntitySet: " + es.getName()));
        }

        // 2) 读 Customers?$top=3
        URI uri = client.newURIBuilder(SERVICE)
                .appendEntitySetSegment("Customers")
                .top(3)
                .select("CustomerID,CompanyName")
                .build();
        System.out.println("Request URI: " + uri);
        ODataEntitySetRequest<ClientEntitySet> req =
                client.getRetrieveRequestFactory().getEntitySetRequest(uri);
        ODataRetrieveResponse<ClientEntitySet> res = req.execute();
        System.out.println("query HTTP status: " + res.getStatusCode());
        ClientEntitySet set = res.getBody();
        System.out.println("entities returned = " + set.getEntities().size());
        for (ClientEntity e : set.getEntities()) {
            String id = e.getProperty("CustomerID").getPrimitiveValue().toCastValue(String.class);
            String name = e.getProperty("CompanyName").getPrimitiveValue().toCastValue(String.class);
            System.out.println("CustomerID=" + id + " | CompanyName=" + name);
        }
        System.out.println("DEMO_DONE");
    }
}

5.2 运行与结果

编译后运行(cp.txt 为预先导出的依赖 classpath 清单;services.odata.org 为海外服务,实测直连不可用、速度慢,故 JVM 走代理参数,代理地址与端口按自身网络环境替换):

java -cp target/classes:cp.txt -Dhttps.proxyHost=<代理地址> -Dhttps.proxyPort=<代理端口> demo.OlingoDemo

完整真实输出(过滤 SLF4J 噪音行后):

metadata HTTP status: 200
Schema namespace: ODataWebV4.Northwind.Model entityTypes=0
Schema namespace: NorthwindModel entityTypes=26
EntityType total = 26
Default EntityContainer: NorthwindEntities
EntitySet: Categories
EntitySet: CustomerDemographics
EntitySet: Customers
EntitySet: Employees
EntitySet: Order_Details
EntitySet: Orders
Request URI: https://services.odata.org/V4/Northwind/Northwind.svc/Customers?%24top=3&%24select=CustomerID%2CCompanyName
query HTTP status: 200
entities returned = 3
CustomerID=ALFKI | CompanyName=Alfreds Futterkiste
CustomerID=ANATR | CompanyName=Ana Trujillo Emparedados y helados
CustomerID=ANTON | CompanyName=Antonio Moreno Taquer?a
DEMO_DONE

解读:

  • $metadata 读取 HTTP 200:解析出两个 Schema(ODataWebV4.Northwind.Model 无 EntityType、NorthwindModel 26 个),EntityType 总数 = 26;默认 EntityContainer 为 NorthwindEntities,其下 EntitySet 含 Categories、CustomerDemographics、Customers、Employees、Order_Details、Orders;
  • Customers 查询 HTTP 200:URI Builder 自动生成编码后的查询串(%24top 即 $top、%2C 即逗号),返回 3 条实体,CustomerID 依次为 ALFKI / ANATR / ANTON(ANTON 行 CompanyName 尾部的 ? 为终端输出编码显示问题);
  • 全程未启用任何 mock/备选方案,services.odata.org 实测可用。

5.3 两个 API 坑(实测确认)

坑一:getMetadataRequest() 会自动追加 /$metadata。 client.getRetrieveRequestFactory().getMetadataRequest(url) 内部自动在 URL 后追加 /$metadata;若手动传入 https://.../Northwind.svc/$metadata,会拼成 .../$metadata/$metadata,服务端 404:

The segment '$metadata' must be the last segment in the URI

正确用法是传服务根 URL(如示例代码中的 SERVICE 常量)。

坑二:EdmSchema 没有 getEntityContainers()。 Olingo 5.0.0 的 EdmSchema 接口只有单数 getEntityContainer()(经 javap 确认的 API 事实);误用复数方法编译报错:

cannot find symbol: method getEntityContainers()

(编译器为 maven-compiler-plugin:3.1,报错以 MojoFailureException 呈现。)取默认 EntityContainer 应使用 edm.getEntityContainer()(示例代码用法)。

六、适用场景与局限(存量维护视角)

适用场景:

  • 存量系统已基于 Olingo client/server 构建(OData V4/V2 数据集成、企业数据服务接口等),需要理解其行为、评估升级或替换路径;
  • 与旧版 OData 数据源对接的遗留 Java 应用的维护;
  • OData 协议与 EDM 元数据模型的学习研究(5.0.0 发布物仍可从 Maven Central 获取,实测编译运行可用)。

局限与风险:

  • 无任何维护与安全更新:最后版本 5.0.0 于 2023-12-18 上传,2025-12 退役决议、2026-06 完成 Attic 迁移;
  • 依赖栈停在旧版本线:httpclient 4.5.14、jackson 2.14 等随 5.0.0 锁定,升级需自行排查兼容性;
  • server 端(odata-server-core)同样止步于 5.0.0,新建 OData 服务不应再选型 Olingo(新项目替代方案本文未实测,选型请以各项目官方文档为准)。

七、常见问题(FAQ)

Q1:Olingo 的"最新版"到底是 4.10.x 还是 5.0.0? Maven Central 上 odata-client-api / odata-server-core 的 latest 均为 5.0.0(2023-12-18 上传);4.10.0 只是版本列表中的一项。部分旧资料称最新为 4.10.x 不准确。

Q2:编译报 cannot find symbol: method getEntityContainers()? Olingo 5.0.0 的 EdmSchema 接口没有该方法(javap 确认只有单数 getEntityContainer());获取默认 EntityContainer 用 edm.getEntityContainer()(见 5.3 节坑二)。

Q3:读 $metadata 返回 404,提示 The segment '$metadata' must be the last segment in the URI? getMetadataRequest(url) 会自动在 URL 后追加 /$metadata;不要再手动拼 /$metadata(会变成 .../$metadata/$metadata)。应传服务根 URL(见 5.3 节坑一)。

Q4:JDK 17 下编译运行有模块化/TLS 问题吗? 实测没有:OpenJDK 17.0.20.1 + Maven 3.6.3 + odata-client-core 5.0.0 编译运行均正常,无 TLS/JDK17 模块问题。

Q5:mvn 命令接管道(如 | head)后退出码不可靠? 是的,管道会掩盖 mvn 退出码。验证编译成败应查看 target/classes 产物,或对输出明确 grep BUILD SUCCESS(见 4.3 节)。

Q6:访问 services.odata.org 慢或不可用? 该服务为海外公开示例服务,实测直连速度慢、经代理才稳定;可在运行时加 JVM 代理参数 -Dhttps.proxyHost/-Dhttps.proxyPort(见 5.2 节)。

参考链接