通過 OpenAPI 批量接入自訂實體與血緣

更新時間:
Copy as MD

本文介紹如何使用 DataWorks OpenAPI 將企業自有系統中的中繼資料對象和血緣關係大量註冊到 DataWorks 資料地圖,實現統一檢索、統一展示、統一血緣分析和影響分析。

概述

在企業資料治理過程中,很多關鍵資料對象並不完全產生於 DataWorks 內部,例如:

  • BI 報表、看板、指標、資料產品。

  • 內部服務 API、資料服務介面、應用系統對象。

  • 第三方資料庫、外部表格、外部欄位。

  • 企業自研平台中的任務、流程、模型或主題域對象。

如果這些對象沒有進入資料地圖,您在做資料資產檢索、上下遊分析、故障排查和變更影響評估時,只能看到 DataWorks 已採集的表和欄位,無法形成完整的資料鏈路。通過自訂實體和血緣註冊相關 OpenAPI,您可以:

  • 將外部業務對象註冊到資料地圖,作為可查詢、可展示、可治理的中繼資料實體。

  • 將 DataWorks 已採集表、欄位、資料集與外部對象建立血緣關係。

  • 註冊表與表、欄位與欄位、資料集與表之間的血緣關係。

  • 為自訂實體、表、欄位補充業務屬性,例如業務域、來源系統、負責人、資料分層。

  • 將外部系統的資產關係沉澱到資料地圖,支撐資料溯源、影響分析和統一資產盤點。

批量接入時,可以把 DataWorks 資料地圖視為企業自有中繼資料和血緣關係的統一消費入口。典型鏈路如下:

  • 企業資料來源:業務系統、第三方資料庫、BI 報表、API 服務等。

  • 批量接入工程:從源系統抽取實體清單、血緣關係清單、屬性定義清單,統一作為 OpenAPI 入參。

  • DataWorks OpenAPI:調用 CreateMetaEntityDefCreateCustomAttributeBatchCreateMetaEntitiesCreateLineageRelationship 等介面寫入實體定義、實體物件、自訂屬性和血緣關係。

  • DataWorks 資料地圖:統一展示已採集對象與自訂對象,提供檢索、詳情、血緣圖、影響分析等能力。

  • 下遊消費:調用 ListLineages 等查詢介面,支撐資料溯源、影響分析、斷鏈檢測、血緣健康巡檢等營運情境。

其中,實體清單描述"有哪些對象要納管",血緣關係清單描述"對象之間如何流轉",屬性定義清單描述"哪些業務欄位需要統一展示、篩選和治理"。

物件類型選擇

接入前建議先判斷對象是否已經被 DataWorks 納管,再決定是否需要註冊實體。

典型接入情境

情境

樣本

推薦方式

將業務對象納入資料地圖

報表、API、指標、資料產品

註冊為自訂實體物件

將外部庫表納入資料地圖

第三方資料庫、外部表格、外部欄位

註冊為擴充表實體

給實體補充商務資訊

業務域、負責人、來源系統、安全等級

建立自訂屬性並寫入實體

註冊外部血緣

表到報表、表到 API、表到外部表格

調用 CreateLineageRelationship

註冊納管實體之間血緣

表與表、欄位與欄位、表與欄位、資料集與表

使用 DataWorks 已採集實體 ID 註冊血緣

說明

CreateLineageRelationship 支援在 DataWorks 已納管實體之間註冊血緣關係,不要求血緣兩端必須包含自訂對象。

非納管自訂對象(custom-{Type}:{Identifier})主要用於相容歷史用法。新接入情境建議優先註冊為 DataWorks 自訂實體物件,再建立血緣關係。

按對象是否納管選擇註冊方式

物件類型

是否需要註冊實體

推薦方式

後續 ID 擷取方式

DataWorks 已採集表、欄位、資料集、資料集版本

直接使用查詢 API 返回的實體 ID 註冊血緣

ListTablesGetTableListColumnsGetColumn 等查詢 API

報表、API、指標、資料產品等業務對象

使用 CreateMetaEntityDef 建立自訂實體定義,再使用 BatchCreateMetaEntities 寫入實體物件

BatchCreateMetaEntities 返回的 Id,或後續調用 GetMetaEntity 查詢

DataWorks 未自動採集的外部庫表欄位

使用擴充表實體建模,先寫入 Database,再寫入 Table 和內聯 Column

BatchCreateMetaEntities 返回的表 ID,或後續調用 ListTablesListColumns 查詢

非納管自訂對象

僅用於相容歷史血緣註冊情境,新接入不推薦

調用方自我維護 custom- 開頭的 ID

前置要求

使用本文涉及的 OpenAPI 前,請先確認帳號和 DataWorks 版本滿足以下條件。

DataWorks 版本

本文樣本會建立實體定義、建立實體物件、建立自訂屬性並註冊血緣關係,因此需要準備 DataWorks 專業版及以上版本。其中:

  • CreateMetaEntityDef 用於建立自訂實體定義或擴充表實體定義,介面要求專業版及以上版本。

  • BatchCreateMetaEntities 用於大量建立自訂實體物件、擴充 Database 和擴充 Table,介面要求專業版及以上版本。

  • CreateLineageRelationship 用於註冊血緣關係,介面要求專業版及以上版本。

如果只調用查詢類 API,請以對應 API 詳情頁中的版本要求為準。

帳號和角色許可權

範例程式碼使用 AccessKey 調用 OpenAPI。AccessKey 對應的主帳號、RAM 使用者或 STS 身份,需要屬於目標 DataWorks 租戶,並具備調用 DataWorks OpenAPI 的許可權。

建立自訂實體定義、自訂屬性、純自訂實體和擴充表實體時,建議使用具備以下任一許可權的帳號:

  • 租戶 Owner。

  • 租用戶系統管理員。

  • 租戶資料治理管理員。

  • 具備 DataWorks FullAccess 許可權的帳號。

如果後續要更新 DataWorks 已採集 MaxCompute 表或欄位上的業務屬性,調用方還需要是 MaxCompute 表 Owner,或對應 DataWorks 專案的專案 Owner、專案系統管理員。本文 Demo 唯讀取已採集表和欄位的 ID 並註冊血緣,不會修改這些已採集表和欄位。

運行本文 Demo 前需要準備的資料

如果需要運行本文 Java Demo,類中的 UPSTREAM_TABLE_ID 需要替換成您帳號下真實存在的 DataWorks 已採集表 ID。建議通過以下方式擷取:

  • 表 ID:調用 ListTablesGetTable,使用返回結果中的 Id

  • 欄位 ID:調用 ListColumnsGetColumn,使用返回結果中的 Id

對於 DataWorks 已採集到的表、欄位、資料集、資料集版本,不建議手動拼接實體 ID,應優先使用對應查詢 API 返回的 ID,避免因為 ID 拼接有誤或對象尚未採集導致血緣註冊失敗。通過 BatchCreateMetaEntities 註冊的擴充表和擴充欄位,也可以在寫入成功後通過 ListTablesGetTableListColumnsGetColumn 查詢並擷取返回結果中的 Id。如果後續要把擴充表或擴充欄位作為血緣端點,也建議優先使用查詢 API 返回的 ID。

如果只按照"大量註冊工程方案"接入自己的對象清單和血緣清單,不一定需要使用本文 Demo 中的上遊表示例;可以直接使用您清單中真實存在的上遊和下遊實體 ID。

資料準備模板

批量接入前,推薦至少準備三類結構化清單。可以使用 CSV、Excel 或資料庫表儲存,欄位名稱可按企業內部規範調整,但語義建議保持一致。

實體定義清單樣本

欄位

說明

樣本

def_name

實體定義名稱,用於建立實體類型

bi_report

display_name

頁面展示名稱

BI 報表

kind

對象建模方式:自訂實體或擴充表實體

CUSTOM_ENTITY

extend

是否擴充 DataWorks 表模型

NONE / TABLE

attribute_defs_json

普通屬性定義,使用 JSON 數組儲存

[{"Name":"reportCode","Type":"STRING"}]

remark

業務說明

企業 BI 報表資產

實體物件清單樣本

欄位

說明

樣本

entity_type

實體類型

custom_entity-bi_report

name

實體名稱

daily_sales_report

comment

實體說明

每日銷售報告

attributes_json

普通屬性值,使用 JSON 對象儲存

{"reportCode":"daily_sales_report"}

custom_attributes_json

自訂屬性值,使用 JSON 對象儲存

{"sourceSystem":["BI"]}

parent_entity_id

擴充 database/table 層級關係

custom_bi-database:src::report_db

columns_json

擴充表欄位列表,隨表寫入

[{"name":"order_id","type":"STRING"}]

血緣關係清單樣本

欄位

說明

樣本

src_id

上遊實體 ID

maxcompute-table:::project:default:dwd_order

src_name

上遊實體名稱

dwd_order

dst_id

下遊實體 ID

custom_entity-bi_report:daily_sales_report

dst_name

下遊實體名稱

daily_sales_report

task_id

產生血緣的任務或過程 ID

dwd_order_to_report

task_type

任務類型

custom-table_to_report

task_attributes_json

任務屬性,使用 JSON 對象儲存

{"scene":"daily_sync"}

接入流程

推薦按照以下流程接入。具體 API 參數、欄位約束、錯誤碼和更多 SDK 程式碼範例,請參見文末"參考文檔"中的 API 詳情頁。

步驟一:梳理需要納管的對象和關係

先整理企業系統中的對象清單和關係清單。對象清單通常包括:

  • 對象名稱,例如訂單查詢 API、銷售報告、外部訂單表。

  • 物件類型,例如 API、報表、指標、資料庫表。

  • 業務屬性,例如負責人、業務域、來源系統。

  • 如果是表或欄位,整理庫名、表名、欄位名和欄位類型。

關係清單通常包括:

  • 上遊對象。

  • 下遊對象。

  • 關聯類型,例如表產出報表、欄位對應欄位、API 寫入表。

  • 產生關係的任務或過程,例如同步任務、加工任務、介面調用鏈路。

如果需要批量接入,建議先按照"資料準備模板"整理實體清單和血緣關係清單,再進入後續 API 呼叫流程。

步驟二:判斷對象是否已經被 DataWorks 納管

如果對象已經由 DataWorks 採集或平台管理,不需要重複註冊實體,直接使用對應查詢 API 返回的實體 ID。常見擷取方式:

  • 表:調用 ListTablesGetTable

  • 欄位:調用 ListColumnsGetColumn

  • 資料集:調用 ListDatasetsGetDataset

  • 資料集版本:調用 ListDatasetVersionsGetDatasetVersion

如果對象還沒有被 DataWorks 納管,再根據物件類型選擇註冊方式:

  • 報表、API、指標、系統等業務對象:註冊為自訂實體。

  • 具備資料庫、表、欄位結構的外部資料資產:註冊為擴充表實體。

步驟三:建立實體定義

使用 CreateMetaEntityDef 建立實體類型定義。

自訂實體適合描述業務概念,例如 API、報表、指標。樣本:

{
  "Name": "customer_api",
  "DisplayName": "業務API",
  "Description": "企業系統中的 API 對象",
  "AttributeDefs": [
    {
      "Name": "apiCode",
      "DisplayName": "API編碼",
      "Type": "STRING",
      "IsOptional": false
    },
    {
      "Name": "serviceName",
      "DisplayName": "服務名稱",
      "Type": "STRING",
      "IsOptional": true
    }
  ]
}

擴充表實體適合描述 DataWorks 未自動採集的外部庫表欄位。建議在建立擴充表時同步建立對應的擴充 Database 對象,並通過 parentMetaEntityId 建立 Database 到 Table 的層級關係,便於後續在資料地圖中按完整庫表層級查看。樣本:

{
  "Name": "bi-table",
  "DisplayName": "BI報表",
  "Description": "外部 BI 系統中的庫表欄位",
  "Extend": "TABLE"
}

具體欄位含義和命名約束請參見 CreateMetaEntityDef API 詳情頁。

步驟四:建立可複用的自訂屬性

如果希望在資料地圖中對實體展示或篩選業務屬性,可以使用 CreateCustomAttribute 建立自訂屬性定義。例如:

  • 來源系統。

  • 業務域。

  • 負責人。

  • 資料分層。

  • 安全等級。

樣本:

{
  "Id": "custom-attribute:source_system",
  "DisplayName": "來源系統",
  "Type": "ENUM",
  "ValueEnums": ["CRM", "ERP", "BI", "CUSTOM_APP"],
  "EntityTypes": ["custom_entity-customer_api"],
  "SearchFilterEnabled": true,
  "DisplayEnabled": true
}

自訂屬性建立後,可在寫入自訂實體、擴充表實體,或更新已採集表欄位商務資訊時使用。EntityTypes 需要填寫可掛載該屬性的具體實體類型;如果需要掛載到擴充表或擴充欄位,請填寫對應擴充實體定義產生的具體類型。

步驟五:建立實體物件

使用 BatchCreateMetaEntities 寫入具體實體物件。

自訂實體物件樣本:

{
  "Entities": [
    {
      "EntityType": "custom_entity-customer_api",
      "Name": "api_001",
      "Comment": "業務訂單查詢 API",
      "Attributes": {
        "apiCode": "api_001",
        "serviceName": "order-service"
      },
      "CustomAttributes": {
        "source_system": ["CRM"]
      }
    }
  ]
}
說明

BatchCreateMetaEntities 是批量介面,需要同時關注兩層成功狀態:返回體最外層的 Success 表示本次批量請求是否被服務端成功處理;Results 中每條記錄的 Success 表示對應實體物件是否建立成功。實際接入時,不能只判斷最外層 Success,還需要逐條檢查 Results[].Success,並記錄失敗項的 ErrorMessage

擴充表實體物件樣本

擴充表寫入前,建議先寫入同一擴充類型下的資料庫實體,再寫入表實體並將表的 parentMetaEntityId 指向該資料庫實體。這樣註冊後可以形成完整的 Database -> Table -> Column 層級關係,便於在資料地圖中查看和管理。由於 BatchCreateMetaEntities 同一批次的實體必須屬於同一種 EntityType,database 和 table 建議拆成兩次請求。

先寫入外部 database:

{
  "Entities": [
    {
      "EntityType": "custom_bi-database",
      "Name": "report_db",
      "Comment": "外部 BI 報表庫",
      "Attributes": {
        "parentMetaEntityId": "custom_bi:custom_bi_src",
        "technicalMetadata.location": "external://bi/report_db"
      }
    }
  ]
}

再寫入外部 table,並通過 columns 一起註冊欄位:

{
  "Entities": [
    {
      "EntityType": "custom_bi-table",
      "Name": "sales_report",
      "Comment": "銷售報告結果表",
      "Attributes": {
        "parentMetaEntityId": "custom_bi-database:custom_bi_src::report_db",
        "columns": "[{\"name\":\"order_id\",\"type\":\"STRING\"},{\"name\":\"amount\",\"type\":\"DECIMAL\"}]"
      }
    }
  ]
}

欄位不需要單獨調用 BatchCreateMetaEntities 建立。上例寫入成功後,外部欄位可使用擴充欄位 ID 參與欄位血緣。擴充表和欄位的 ID 可以通過 ListTablesGetTableListColumnsGetColumn 查詢獲得;如果已經明確擴充實體 ID 規則,也可以按規則產生,例如 custom_bi-column:custom_bi_src::report_db::sales_report:order_id

寫入成功後,響應中會返回實體 ID。後續註冊血緣、查詢詳情、更新實體時都使用該 ID。

步驟六:註冊血緣關係

使用 CreateLineageRelationship 註冊實體之間的上下遊關係。血緣方向始終是:上遊 SrcEntity -> 下遊 DstEntity

樣本一:DataWorks 已採集表到自訂 API。

{
  "SrcEntity": {
    "Id": "mysql-table:rm-xxx::sales_db::orders",
    "Name": "orders"
  },
  "DstEntity": {
    "Id": "custom_entity-customer_api:api_001",
    "Name": "api_001"
  },
  "Task": {
    "Id": "table_to_api_001",
    "Type": "custom-lineage-task",
    "Attributes": {
      "scene": "table_to_api"
    }
  }
}

樣本二:DataWorks 已採集表到外部擴充表。

{
  "SrcEntity": {
    "Id": "maxcompute-table:::prod_project:default:dwd_order",
    "Name": "dwd_order"
  },
  "DstEntity": {
    "Id": "custom_bi-table:custom_bi_src::report_db::sales_report",
    "Name": "sales_report"
  },
  "Task": {
    "Id": "dwd_order_to_sales_report",
    "Type": "custom-table-lineage",
    "Attributes": {
      "scene": "external_bi_table"
    }
  }
}

樣本三:DataWorks 已採集欄位到外部擴充欄位。

{
  "SrcEntity": {
    "Id": "maxcompute-column:::prod_project:default:dwd_order:order_id",
    "Name": "order_id"
  },
  "DstEntity": {
    "Id": "custom_bi-column:custom_bi_src::report_db::sales_report:order_id",
    "Name": "order_id"
  },
  "Task": {
    "Id": "dwd_order_id_to_report_order_id",
    "Type": "custom-column-lineage",
    "Attributes": {
      "scene": "external_bi_column"
    }
  }
}

對於欄位級血緣,SrcEntityDstEntity 使用欄位實體 ID。欄位 ID 可以來自 ListColumnsGetColumn,包括 DataWorks 已採集欄位和登入擴充表欄位。具體請求參數和返回欄位請參見 CreateLineageRelationship API 詳情頁。

步驟七:驗證註冊結果並消費血緣

註冊完成後,建議做兩類驗證。

驗證實體是否寫入成功

  • 自訂實體:調用 GetMetaEntity

  • 擴充表:調用 GetTable

  • 擴充欄位:調用 GetColumn

  • DataWorks 已採集對象:調用對應的 Get API。

說明

實體建立成功後,查詢側可能存在約 1 秒的短暫可見度延遲。如果剛寫入後沒有立即查詢到,可短暫等待後重試。

驗證血緣是否寫入成功

  • 查詢下遊:調用 ListLineages 並傳入 SrcEntityId

  • 查詢上遊:調用 ListLineages 並傳入 DstEntityId

  • 需要關係詳情時,設定 NeedAttachRelationship=true

說明

血緣註冊後可能存在短暫索引延遲。如果剛寫入後沒有立即查詢到,可等待數秒後重試。

完成基礎驗證後,可以繼續把血緣資料用於日常分析:

  • 上遊溯源:從目標實體出發,調用 ListLineages 查詢上遊,並按上遊節點繼續遞迴查詢,用於回答"這個報表或表的資料來自哪裡"。

  • 下遊影響分析:從變更實體出發,調用 ListLineages 查詢下遊,並統計下遊實體類型和數量,用於回答"這張表或這個欄位變更會影響哪些報表、API 或外部表格"。

  • 斷鏈檢測:將大量註冊的實體清單與血緣端點清單做比對,找出沒有任何上遊或下遊的孤立實體,作為待補錄或待清理對象。

  • 血緣健康巡檢:定期檢查實體是否存在、血緣關係是否重複、血緣端點是否可查詢、批量寫入是否有失敗項,形成巡檢報表。

多層級影響分析的實現思路如下:

queue = [changedEntityId]
visited = set()
while queue is not empty:
    current = queue.pop()
    downstream = ListLineages(SrcEntityId=current)
    for entity in downstream:
        if entity.id not in visited:
            visited.add(entity.id)
            queue.push(entity.id)

/* 輸出 visited 中的實體類型、實體名稱和影響路徑。 */

建議在批量接入工程中儲存每次註冊成功的實體 ID 和血緣關係 ID。後續做影響分析、斷鏈檢測或累加式更新時,可以直接基於這份結果表和查詢 API 做比對,不需要重新解析原始業務系統資料。

大量註冊工程方案

當接入規模從幾個對象擴大到幾百個報表、上千張外部表格或上萬條血緣關係時,建議把註冊過程設計成可重複運行、可斷點續跑、可審計的批處理任務。

推薦寫入順序

批量接入時,建議按照以下順序執行:

  1. 建立或確認實體定義:調用 CreateMetaEntityDef

  2. 建立或確認自訂屬性:調用 CreateCustomAttribute

  3. 寫入擴充 Database:調用 BatchCreateMetaEntities

  4. 寫入擴充 Table,並隨 Table 寫入 Column。

  5. 寫入純自訂實體,例如報表、API、指標。

  6. 註冊血緣關係:調用 CreateLineageRelationship

  7. 查詢驗證並產生接入結果報告。

批次、重試和斷點續跑

BatchCreateMetaEntities 是實體批量寫入介面,單次請求最多寫入 5 個實體,並且同一批次必須是同一種 EntityType。實際工程中建議先按 EntityType 分組,再按 5 條一批切分。

CreateLineageRelationship 按單條血緣關係寫入。血緣量較大時,可以控制並發度分批調用,並對限流或臨時服務異常做退避重試。

建議採用以下錯誤處理模式:

for batch in splitByEntityType(rows, size=5):
    resp = BatchCreateMetaEntities(batch)
    if resp.Success is not true:
        write_batch_failed(batch, resp.RequestId, resp.Message)
        continue

    for result in resp.Results:
        if result.Success is true:
            mark_done(result.Id)
        else:
            write_row_failed(result.Id, result.ErrorMessage, resp.RequestId)
說明

這裡需要同時判斷兩層成功狀態:最外層 Success 表示請求是否被服務端成功處理,Results[].Success 表示每個實體物件是否寫入成功。批量工程不要因為最外層 Success=true 就認為所有實體都已成功。

斷點續跑建議

  • 為實體和血緣使用穩定的業務 ID,避免每次運行產生不同標識。

  • 每批寫入後記錄成功項、失敗項、RequestId、失敗原因和原始行號。

  • 重跑時跳過已成功項,只重試失敗項或變更項。

  • 運行前可通過 GetMetaEntityGetCustomAttributeListLineageRelationships 等查詢介面確認目標對象是否已經存在。

  • 4xx 類資料錯誤通常需要修正清單後再重試;限流、網路抖動、臨時服務異常可以做有限次數退避重試。

對於客戶側每日同步任務,推薦保留"期望狀態表"和"上次成功寫入結果表"。增量同步處理時只處理新增和變更對象;定期全量比對時,用期望狀態表與 DataWorks 查詢結果做差異檢查,發現缺失、重複或廢棄關係後再補錄或清理。

頁面效果

Demo 註冊完成後,您可以在 DataWorks 資料地圖中看到以下效果。實際頁面展示會受帳號許可權、地區、資料地圖頁面版本和註冊資料內容影響。

自訂實體可被搜尋和篩選

註冊自訂實體定義和實體物件後,可以在資料地圖中搜尋到對應實體。若實體定義中的屬性開啟了搜尋篩選能力,頁面左側會展示相應篩選項(例如按業務域、來源系統、負責人過濾報表實體)。

自訂實體詳情展示屬性資訊

進入自訂實體詳情頁後,可以查看實體基礎資訊、普通屬性和自訂屬性。例如 BI 報表實體詳情頁會展示報表編碼、業務域、負責人以及來源系統等業務欄位。

自訂屬性可統一管理

通過 CreateCustomAttribute 建立的自訂屬性會進入資料地圖的自訂屬性管理頁,可查看屬性名稱、類型、適用實體類型等資訊,便於統一維護和複用。

擴充表實體進入表資產搜尋

註冊擴充 Database、Table 和 Column 後,外部庫表可以進入資料地圖的表資產視圖,並按資料來源類型、資料庫等條件檢索,與 DataWorks 已採集表統一呈現。

表級和欄位級血緣在血緣圖中展示

註冊"表到自訂實體"、"表到表"、"欄位到欄位"血緣後,可以在資料地圖的血緣頁面查看上下遊關係。例如同一張銷售匯總表可以同時關聯到自訂報表實體和擴充表實體,並向下展示欄位級血緣。

Java SDK 範例

下面提供一個可直接編譯啟動並執行 Java Demo,同時覆蓋:

  • 註冊 BI 報表自訂實體,並建立 DataWorks 已採集表到報表的血緣。

  • 註冊外部擴充表,並建立表到表、欄位到欄位血緣。

Demo 中的業務標識集中放在類頂部常量裡。實際接入時,請替換為您帳號下真實的上遊表 ID、欄位 ID、報表名稱、外部庫表名稱等。首次運行前,也請確認樣本中的實體定義、自訂屬性和實體物件在帳號下尚未存在,或改成企業內部唯一的業務標識。

建議先通過 ListTablesGetTable 擷取真實上遊表 ID,通過 ListColumnsGetColumn 擷取真實上遊欄位 ID,再運行 Demo。

1. 準備工程

工程目錄:

custom-metadata-lineage-demo
├── pom.xml
└── src
    └── main
        └── java
            └── CustomMetadataLineageDemo.java

pom.xml

<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>example</groupId>
  <artifactId>custom-metadata-lineage-demo</artifactId>
  <version>1.0.0</version>
  <properties>
    <maven.compiler.source>${java.version}</maven.compiler.source>
    <maven.compiler.target>${java.version}</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <dependencies>
    <dependency>
      <groupId>com.aliyun</groupId>
      <artifactId>dataworks_public20240518</artifactId>
      <version>${dataworks.sdk.version}</version>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <version>3.1.0</version>
        <configuration>
          <mainClass>CustomMetadataLineageDemo</mainClass>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

2. 編寫 Demo

將以下代碼儲存為 src/main/java/CustomMetadataLineageDemo.java

import com.aliyun.dataworks_public20240518.Client;
import com.aliyun.dataworks_public20240518.models.BatchCreateMetaEntitiesRequest;
import com.aliyun.dataworks_public20240518.models.BatchCreateMetaEntitiesResponse;
import com.aliyun.dataworks_public20240518.models.CreateCustomAttributeRequest;
import com.aliyun.dataworks_public20240518.models.CreateLineageRelationshipRequest;
import com.aliyun.dataworks_public20240518.models.CreateMetaEntityDefRequest;
import com.aliyun.dataworks_public20240518.models.GetCustomAttributeRequest;
import com.aliyun.dataworks_public20240518.models.GetCustomAttributeResponse;
import com.aliyun.dataworks_public20240518.models.GetMetaEntityDefRequest;
import com.aliyun.dataworks_public20240518.models.GetMetaEntityDefResponse;
import com.aliyun.dataworks_public20240518.models.LineageEntity;
import com.aliyun.dataworks_public20240518.models.LineageTask;
import com.aliyun.dataworks_public20240518.models.MetaEntityAttributeDef;
import com.aliyun.dataworks_public20240518.models.MetaEntityWriteResult;
import com.aliyun.teaopenapi.models.Config;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class CustomMetadataLineageDemo {
    private static final String REGION_ID = "cn-shanghai";

    // DataWorks 已採集表 ID。請通過 ListTables/GetTable 查詢後替換。
    private static final String UPSTREAM_TABLE_ID = "maxcompute-table:::prod_project:default:dwd_sales_order";
    private static final String UPSTREAM_TABLE_NAME = "dwd_sales_order";

    // BI 報表示例。
    private static final String REPORT_ENTITY_TYPE = "custom_entity-bi_report";
    private static final String REPORT_ENTITY_NAME = "daily_sales_report";

    // 外部擴充表示例。
    private static final String EXT_PREFIX = "warehouse";
    private static final String EXT_SOURCE = "custom_external_bi_src";
    private static final String EXT_DATABASE = "analytics_db";
    private static final String EXT_TABLE = "ads_sales_summary";
    private static final String EXT_TABLE_ID =
            "custom_" + EXT_PREFIX + "-table:" + EXT_SOURCE + "::" + EXT_DATABASE + "::" + EXT_TABLE;
    private static final String EXT_ORDER_ID_COLUMN_ID =
            "custom_" + EXT_PREFIX + "-column:" + EXT_SOURCE + "::" + EXT_DATABASE + "::" + EXT_TABLE + ":order_id";
    private static final String EXT_AMOUNT_COLUMN_ID =
            "custom_" + EXT_PREFIX + "-column:" + EXT_SOURCE + "::" + EXT_DATABASE + "::" + EXT_TABLE + ":amount";

    public static void main(String[] args) throws Exception {
        Client client = new Client(new Config()
                .setAccessKeyId(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"))
                .setAccessKeySecret(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"))
                .setRegionId(REGION_ID));

        if (System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID") == null
                || System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET") == null) {
            throw new IllegalArgumentException(
                    "請先設定 ALIBABA_CLOUD_ACCESS_KEY_ID 和 ALIBABA_CLOUD_ACCESS_KEY_SECRET");
        }

        String reportId = registerBiReport(client);
        registerExternalTable(client);
        createLineage(client, UPSTREAM_TABLE_ID, UPSTREAM_TABLE_NAME,
                reportId, REPORT_ENTITY_NAME, "dw_table_to_bi_report", "custom-table_to_report");
        createLineage(client, UPSTREAM_TABLE_ID, UPSTREAM_TABLE_NAME,
                EXT_TABLE_ID, EXT_TABLE, "dw_table_to_external_table", "custom-table_to_table");
        createLineage(client, EXT_ORDER_ID_COLUMN_ID, "order_id",
                EXT_AMOUNT_COLUMN_ID, "amount", "ext_col_to_ext_col", "custom-column_to_column");

        System.out.println("自訂實體、擴充表和血緣註冊完成。");
    }

    private static String registerBiReport(Client client) throws Exception {
        ensureMetaEntityDef(client, REPORT_ENTITY_TYPE, new CreateMetaEntityDefRequest()
                .setName("bi_report")
                .setDisplayName("BI 報表")
                .setDescription("企業 BI 報表資產")
                .setExtend("NONE")
                .setAttributeDefs(Arrays.asList(
                        new MetaEntityAttributeDef()
                                .setName("reportCode").setDisplayName("報表編碼").setType("STRING").setIsOptional(false),
                        new MetaEntityAttributeDef()
                                .setName("reportUrl").setDisplayName("報表地址").setType("STRING").setIsOptional(true)
                )));

        ensureCustomAttribute(client, "custom-attribute:sourceSystem", new CreateCustomAttributeRequest()
                .setId("custom-attribute:sourceSystem")
                .setDisplayName("來源系統")
                .setType("ENUM")
                .setValueEnums(Arrays.asList("BI", "CRM", "ERP"))
                .setEntityTypes(Collections.singletonList(REPORT_ENTITY_TYPE))
                .setDisplayEnabled(true)
                .setSearchFilterEnabled(true));

        Map<String, String> attrs = new HashMap<String, String>();
        attrs.put("reportCode", REPORT_ENTITY_NAME);
        attrs.put("reportUrl", "https://bi.example.com/reports/daily_sales");

        Map<String, List<String>> customAttrs = new HashMap<String, List<String>>();
        customAttrs.put("sourceSystem", Collections.singletonList("BI"));

        String reportId = createOneEntity(client,
                new BatchCreateMetaEntitiesRequest.BatchCreateMetaEntitiesRequestEntities()
                        .setEntityType(REPORT_ENTITY_TYPE)
                        .setName(REPORT_ENTITY_NAME)
                        .setComment("每日銷售報告")
                        .setAttributes(attrs)
                        .setCustomAttributes(customAttrs));
        Thread.sleep(1000L); // 實體建立成功後,查詢側可能存在約 1 秒可見度延遲。
        return reportId;
    }

    private static void registerExternalTable(Client client) throws Exception {
        ensureMetaEntityDef(client, "custom_" + EXT_PREFIX + "-table", new CreateMetaEntityDefRequest()
                .setName(EXT_PREFIX + "-table")
                .setDisplayName("外部數倉表")
                .setDescription("DataWorks 未自動採集的外部庫表")
                .setExtend("TABLE"));

        Map<String, String> dbAttrs = new HashMap<String, String>();
        dbAttrs.put("parentMetaEntityId", "custom_" + EXT_PREFIX + ":" + EXT_SOURCE);
        dbAttrs.put("technicalMetadata.location", "external://" + EXT_DATABASE);
        createOneEntity(client, new BatchCreateMetaEntitiesRequest.BatchCreateMetaEntitiesRequestEntities()
                .setEntityType("custom_" + EXT_PREFIX + "-database")
                .setName(EXT_DATABASE)
                .setComment("外部分析庫")
                .setAttributes(dbAttrs));

        Map<String, String> tableAttrs = new HashMap<String, String>();
        tableAttrs.put("parentMetaEntityId", "custom_" + EXT_PREFIX + "-database:" + EXT_SOURCE + "::" + EXT_DATABASE);
        tableAttrs.put("tableType", "TABLE");
        tableAttrs.put("columns", "["
                + "{\"name\":\"order_id\",\"type\":\"STRING\",\"comment\":\"訂單 ID\",\"position\":1},"
                + "{\"name\":\"amount\",\"type\":\"DECIMAL\",\"comment\":\"銷售金額\",\"position\":2}"
                + "]");
        createOneEntity(client, new BatchCreateMetaEntitiesRequest.BatchCreateMetaEntitiesRequestEntities()
                .setEntityType("custom_" + EXT_PREFIX + "-table")
                .setName(EXT_TABLE)
                .setComment("外部銷售匯總表")
                .setAttributes(tableAttrs));
        Thread.sleep(1000L);
    }

    private static String createOneEntity(
            Client client,
            BatchCreateMetaEntitiesRequest.BatchCreateMetaEntitiesRequestEntities entity) throws Exception {
        BatchCreateMetaEntitiesResponse response = client.batchCreateMetaEntities(
                new BatchCreateMetaEntitiesRequest().setEntities(Collections.singletonList(entity)));
        if (response.getBody() == null || !Boolean.TRUE.equals(response.getBody().getSuccess())) {
            throw new RuntimeException("BatchCreateMetaEntities 請求失敗");
        }
        MetaEntityWriteResult result = response.getBody().getResults().get(0);
        if (!Boolean.TRUE.equals(result.getSuccess())) {
            if (isAlreadyExists(result.getErrorMessage())) {
                String existingId = buildExpectedEntityId(entity);
                System.out.println("實體已存在,複用: " + existingId);
                return existingId;
            }
            throw new RuntimeException("實體建立失敗: " + result.getErrorMessage());
        }
        System.out.println("實體建立成功: " + result.getId());
        return result.getId();
    }

    private static String buildExpectedEntityId(
            BatchCreateMetaEntitiesRequest.BatchCreateMetaEntitiesRequestEntities entity) {
        String entityType = entity.getEntityType();
        String name = entity.getName();
        if (entityType.startsWith("custom_entity-")) {
            return entityType + ":" + name;
        }

        Map<String, String> attributes = entity.getAttributes();
        String parentMetaEntityId = attributes == null ? null : attributes.get("parentMetaEntityId");
        if (entityType.endsWith("-database") && parentMetaEntityId != null) {
            String metaSource = parentMetaEntityId.substring(parentMetaEntityId.indexOf(':') + 1);
            return entityType + ":" + metaSource + "::" + name;
        }
        if (entityType.endsWith("-table") && parentMetaEntityId != null) {
            String parent = parentMetaEntityId.substring(parentMetaEntityId.indexOf(':') + 1);
            return entityType + ":" + parent + "::" + name;
        }

        throw new IllegalArgumentException("無法根據實體類型和屬性推導已存在實體 ID: " + entityType + "/" + name);
    }

    private static void createLineage(
            Client client,
            String srcId,
            String srcName,
            String dstId,
            String dstName,
            String taskId,
            String taskType) throws Exception {
        client.createLineageRelationship(new CreateLineageRelationshipRequest()
                .setSrcEntity(new LineageEntity().setId(srcId).setName(srcName))
                .setDstEntity(new LineageEntity().setId(dstId).setName(dstName))
                .setTask(new LineageTask().setId(taskId).setType(taskType)));
        System.out.println("血緣建立成功: " + srcId + " -> " + dstId);
    }

    private static void ensureMetaEntityDef(
            Client client,
            String entityType,
            CreateMetaEntityDefRequest request) throws Exception {
        if (metaEntityDefExists(client, entityType)) {
            System.out.println("實體定義已存在,跳過建立: " + entityType);
            return;
        }
        client.createMetaEntityDef(request);
    }

    private static boolean metaEntityDefExists(Client client, String entityType) throws Exception {
        try {
            GetMetaEntityDefResponse response = client.getMetaEntityDef(
                    new GetMetaEntityDefRequest().setEntityType(entityType));
            return response.getBody() != null
                    && Boolean.TRUE.equals(response.getBody().getSuccess())
                    && response.getBody().getMetaEntityDef() != null;
        } catch (Exception e) {
            if (isNotFound(e)) {
                return false;
            }
            throw e;
        }
    }

    private static void ensureCustomAttribute(
            Client client,
            String id,
            CreateCustomAttributeRequest request) throws Exception {
        if (customAttributeExists(client, id)) {
            System.out.println("自訂屬性已存在,跳過建立: " + id);
            return;
        }
        client.createCustomAttribute(request);
    }

    private static boolean customAttributeExists(Client client, String id) throws Exception {
        try {
            GetCustomAttributeResponse response = client.getCustomAttribute(
                    new GetCustomAttributeRequest().setId(id));
            return response.getBody() != null
                    && Boolean.TRUE.equals(response.getBody().getSuccess())
                    && response.getBody().getCustomAttribute() != null;
        } catch (Exception e) {
            if (isNotFound(e)) {
                return false;
            }
            throw e;
        }
    }

    private static boolean isNotFound(Exception e) {
        String message = e.getMessage();
        if (message == null) {
            return false;
        }
        String lowerMessage = message.toLowerCase();
        return lowerMessage.contains("not found")
                || lowerMessage.contains("not exist")
                || lowerMessage.contains("not_exists")
                || message.contains("不存在");
    }

    private static boolean isAlreadyExists(String message) {
        if (message == null) {
            return false;
        }
        String lowerMessage = message.toLowerCase();
        return lowerMessage.contains("already exists") || lowerMessage.contains("already exist");
    }
}

3. 運行 Demo

運行前配置訪問憑證。範例程式碼從環境變數讀取 AccessKey,避免在代碼中明文寫入密鑰。

export ALIBABA_CLOUD_ACCESS_KEY_ID="your-access-key-id"
export ALIBABA_CLOUD_ACCESS_KEY_SECRET="your-access-key-secret"

執行:

export DATAWORKS_SDK_VERSION="latest-version"
export JAVA_VERSION="8"
mvn -Ddataworks.sdk.version="${DATAWORKS_SDK_VERSION}" \
    -Djava.version="${JAVA_VERSION}" \
    compile exec:java

說明:

  • Demo 按建立流程編寫。運行前,請將類頂部的實體類型、實體名稱、外部庫表名稱等業務標識改成您帳號下尚未使用的唯一值。

  • BatchCreateMetaEntities 需要同時檢查最外層 Success 和每條 Results[].Success,樣本中的 createOneEntity 已包含這部分檢查。

  • 實體建立成功後,查詢側可能有約 1 秒可見度延遲。樣本在建立後加入了短暫等待,實際生產接入可改成有限重試。

4. 批量接入核心程式碼片段

上面的 Demo 用於快速跑通端到端流程。生產環境批量接入時,建議參考"資料準備模板"準備實體物件清單和血緣關係清單,再分別轉換成 List<EntityRow>List<LineageRow> 作為批量方法入參。

其中,EntityRow 對應實體物件清單,LineageRow 對應血緣關係清單。如果清單來自 CSV、Excel 或資料庫,可以先把 attributes_jsoncustom_attributes_jsontask_attributes_json 解析成 Map 後賦值給對應 Row 對象。下面樣本是可嵌入前面 Demo 工程的核心程式碼片段,不是完整 CSV/Excel 讀取器;檔案讀取和 JSON 解析可以按企業內部工程規範實現。

如果將以下代碼放入上面的 CustomMetadataLineageDemo 類中,需要額外引入:

import java.util.ArrayList;
import java.util.LinkedHashMap;

大量建立實體

private static BatchWriteResult batchCreateEntities(Client client, List<EntityRow> entityRows) throws Exception {
    BatchWriteResult report = new BatchWriteResult();

    // BatchCreateMetaEntities 要求同一批次為同一種 EntityType,因此先按 EntityType 分組。
    Map<String, List<EntityRow>> grouped = new LinkedHashMap<String, List<EntityRow>>();
    for (EntityRow row : entityRows) {
        if (!grouped.containsKey(row.entityType)) {
            grouped.put(row.entityType, new ArrayList<EntityRow>());
        }
        grouped.get(row.entityType).add(row);
    }

    for (Map.Entry<String, List<EntityRow>> entry : grouped.entrySet()) {
        List<EntityRow> sameTypeRows = entry.getValue();
        for (int from = 0; from < sameTypeRows.size(); from += 5) {
            int to = Math.min(from + 5, sameTypeRows.size());
            List<EntityRow> rowBatch = sameTypeRows.subList(from, to);

            List<BatchCreateMetaEntitiesRequest.BatchCreateMetaEntitiesRequestEntities> requestEntities =
                    new ArrayList<BatchCreateMetaEntitiesRequest.BatchCreateMetaEntitiesRequestEntities>();
            for (EntityRow row : rowBatch) {
                requestEntities.add(row.toRequestEntity());
            }

            BatchCreateMetaEntitiesResponse response = client.batchCreateMetaEntities(
                    new BatchCreateMetaEntitiesRequest().setEntities(requestEntities));
            if (response.getBody() == null || !Boolean.TRUE.equals(response.getBody().getSuccess())) {
                for (EntityRow row : rowBatch) {
                    report.failed.add("entity=" + row.name + ", reason=batch request failed");
                }
                continue;
            }

            List<MetaEntityWriteResult> results = response.getBody().getResults();
            if (results == null || results.size() != rowBatch.size()) {
                for (EntityRow row : rowBatch) {
                    report.failed.add("entity=" + row.name + ", reason=unexpected batch result");
                }
                continue;
            }
            for (int i = 0; i < results.size(); i++) {
                MetaEntityWriteResult result = results.get(i);
                if (Boolean.TRUE.equals(result.getSuccess())) {
                    report.successIds.add(result.getId());
                } else {
                    EntityRow row = rowBatch.get(i);
                    report.failed.add("entity=" + row.name + ", reason=" + result.getErrorMessage());
                }
            }
        }
    }

    return report;
}

大量註冊血緣關係

private static BatchWriteResult batchCreateLineages(Client client, List<LineageRow> lineages) {
    BatchWriteResult report = new BatchWriteResult();
    for (LineageRow row : lineages) {
        boolean success = false;
        String lastError = null;

        for (int attempt = 1; attempt <= 3; attempt++) {
            try {
                LineageTask task = new LineageTask().setId(row.taskId).setType(row.taskType);
                if (row.taskAttributes != null && !row.taskAttributes.isEmpty()) {
                    task.setAttributes(row.taskAttributes);
                }

                client.createLineageRelationship(new CreateLineageRelationshipRequest()
                        .setSrcEntity(new LineageEntity().setId(row.srcId).setName(row.srcName))
                        .setDstEntity(new LineageEntity().setId(row.dstId).setName(row.dstName))
                        .setTask(task));
                report.successIds.add(row.srcId + " -> " + row.dstId);
                success = true;
                break;
            } catch (Exception e) {
                lastError = e.getMessage();
                try {
                    Thread.sleep(1000L * attempt);
                } catch (InterruptedException interrupted) {
                    Thread.currentThread().interrupt();
                    lastError = interrupted.getMessage();
                    break;
                }
            }
        }

        if (!success) {
            report.failed.add("lineage=" + row.srcId + " -> " + row.dstId + ", reason=" + lastError);
        }
    }
    return report;
}

輔助結構

private static class EntityRow {
    // 對應實體物件清單中的 entity_type、name、comment。
    private String entityType;
    private String name;
    private String comment;

    // 對應 attributes_json、custom_attributes_json。
    private Map<String, String> attributes = new HashMap<String, String>();
    private Map<String, List<String>> customAttributes = new HashMap<String, List<String>>();

    // 對應 parent_entity_id、columns_json,主要用於擴充 Database/Table。
    private String parentEntityId;
    private String columnsJson;

    private BatchCreateMetaEntitiesRequest.BatchCreateMetaEntitiesRequestEntities toRequestEntity() {
        Map<String, String> mergedAttributes = new HashMap<String, String>();
        if (attributes != null) {
            mergedAttributes.putAll(attributes);
        }
        if (parentEntityId != null && parentEntityId.length() > 0) {
            mergedAttributes.put("parentMetaEntityId", parentEntityId);
        }
        if (columnsJson != null && columnsJson.length() > 0) {
            mergedAttributes.put("columns", columnsJson);
        }

        return new BatchCreateMetaEntitiesRequest.BatchCreateMetaEntitiesRequestEntities()
                .setEntityType(entityType)
                .setName(name)
                .setComment(comment)
                .setAttributes(mergedAttributes)
                .setCustomAttributes(customAttributes);
    }
}

private static class BatchWriteResult {
    private final List<String> successIds = new ArrayList<String>();
    private final List<String> failed = new ArrayList<String>();
}

private static class LineageRow {
    private String srcId;
    private String srcName;
    private String dstId;
    private String dstName;
    private String taskId;
    private String taskType;
    private Map<String, String> taskAttributes = new HashMap<String, String>();
}

實際工程中,可以把 BatchWriteResult.failed 寫入檔案、資料庫或訊息佇列,作為斷點續跑和人工修正的資料來源。對於失敗項,不建議無差別無限重試;參數錯誤、實體類型不存在、血緣端點不存在等問題需要先修正資料清單,再重新執行。

推薦實踐

優先註冊為 DataWorks 自訂實體物件

非納管 custom-{Type}:{Identifier} 對象可以直接參与血緣註冊,但這種方式不利於後續管理。新接入情境建議:

  1. 使用 CreateMetaEntityDef 建立實體定義。

  2. 使用 BatchCreateMetaEntities 建立實體物件。

  3. 使用返回的實體 ID 註冊血緣。

這樣可以在資料地圖中查看實體詳情、維護屬性、複用自訂屬性,並支援後續治理。

區分自訂實體和擴充表實體

如果對象是報表、API、指標、系統、資料產品等業務概念,建議註冊為自訂實體。如果對象具備明確的資料庫、表、欄位結構,且 DataWorks 未自動採集,建議註冊為擴充表實體。

註冊擴充表實體時,建議同時建立對應的擴充 Database 對象,再建立 Table 和內聯 Column。這樣可以保留外部資料源的庫表欄位層級,後續在資料地圖中查看資產時更接近真實資料結構。

警告

請勿通過 BatchCreateMetaEntities 建立 MaxCompute、DLF、EMR 等 DataWorks 採集器管理的預置實體。

  • 對於已支援的類型:

    • 若未採集,請通過採集器將其納入 DataWorks 管理。

    • 若已採集,請通過查詢 API 擷取其 ID 直接使用。

  • 對於不支援的類型:

    • 請使用擴充表實體進行建立。

通過 API 詳情頁確認參數

本文樣本用於說明 API 組合方式和接入流程。欄位必填性、命名規則、枚舉值、錯誤碼、SDK 代碼等詳細資料,請進入對應 API 詳情頁查看。

營運與更新

自訂實體和血緣關係接入後,建議把它作為一條持續同步鏈路維護,而不是一次性匯入任務。常見營運動作如下:

營運動作

推薦 API

說明

更新實體定義展示名、說明或屬性定義

UpdateMetaEntityDef

適合實體模型演化;修改前建議評估已有實體資料相容性

更新自訂屬性定義

UpdateCustomAttribute

適合調整展示、篩選、枚舉值等屬性配置

更新自訂實體或擴充表實體物件

UpdateMetaEntity

適合更新實體說明、普通屬性、自訂屬性等資訊

更新表或欄位業務屬性

UpdateTableBusinessMetadata / UpdateColumnBusinessMetadata

適合維護表、欄位上的 README 和自訂屬性

刪除廢棄血緣

DeleteLineageRelationship

可先通過 ListLineageRelationships 找到需要刪除的關係

刪除廢棄實體定義

DeleteMetaEntityDef

刪除前需要確認沒有仍在使用的實體和血緣關係,謹慎使用強制移除能力

血緣關係變化時,推薦先從客戶系統產生一份"期望血緣清單",再用 ListLineageRelationships 查詢 DataWorks 當前已有關係,對比後執行:

  • 期望清單有、DataWorks 沒有:調用 CreateLineageRelationship 補充。

  • 期望清單沒有、DataWorks 仍存在:調用 DeleteLineageRelationship 清理。

  • 兩邊都存在但任務資訊變化:先刪除舊關係,再建立新關係。

如果客戶系統每天都有新增對象,建議使用增量同步處理;如果客戶系統存在大量歷史修訂或人工維護資料,建議定期執行全量比對,避免 DataWorks 中出現漂移資料。

常見問題

現象

可能原因

處理建議

實體類型不存在

還沒有建立實體定義,或建立後查詢側尚未可見

先調用 CreateMetaEntityDef,短暫等待後再寫入實體

實體寫入請求成功但部分實體失敗

只判斷了最外層 Success,忽略了 Results[].Success

逐條檢查 Results,將失敗項落盤並按錯誤原因補錄

血緣端點不存在

上遊或下遊實體 ID 不正確,或剛建立的實體尚未可查

使用 GetMetaEntityGetTableGetColumn 等 API 驗證端點 ID,必要時等待後重試

欄位血緣註冊失敗

欄位 ID 手動拼接錯誤,或擴充表欄位未隨表註冊

已採集欄位優先使用 ListColumnsGetColumn 返回 ID;擴充欄位確認 columns 已隨表寫入

批量調用被限流

並發過高或短時間請求過多

降低並發度,對限流錯誤做指數退避重試

重跑後出現重複或舊血緣

沒有用穩定 ID 和期望狀態做比對

儲存成功結果表;重跑前查詢已有關係,跳過已存在關係或先刪除舊關係

自訂屬性在頁面不可篩選

屬性定義未開啟搜尋篩選,或適用實體類型不包含目標類型

檢查 SearchFilterEnabledEntityTypes,必要時更新自訂屬性定義

需要排查批量寫入失敗

失敗資訊缺少可定位上下文

記錄失敗請求的 RequestId、原始行號、實體 ID、血緣端點 ID 和錯誤資訊,提工單或排查時優先提供 RequestId

頁面或查詢介面短時間查不到剛寫入對象

查詢索引存在短暫延遲

等待 1 秒到數秒後重試;批量工程中使用有限重試而不是立即判定失敗

常見接入方案

方案一:把 BI 報表接入資料地圖

適合希望在資料地圖中查看報表依賴哪些表、某張表變更會影響哪些報表的情境。推薦流程:

  1. 使用 CreateMetaEntityDef 建立報表實體定義。

  2. 使用 CreateCustomAttribute 建立業務域、負責人、來源系統等屬性。

  3. 使用 BatchCreateMetaEntities 批量寫入報表實體。

  4. 使用 ListTablesListColumns 擷取 DataWorks 已採集表欄位 ID。

  5. 使用 CreateLineageRelationship 註冊表到報表、欄位到欄位血緣。

  6. 使用 ListLineages 驗證上下遊鏈路。

方案二:把外部資料庫表接入資料地圖

適合企業已有第三方資料庫或外部系統表,DataWorks 未自動採集,但希望納入統一資產視圖的情境。推薦流程:

  1. 使用 CreateMetaEntityDef 建立擴充表定義。

  2. 使用 BatchCreateMetaEntities 先寫入外部資料庫,再寫入外部表格,欄位隨表內聯寫入。

  3. 使用 CreateCustomAttribute 補充來源系統、資料域等業務屬性。

  4. 使用 CreateLineageRelationship 建立 DataWorks 表與外部表格的血緣關係。

  5. 使用 GetTableGetColumnListLineages 驗證結果。

方案三:補充 DataWorks 已採集資產之間的血緣

適合企業存在外部加工鏈路或歷史鏈路,希望補充到資料地圖中的情境。推薦流程:

  1. 使用 ListTablesListColumnsListDatasets 等 API 擷取實體 ID。

  2. 根據實際上下遊方向調用 CreateLineageRelationship

  3. 使用 ListLineages 查詢驗證。

這種情境不需要建立自訂實體定義,也不需要寫入實體物件。

參考文檔