From b76bb1a36b9bbe360e1d027df50a7958f43e29d9 Mon Sep 17 00:00:00 2001 From: "Donald F. Coffin" Date: Thu, 8 Jan 2026 12:21:42 -0500 Subject: [PATCH] feat: ESPI 4.0 Schema Compliance - Phase 4: ReadingQuality MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit implements Phase 4 of ESPI 4.0 schema compliance, updating ReadingQuality to match the ESPI 4.0 XSD specification (espi.xsd:1062). Key Changes: ReadingQualityEntity: - Changed ID from UUID to Long (48+ bits) per Object pattern - ReadingQuality extends Object, not IdentifiedObject - Updated @GeneratedValue to use IDENTITY strategy - Removed UUID-related imports (JdbcTypeCode, SqlTypes) - Field order already correct (only has quality field per XSD) ReadingQualityDto: - Already compliant - no changes needed - Single quality field matches XSD specification ReadingQualityMapper: - Simplified mappings (MapStruct handles by name) - Removed explicit quality field mapping - Kept only necessary ignore mappings (id, intervalReading) Flyway Migrations: - Moved reading_qualities table from V3 to V2 vendor-specific files - Rationale: ReadingQuality requires vendor-specific auto-increment syntax (BIGINT AUTO_INCREMENT for MySQL/H2, BIGSERIAL for PostgreSQL) - Removed all IdentifiedObject fields (description, created, updated, published, links) from table structure - Updated V3 header to document reading_qualities relocation - Added to dependency chain: meter_readings → interval_blocks → interval_readings → reading_qualities Test Results: - All 550 tests passing including integration tests - PostgreSQL TestContainer integration tests: ✅ - MySQL TestContainer integration tests: ✅ - H2 in-memory integration tests: ✅ Migration Strategy: - V1: Base vendor-neutral tables - V2: Vendor-specific tables (now includes meter_readings, interval_blocks, interval_readings, reading_qualities with correct auto-increment syntax) - V3: Additional tables that depend on V1 and V2 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- .../domain/usage/ReadingQualityEntity.java | 13 ++--- .../mapper/usage/ReadingQualityMapper.java | 4 +- .../V3__Create_additiional_Base_Tables.sql | 47 +++++-------------- .../db/vendor/h2/V2__H2_Specific_Tables.sql | 20 ++++++++ .../mysql/V2__MySQL_Specific_Tables.sql | 19 ++++++++ .../V2__PostgreSQL_Specific_Tables.sql | 19 ++++++++ 6 files changed, 76 insertions(+), 46 deletions(-) diff --git a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/usage/ReadingQualityEntity.java b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/usage/ReadingQualityEntity.java index 062bf916..de6ab782 100644 --- a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/usage/ReadingQualityEntity.java +++ b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/usage/ReadingQualityEntity.java @@ -25,12 +25,9 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import org.hibernate.annotations.JdbcTypeCode; import org.hibernate.proxy.HibernateProxy; -import org.hibernate.type.SqlTypes; import java.util.Objects; -import java.util.UUID; /** * Pure JPA/Hibernate entity for ReadingQuality without JAXB concerns. @@ -55,13 +52,13 @@ public class ReadingQualityEntity { /** - * Primary key identifier. + * Primary key identifier (48+ bits as per ESPI requirement). + * ReadingQuality extends Object (not IdentifiedObject) per ESPI 4.0 XSD. */ @Id - @GeneratedValue(strategy = GenerationType.UUID) - @JdbcTypeCode(SqlTypes.CHAR) - @Column(length = 36, columnDefinition = "char(36)", updatable = false, nullable = false) - private UUID id; + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; // Quality constants based on common industry standards public static final String QUALITY_GOOD = "GOOD"; diff --git a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/mapper/usage/ReadingQualityMapper.java b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/mapper/usage/ReadingQualityMapper.java index 44d2888b..10a24864 100644 --- a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/mapper/usage/ReadingQualityMapper.java +++ b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/mapper/usage/ReadingQualityMapper.java @@ -41,18 +41,16 @@ public interface ReadingQualityMapper { * @param entity the reading quality entity * @return the reading quality DTO */ - @Mapping(target = "quality", source = "quality") ReadingQualityDto toDto(ReadingQualityEntity entity); /** * Converts a ReadingQualityDto to a ReadingQualityEntity. - * Maps quality indicators and validation information. + * The ID is generated by the database, and intervalReading is set by the parent. * * @param dto the reading quality DTO * @return the reading quality entity */ @Mapping(target = "id", ignore = true) - @Mapping(target = "quality", source = "quality") @Mapping(target = "intervalReading", ignore = true) ReadingQualityEntity toEntity(ReadingQualityDto dto); diff --git a/openespi-common/src/main/resources/db/migration/V3__Create_additiional_Base_Tables.sql b/openespi-common/src/main/resources/db/migration/V3__Create_additiional_Base_Tables.sql index 386b256c..073c30d2 100644 --- a/openespi-common/src/main/resources/db/migration/V3__Create_additiional_Base_Tables.sql +++ b/openespi-common/src/main/resources/db/migration/V3__Create_additiional_Base_Tables.sql @@ -17,14 +17,14 @@ * - interval_blocks (depends on meter_readings) * - interval_block_related_links (FK dependency) * - interval_readings (extends Object, requires vendor-specific auto-increment, depends on interval_blocks) + * - reading_qualities (extends Object, requires vendor-specific auto-increment, depends on interval_readings) * - * Reason: IntervalReading extends Object (not IdentifiedObject) per ESPI 4.0 XSD (espi.xsd:1016), - * requiring Long ID with vendor-specific auto-increment syntax (BIGINT AUTO_INCREMENT for MySQL/H2, - * BIGSERIAL for PostgreSQL). To keep the dependency chain together (meter_readings → interval_blocks - * → interval_readings), all three were moved to V2 vendor files. + * Reason: IntervalReading and ReadingQuality both extend Object (not IdentifiedObject) per ESPI 4.0 XSD + * (espi.xsd:1016 and espi.xsd:1062), requiring Long ID with vendor-specific auto-increment syntax + * (BIGINT AUTO_INCREMENT for MySQL/H2, BIGSERIAL for PostgreSQL). To keep the dependency chain together + * (meter_readings → interval_blocks → interval_readings → reading_qualities), all were moved to V2 vendor files. * * Tables in this migration: - * - reading_qualities (depends on interval_readings from V2) * - usage_summaries (depends on usage_points from V2) * - usage_summary_related_links (FK dependency) * - subscription_usage_points (join table) @@ -49,40 +49,17 @@ -- 1. meter_readings + meter_reading_related_links -- 2. interval_blocks + interval_block_related_links -- 3. interval_readings (no related_links - extends Object, not IdentifiedObject) +-- 4. reading_qualities (no related_links - extends Object, not IdentifiedObject) -- -- See file header for detailed explanation. -- ================================================================================== --- Reading Quality Table -CREATE TABLE reading_qualities -( - id CHAR(36) PRIMARY KEY , - description VARCHAR(255), - created TIMESTAMP NOT NULL, - updated TIMESTAMP NOT NULL, - published TIMESTAMP, - up_link_rel VARCHAR(255), - up_link_href VARCHAR(1024), - up_link_type VARCHAR(255), - self_link_rel VARCHAR(255), - self_link_href VARCHAR(1024), - self_link_type VARCHAR(255), - - -- Reading quality specific fields - quality VARCHAR(50), - - -- Foreign key relationships - -- IntervalReading uses Long ID (BIGINT/BIGSERIAL) as it extends Object, not IdentifiedObject - interval_reading_id BIGINT, - - FOREIGN KEY (interval_reading_id) REFERENCES interval_readings (id) ON DELETE CASCADE -); - --- Indexes for reading_qualities table -CREATE INDEX idx_reading_quality_interval_reading_id ON reading_qualities (interval_reading_id); -CREATE INDEX idx_reading_quality_quality ON reading_qualities (quality); -CREATE INDEX idx_reading_quality_created ON reading_qualities (created); -CREATE INDEX idx_reading_quality_updated ON reading_qualities (updated); +-- Reading Quality Table - Moved to vendor-specific V2 migration files +-- ReadingQuality extends Object (not IdentifiedObject) per ESPI 4.0 XSD (espi.xsd:1062) +-- Table creation moved to V2 vendor files due to auto-increment syntax differences +-- See: db/vendor/mysql/V2__MySQL_Specific_Tables.sql +-- db/vendor/postgres/V2__PostgreSQL_Specific_Tables.sql +-- db/vendor/h2/V2__H2_Specific_Tables.sql -- Usage Summary Table CREATE TABLE usage_summaries diff --git a/openespi-common/src/main/resources/db/vendor/h2/V2__H2_Specific_Tables.sql b/openespi-common/src/main/resources/db/vendor/h2/V2__H2_Specific_Tables.sql index b101b1c8..1de5c8e5 100644 --- a/openespi-common/src/main/resources/db/vendor/h2/V2__H2_Specific_Tables.sql +++ b/openespi-common/src/main/resources/db/vendor/h2/V2__H2_Specific_Tables.sql @@ -291,3 +291,23 @@ CREATE TABLE interval_readings CREATE INDEX idx_interval_reading_block_id ON interval_readings (interval_block_id); CREATE INDEX idx_interval_reading_time_start ON interval_readings (time_period_start); CREATE INDEX idx_interval_reading_value ON interval_readings (reading_value); + +-- Reading Quality Table (Object-based entity, no IdentifiedObject) +-- ReadingQuality extends Object per ESPI 4.0 XSD (espi.xsd:1062) +-- XSD sequence: quality +CREATE TABLE reading_qualities +( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + + -- ESPI 4.0 field + quality VARCHAR(50) NOT NULL, + + -- Foreign key relationship (parent: IntervalReading) + interval_reading_id BIGINT, + + FOREIGN KEY (interval_reading_id) REFERENCES interval_readings (id) ON DELETE CASCADE +); + +-- Create indexes for reading_qualities table +CREATE INDEX idx_reading_quality_interval_reading_id ON reading_qualities (interval_reading_id); +CREATE INDEX idx_reading_quality_quality ON reading_qualities (quality); diff --git a/openespi-common/src/main/resources/db/vendor/mysql/V2__MySQL_Specific_Tables.sql b/openespi-common/src/main/resources/db/vendor/mysql/V2__MySQL_Specific_Tables.sql index 648d80a9..c700730c 100644 --- a/openespi-common/src/main/resources/db/vendor/mysql/V2__MySQL_Specific_Tables.sql +++ b/openespi-common/src/main/resources/db/vendor/mysql/V2__MySQL_Specific_Tables.sql @@ -295,3 +295,22 @@ CREATE TABLE interval_readings INDEX idx_interval_reading_time_start (time_period_start), INDEX idx_interval_reading_value (reading_value) ); + +-- Reading Quality Table (Object-based entity, no IdentifiedObject) +-- ReadingQuality extends Object per ESPI 4.0 XSD (espi.xsd:1062) +-- XSD sequence: quality +CREATE TABLE reading_qualities +( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + + -- ESPI 4.0 field + quality VARCHAR(50) NOT NULL, + + -- Foreign key relationship (parent: IntervalReading) + interval_reading_id BIGINT, + + FOREIGN KEY (interval_reading_id) REFERENCES interval_readings (id) ON DELETE CASCADE, + + INDEX idx_reading_quality_interval_reading_id (interval_reading_id), + INDEX idx_reading_quality_quality (quality) +); diff --git a/openespi-common/src/main/resources/db/vendor/postgres/V2__PostgreSQL_Specific_Tables.sql b/openespi-common/src/main/resources/db/vendor/postgres/V2__PostgreSQL_Specific_Tables.sql index 828a8484..c1a54778 100644 --- a/openespi-common/src/main/resources/db/vendor/postgres/V2__PostgreSQL_Specific_Tables.sql +++ b/openespi-common/src/main/resources/db/vendor/postgres/V2__PostgreSQL_Specific_Tables.sql @@ -283,3 +283,22 @@ CREATE TABLE interval_readings CREATE INDEX idx_interval_reading_block_id ON interval_readings (interval_block_id); CREATE INDEX idx_interval_reading_time_start ON interval_readings (time_period_start); CREATE INDEX idx_interval_reading_value ON interval_readings (reading_value); + +-- Reading Quality Table (Object-based entity, no IdentifiedObject) +-- ReadingQuality extends Object per ESPI 4.0 XSD (espi.xsd:1062) +-- XSD sequence: quality +CREATE TABLE reading_qualities +( + id BIGSERIAL PRIMARY KEY, + + -- ESPI 4.0 field + quality VARCHAR(50) NOT NULL, + + -- Foreign key relationship (parent: IntervalReading) + interval_reading_id BIGINT, + + FOREIGN KEY (interval_reading_id) REFERENCES interval_readings (id) ON DELETE CASCADE +); + +CREATE INDEX idx_reading_quality_interval_reading_id ON reading_qualities (interval_reading_id); +CREATE INDEX idx_reading_quality_quality ON reading_qualities (quality);