Implementation:Datahub project Datahub Entity Mutable Patch
| Property | Value |
|---|---|
| Implementation Name | Entity_Mutable_Patch |
| Workflow | Java_SDK_V2_Entity_Management |
| Type | API Doc |
| Principle | Principle:Datahub_project_Datahub_Patch_Based_Updates |
| Repository | https://github.com/datahub-project/datahub |
| Last Updated | 2026-02-09 17:00 GMT |
Overview
Description
Entity_Mutable_Patch documents the entity.mutable() method and the fluent patch API for modifying metadata entities. The mutable() method creates a writable copy from a read-only entity (fetched from the server), enabling a read-modify-write workflow. Once mutable, the entity exposes fluent methods like setDescription(), addTag(), addOwner(), and addTerm() that accumulate JSON Patch operations internally. These patches are emitted when client.entities().upsert(entity) is called.
The VersionAwarePatchTransformer handles server version compatibility by transforming patches to full aspect replacements for servers that do not support specific patch types (DataHub Core <= v1.3.0).
Usage
Call .mutable() on any read-only entity to get a writable copy. If the entity is already mutable (e.g., created via a builder), .mutable() returns the same instance (idempotent). Apply modifications through the fluent API, then call client.entities().upsert(entity) to persist changes.
Code Reference
Source Location
metadata-integration/java/datahub-client/src/main/java/datahub/client/v2/entity/Entity.java(Lines 384-747) --mutable()method and patch infrastructuremetadata-integration/java/datahub-client/src/main/java/datahub/client/v2/patch/VersionAwarePatchTransformer.java(Lines 51-184) -- Server version-aware patch transformation
Signature
// Create mutable copy from read-only entity
public <T extends Entity> T mutable()
// Fluent mutation methods (available on mutable entities)
public Dataset setDescription(@Nonnull String description)
public Dataset setSystemDescription(@Nonnull String description)
public Dataset setEditableDescription(@Nonnull String description)
public Dataset setDisplayName(@Nonnull String name)
public Dataset addCustomProperty(@Nonnull String key, @Nonnull String value)
public Dataset removeCustomProperty(@Nonnull String key)
public Dataset setCustomProperties(@Nonnull Map<String, String> properties)
// From HasTags interface
public T addTag(@Nonnull String tag)
public T removeTag(@Nonnull String tag)
// From HasOwners interface
public T addOwner(@Nonnull String ownerUrn, @Nonnull OwnershipType type)
public T removeOwner(@Nonnull String ownerUrn)
// From HasGlossaryTerms interface
public T addTerm(@Nonnull String termUrn)
public T removeTerm(@Nonnull String termUrn)
// From HasDomains interface
public T setDomain(@Nonnull String domainUrn)
public T unsetDomain()
// Patch registration (internal)
protected void registerPatchBuilder(String aspectName, AbstractMultiFieldPatchBuilder<?> builder)
protected void addPatchMcp(MetadataChangeProposal patch)
Import
import datahub.client.v2.entity.Entity;
import datahub.client.v2.entity.Dataset;
import datahub.client.v2.patch.VersionAwarePatchTransformer;
I/O Contract
mutable() Method:
| Input | Output | Behavior |
|---|---|---|
| Called on read-only entity | New mutable copy of the entity | Creates writable copy via reflection-based cloning; shares aspect cache with original, fresh mutation tracking |
| Called on mutable entity | Same entity instance (this) | Idempotent -- returns self without creating a copy |
Mutation Methods:
| Method | Patch Aspect | Operation Type | Mode Awareness |
|---|---|---|---|
setDescription(String) |
editableDatasetProperties (SDK) or datasetProperties (INGESTION) |
JSON Patch ADD | Yes -- writes to editable or system aspect based on mode |
addTag(String) |
globalTags |
JSON Patch ADD | No |
removeTag(String) |
globalTags |
JSON Patch REMOVE | No |
addOwner(String, OwnershipType) |
ownership |
JSON Patch ADD | No |
removeOwner(String) |
ownership |
JSON Patch REMOVE | No |
addTerm(String) |
glossaryTerms |
JSON Patch ADD | No |
removeTerm(String) |
glossaryTerms |
JSON Patch REMOVE | No |
setDomain(String) |
domains |
Full aspect UPSERT | No |
addCustomProperty(String, String) |
datasetProperties |
Accumulated patch ADD | No |
removeCustomProperty(String) |
datasetProperties |
Accumulated patch REMOVE | No |
Version-Aware Transformation:
| Server Version | Behavior |
|---|---|
| DataHub Core > v1.3.0 | Patches passed through unchanged |
| DataHub Core <= v1.3.0 | Editable property patches transformed to full aspect replacements via read-modify-write |
| All versions | mlModelProperties patches always transformed with retry function
|
| Unknown (serverConfig null) | Patches passed through unchanged with warning |
Exceptions:
| Exception | Cause |
|---|---|
ReadOnlyEntityException |
Mutation method called on a read-only entity (must call .mutable() first)
|
RuntimeException |
Reflection-based cloning fails in mutable()
|
Usage Examples
Read-Modify-Write Pattern
import datahub.client.v2.entity.Dataset;
// Step 1: Fetch entity (returns read-only)
String urn = "urn:li:dataset:(urn:li:dataPlatform:snowflake,analytics.users,PROD)";
Dataset readOnly = client.entities().get(urn, Dataset.class);
// Step 2: Create mutable copy
Dataset mutable = readOnly.mutable();
// Step 3: Apply patch operations (accumulated internally)
mutable.setDescription("Updated user analytics table");
mutable.addTag("verified");
mutable.addOwner("urn:li:corpuser:alice", OwnershipType.DATA_OWNER);
mutable.addTerm("urn:li:glossaryTerm:UserData");
// Step 4: Upsert persists all patches
client.entities().upsert(mutable);
Patching a Newly Built Entity
// Entities created via builders are already mutable
Dataset dataset = Dataset.builder()
.platform("bigquery")
.name("project.dataset.events")
.description("Event stream")
.build();
// Patches accumulate alongside builder-cached aspects
dataset.addTag("streaming");
dataset.addCustomProperty("retention_days", "90");
// Upsert emits both full aspects (description) and patches (tag, custom property)
client.entities().upsert(dataset);
Accumulated Custom Property Operations
Dataset mutable = readOnly.mutable();
// Multiple addCustomProperty calls accumulate in a single DatasetPropertiesPatchBuilder
mutable.addCustomProperty("team", "data-eng");
mutable.addCustomProperty("sla", "99.9%");
mutable.addCustomProperty("cost_center", "CC-1234");
mutable.removeCustomProperty("deprecated_key");
// One patch MCP is emitted containing all four operations
client.entities().upsert(mutable);
Related Pages
- Principle:Datahub_project_Datahub_Patch_Based_Updates
- Implementation:Datahub_project_Datahub_EntityClient_Get -- Fetching entities before making them mutable
- Implementation:Datahub_project_Datahub_EntityClient_Upsert -- Persisting patch operations
- Implementation:Datahub_project_Datahub_Dataset_Builder -- Building entities that support patching
- Environment:Datahub_project_Datahub_Java_Build