Skip to content

feat: Relationship JDBC insert/update [DHIS2-21378]#24093

Draft
enricocolasante wants to merge 6 commits into
masterfrom
DHIS2-21378-relationship
Draft

feat: Relationship JDBC insert/update [DHIS2-21378]#24093
enricocolasante wants to merge 6 commits into
masterfrom
DHIS2-21378-relationship

Conversation

@enricocolasante

@enricocolasante enricocolasante commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Migrates the Relationship entity in the tracker importer from Hibernate (EntityManager.persist)
to raw JDBC, as part of the Hibernate→JDBC import migration (DHIS2-21378, Phase 6).
Relationships are written through a three-statement flush that breaks the non-deferrable circular
FK between relationship and relationshipitem, and two cache-correctness fixes prevent the JDBC
write path — which bypasses Hibernate's invalidation — from serving stale relationship reads or
silently disabling duplicate detection.

Changes

  • Relationship JDBC write path (EntityWriteBatch.java): replaces persistAll(...) /
    em.persist with three batched JDBC statements per flush — a multi-row INSERT into
    relationship (with from/to_relationshipitemid left NULL), a multi-row INSERT into
    relationshipitem for the from+to items, and an unnest UPDATE that backfills the from/to FKs.
    The three-step shape is required because the circular FK between the two tables is not deferrable
    in the live schema. INSERT-only.
  • ID allocation (temporary) (RelationshipPersister.java, AbstractTrackerPersister.java):
    sequenceName() now returns hibernate_sequence (was null) so relationship ids are
    pre-allocated, and assignId(...) learns to assign the pre-allocated id to Relationship.
    Both relationship and relationshipitem draw ids from the shared global hibernate_sequence
    for now — this is temporary; dedicated sequences will be provisioned via a Flyway migration
    (see Next Steps), since the shared counter is fragile (RelationshipItem.id is a Java int
    while the global sequence is bigint).
  • ObjectStyle serialization (EntityWriteBatch.java): adds toObjectStyleJson(...) using
    JsonBinaryType.MAPPER so the style jsonb column matches the Hibernate jbObjectStyle
    UserType. Removes the now-unused persistAll helper.
  • Duplicate-detection correctness (HibernateRelationshipStore.java): forces the
    relationship-key lookup off the query cache (setCacheable(false) + QueryHints.CACHEABLE).
    JDBC writes bypass query-cache invalidation, so a cached empty result would otherwise linger
    across imports and silently disable duplicate detection.
  • **L2 entity cache disabled ** (Relationship.hbm.xml, RelationshipItem.hbm.xml):
    removes <cache usage="read-write"/> to avoid stale-cache reads of relationships/items written
    via JDBC while the importer migration is in progress.
  • Tests (RelationshipImportTest.java, tracker/relationships_duplicate.json): adds
    shouldRejectDuplicateRelationships, asserting a second import of equivalent relationships is
    rejected with E4018 (2 ignored, 0 created). Cleans stray import-parameter keys out of
    tracker/relationships.json (they belong on TrackerImportParams, not the payload).

Next Steps

Part of the DHIS2-21378 Hibernate→JDBC importer migration.

Done

  • ✅ Phases 0–5 (EntityManager out of persister API, ChangeLogAccumulator decoupling, FileResource
    store extraction, TEAV staging, top-level entity staging, named-query → JDBC).
  • ✅ Phase 6 JDBC INSERT/UPDATE for TrackedEntity, Enrollment, TrackerEvent, SingleEvent.
  • ✅ Phase 6 Relationship INSERT through JDBC (this PR).

To do

  • Provision dedicated sequences via a Flyway migration for relationship and
    relationshipitem, replacing the shared global hibernate_sequence. Required because the shared
    counter is fragile — RelationshipItem.id is a Java int while the global sequence is bigint —
    and a dedicated sequence makes the JDBC allocateIds allocation self-contained.
  • ⬜ Phase 6 TEAV INSERT/UPDATE/DELETE through JDBC (TEAV still delegates to EntityManager).
  • ⬜ Remove EntityManager from AbstractTrackerPersister / EntityWriteBatch.

@enricocolasante enricocolasante force-pushed the DHIS2-21378-relationship branch from 5ea4b99 to 1977d58 Compare June 2, 2026 09:45
@sonarqubecloud

sonarqubecloud Bot commented Jun 2, 2026

Copy link
Copy Markdown

@enricocolasante enricocolasante force-pushed the DHIS2-21378-relationship branch from 1977d58 to 146b6e8 Compare June 2, 2026 11:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant