Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,11 @@
import lombok.Setter;
import org.greenbuttonalliance.espi.common.domain.common.DateTimeInterval;
import org.hibernate.annotations.BatchSize;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.type.SqlTypes;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

/**
* Pure JPA/Hibernate entity for IntervalReading without JAXB concerns.
Expand All @@ -52,13 +49,13 @@
public class IntervalReadingEntity {

/**
* Primary key identifier.
* Primary key identifier (48+ bits as per ESPI requirement).
* IntervalReading 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;

/**
* Cost associated with this interval reading.
Expand All @@ -67,6 +64,29 @@ public class IntervalReadingEntity {
@Column(name = "cost")
private Long cost;

/**
* Reading quality indicators for this interval reading.
* One-to-many relationship with cascade and orphan removal.
* -- GETTER --
* Gets the reading qualities collection.
* Lombok @Data should generate this, but added manually for compilation.
*
* @return the list of reading qualities

*/
@OneToMany(mappedBy = "intervalReading", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
@BatchSize(size = 10)
private List<ReadingQualityEntity> readingQualities = new ArrayList<>();

/**
* Time period for this interval reading.
* Embedded value object containing start time and duration.
*/
@Embedded
@AttributeOverride(name = "start", column = @Column(name = "time_period_start"))
@AttributeOverride(name = "duration", column = @Column(name = "time_period_duration"))
private DateTimeInterval timePeriod;

/**
* The actual measured value for this interval.
* This is the primary measurement data.
Expand Down Expand Up @@ -95,15 +115,6 @@ public class IntervalReadingEntity {
@Column(name = "cpp")
private Long cpp;

/**
* Time period for this interval reading.
* Embedded value object containing start time and duration.
*/
@Embedded
@AttributeOverride(name = "start", column = @Column(name = "time_period_start"))
@AttributeOverride(name = "duration", column = @Column(name = "time_period_duration"))
private DateTimeInterval timePeriod;

/**
* Interval block that contains this reading.
* Many interval readings belong to one interval block.
Expand All @@ -112,20 +123,6 @@ public class IntervalReadingEntity {
@JoinColumn(name = "interval_block_id")
private IntervalBlockEntity intervalBlock;

/**
* Reading quality indicators for this interval reading.
* One-to-many relationship with cascade and orphan removal.
* -- GETTER --
* Gets the reading qualities collection.
* Lombok @Data should generate this, but added manually for compilation.
*
* @return the list of reading qualities

*/
@OneToMany(mappedBy = "intervalReading", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
@BatchSize(size = 10)
private List<ReadingQualityEntity> readingQualities = new ArrayList<>();

/**
* Constructor with core reading data.
*
Expand Down Expand Up @@ -333,10 +330,10 @@ public String toString() {
return getClass().getSimpleName() + "(" +
"id = " + getId() + ", " +
"cost = " + getCost() + ", " +
"timePeriod = " + getTimePeriod() + ", " +
"value = " + getValue() + ", " +
"consumptionTier = " + getConsumptionTier() + ", " +
"tou = " + getTou() + ", " +
"cpp = " + getCpp() + ", " +
"timePeriod = " + getTimePeriod() + ")";
"cpp = " + getCpp() + ")";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,26 +35,21 @@
@XmlRootElement(name = "IntervalReading", namespace = "http://naesb.org/espi")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "IntervalReading", namespace = "http://naesb.org/espi", propOrder = {
"cost", "currency", "value", "timePeriod", "readingQualities",
"consumptionTier", "tou", "cpp"
"cost", "readingQualities", "timePeriod", "value", "consumptionTier", "tou", "cpp"
})
public record IntervalReadingDto(

@XmlElement(name = "cost")
Long cost,

@XmlElement(name = "currency")
Integer currency,

@XmlElement(name = "value")
Long value,
@XmlElement(name = "ReadingQuality")
List<ReadingQualityDto> readingQualities,

@XmlElement(name = "timePeriod")
DateTimeIntervalDto timePeriod,

@XmlElement(name = "ReadingQuality")
//@XmlElementWrapper(name = "ReadingQualities")
List<ReadingQualityDto> readingQualities,
@XmlElement(name = "value")
Long value,

@XmlElement(name = "consumptionTier")
Integer consumptionTier,
Expand All @@ -70,20 +65,20 @@ public record IntervalReadingDto(
* Default constructor for JAXB.
*/
public IntervalReadingDto() {
this(null, null, null, null, null, null, null, null);
this(null, null, null, null, null, null, null);
}

/**
* Minimal constructor for basic interval reading data.
*/
public IntervalReadingDto(Long value, DateTimeIntervalDto timePeriod) {
this(null, null, value, timePeriod, null, null, null, null);
this(null, null, timePeriod, value, null, null, null);
}

/**
* Constructor for interval reading with cost information.
*/
public IntervalReadingDto(Long value, Long cost, Integer currency, DateTimeIntervalDto timePeriod) {
this(cost, currency, value, timePeriod, null, null, null, null);
public IntervalReadingDto(Long value, Long cost, DateTimeIntervalDto timePeriod) {
this(cost, null, timePeriod, value, null, null, null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,31 +44,17 @@ public interface IntervalReadingMapper {
* @param entity the interval reading entity
* @return the interval reading DTO
*/
@Mapping(target = "cost", source = "cost")
@Mapping(target = "currency", ignore = true)
@Mapping(target = "value", source = "value")
@Mapping(target = "timePeriod", source = "timePeriod")
@Mapping(target = "readingQualities", source = "readingQualities")
@Mapping(target = "consumptionTier", source = "consumptionTier")
@Mapping(target = "tou", source = "tou")
@Mapping(target = "cpp", source = "cpp")
IntervalReadingDto toDto(IntervalReadingEntity entity);

/**
* Converts an IntervalReadingDto to an IntervalReadingEntity.
* Maps all related DTOs to their corresponding entities.
* The ID is generated by the database, and intervalBlock is set by the parent.
*
* @param dto the interval reading DTO
* @return the interval reading entity
*/
@Mapping(target = "id", ignore = true)
@Mapping(target = "cost", source = "cost")
@Mapping(target = "value", source = "value")
@Mapping(target = "timePeriod", source = "timePeriod")
@Mapping(target = "readingQualities", source = "readingQualities")
@Mapping(target = "consumptionTier", source = "consumptionTier")
@Mapping(target = "tou", source = "tou")
@Mapping(target = "cpp", source = "cpp")
@Mapping(target = "intervalBlock", ignore = true)
IntervalReadingEntity toEntity(IntervalReadingDto dto);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,121 +1,57 @@
-- Meter Reading Table
CREATE TABLE meter_readings
(
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),

-- Foreign key relationships
usage_point_id CHAR(36),
reading_type_id CHAR(36),

FOREIGN KEY (usage_point_id) REFERENCES usage_points (id) ON DELETE CASCADE,
FOREIGN KEY (reading_type_id) REFERENCES reading_types (id) ON DELETE SET NULL
);

-- Indexes for meter_readings table
CREATE INDEX idx_meter_reading_usage_point_id ON meter_readings (usage_point_id);
CREATE INDEX idx_meter_reading_reading_type_id ON meter_readings (reading_type_id);
CREATE INDEX idx_meter_reading_created ON meter_readings (created);
CREATE INDEX idx_meter_reading_updated ON meter_readings (updated);

-- Related Links Table for Meter Readings
CREATE TABLE meter_reading_related_links
(
meter_reading_id CHAR(36) NOT NULL,
related_links VARCHAR(1024),
FOREIGN KEY (meter_reading_id) REFERENCES meter_readings (id) ON DELETE CASCADE
);

-- Indexes for meter_reading_related_links table
CREATE INDEX idx_meter_reading_related_links ON meter_reading_related_links (meter_reading_id);

-- Interval Block Table
CREATE TABLE interval_blocks
(
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),

-- Interval block specific fields
interval_duration BIGINT,
interval_start BIGINT,

-- Foreign key relationships
meter_reading_id CHAR(36),

FOREIGN KEY (meter_reading_id) REFERENCES meter_readings (id) ON DELETE CASCADE
);

-- Indexes for interval_blocks table
CREATE INDEX idx_interval_block_meter_reading_id ON interval_blocks (meter_reading_id);
CREATE INDEX idx_interval_block_start ON interval_blocks (interval_start);
CREATE INDEX idx_interval_block_created ON interval_blocks (created);
CREATE INDEX idx_interval_block_updated ON interval_blocks (updated);

-- Related Links Table for Interval Blocks
CREATE TABLE interval_block_related_links
(
interval_block_id CHAR(36) NOT NULL,
related_links VARCHAR(1024),
FOREIGN KEY (interval_block_id) REFERENCES interval_blocks (id) ON DELETE CASCADE
);

-- Indexes for interval_block_related_links table
CREATE INDEX idx_interval_block_related_links ON interval_block_related_links (interval_block_id);

-- Interval Reading Table
CREATE TABLE interval_readings
(
id CHAR(36) PRIMARY KEY ,
description VARCHAR(255),
created TIMESTAMP,
updated TIMESTAMP,
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),

-- Interval reading specific fields
cost BIGINT,
reading_value BIGINT,
time_period_start BIGINT,
time_period_duration BIGINT,
consumption_tier BIGINT,
tou BIGINT,
cpp BIGINT,

-- Foreign key relationships
interval_block_id CHAR(36),

FOREIGN KEY (interval_block_id) REFERENCES interval_blocks (id) ON DELETE CASCADE
);

-- Indexes for interval_readings table
CREATE INDEX idx_interval_reading_interval_block_id ON interval_readings (interval_block_id);
CREATE INDEX idx_interval_reading_time_period_start ON interval_readings (time_period_start);
CREATE INDEX idx_interval_reading_value ON interval_readings (reading_value);
CREATE INDEX idx_interval_reading_created ON interval_readings (created);
CREATE INDEX idx_interval_reading_updated ON interval_readings (updated);
/*
* OpenESPI Additional Base Tables Migration
*
* Copyright (c) 2018-2025 Green Button Alliance, Inc.
* Licensed under the Apache License, Version 2.0
*
* This migration creates additional vendor-neutral tables that depend on both
* V1 (base tables) and V2 (vendor-specific tables) to already exist.
*
* IMPORTANT: The following tables were moved to V2 vendor-specific files
* because they require vendor-specific syntax OR are part of dependency chains
* that include vendor-specific tables:
*
* Moved to V2:
* - meter_readings (depends on usage_points from V2)
* - meter_reading_related_links (FK dependency)
* - 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)
*
* 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.
*
* 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)
* - aggregated_node_refs (depends on pnode_refs from V2)
* - customer schema tables
* - end_device schema tables
* - service location/supplier tables
* - statement tables
*
* Compatible with: H2, MySQL, PostgreSQL
*/

-- ==================================================================================
-- TABLES MOVED TO V2 VENDOR-SPECIFIC MIGRATION FILES
-- ==================================================================================
-- The following tables have been moved to V2 vendor-specific files:
-- - db/vendor/mysql/V2__MySQL_Specific_Tables.sql
-- - db/vendor/postgres/V2__PostgreSQL_Specific_Tables.sql
-- - db/vendor/h2/V2__H2_Specific_Tables.sql
--
-- Tables moved:
-- 1. meter_readings + meter_reading_related_links
-- 2. interval_blocks + interval_block_related_links
-- 3. interval_readings (no related_links - extends Object, not IdentifiedObject)
--
-- See file header for detailed explanation.
-- ==================================================================================

-- Reading Quality Table
CREATE TABLE reading_qualities
Expand All @@ -136,7 +72,8 @@ CREATE TABLE reading_qualities
quality VARCHAR(50),

-- Foreign key relationships
interval_reading_id CHAR(36),
-- 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
);
Expand Down
Loading
Loading