ProjectRequirementVersionRepository.java

package com.taxonomy.portfolio.repository;

import com.taxonomy.portfolio.model.ProjectRequirementVersion;
import jakarta.persistence.Persistence;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.List;
import java.util.Optional;

public interface ProjectRequirementVersionRepository
        extends JpaRepository<ProjectRequirementVersion, Long> {

    List<ProjectRequirementVersion> findByRequirementIdAndScopeKeyOrderByVersionNumberDesc(
            Long requirementId, String scopeKey);

    Optional<ProjectRequirementVersion>
            findFirstByRequirementIdAndScopeKeyOrderByVersionNumberDesc(
                    Long requirementId, String scopeKey);

    Optional<ProjectRequirementVersion> findByRequirementIdAndScopeKeyAndVersionNumber(
            Long requirementId, String scopeKey, int versionNumber);

    Optional<ProjectRequirementVersion> findByRequirementIdAndScopeKeyAndContentHash(
            Long requirementId, String scopeKey, String contentHash);

    /**
     * Resolve from the already populated persistence context when a requirement
     * list join-fetched its current version. Otherwise execute a query containing
     * the complete requirement and tenant key; never initialize an ID-only proxy.
     */
    default Optional<ProjectRequirementVersion> findByIdAndRequirementIdAndScopeKey(
            Long id, Long requirementId, String scopeKey) {
        if (id == null || requirementId == null
                || scopeKey == null || scopeKey.isBlank()) {
            return Optional.empty();
        }
        String normalizedScope = scopeKey.strip();
        ProjectRequirementVersion candidate = getReferenceById(id);
        if (Persistence.getPersistenceUtil().isLoaded(candidate)) {
            return Optional.of(candidate)
                    .filter(version -> requirementId.equals(version.getRequirementId()))
                    .filter(version -> normalizedScope.equals(version.getScopeKey()));
        }
        return findExactTenantVersion(id, requirementId, normalizedScope);
    }

    @Query("select version from ProjectRequirementVersion version "
            + "where version.id = :id and version.requirementId = :requirementId "
            + "and version.scopeKey = :scopeKey")
    Optional<ProjectRequirementVersion> findExactTenantVersion(
            @Param("id") Long id,
            @Param("requirementId") Long requirementId,
            @Param("scopeKey") String scopeKey);

    /** Compatibility methods retained while all callers migrate to exact scope. */
    @Deprecated(forRemoval = false)
    List<ProjectRequirementVersion> findByRequirementIdOrderByVersionNumberDesc(
            Long requirementId);

    @Deprecated(forRemoval = false)
    Optional<ProjectRequirementVersion> findFirstByRequirementIdOrderByVersionNumberDesc(
            Long requirementId);

    @Deprecated(forRemoval = false)
    Optional<ProjectRequirementVersion> findByRequirementIdAndVersionNumber(
            Long requirementId, int versionNumber);

    @Deprecated(forRemoval = false)
    Optional<ProjectRequirementVersion> findByRequirementIdAndContentHash(
            Long requirementId, String contentHash);

    @Deprecated(forRemoval = false)
    Optional<ProjectRequirementVersion> findByIdAndRequirementId(
            Long id, Long requirementId);
}