From cbee6c31c7c424d86a69a56e683bd9a223945dc9 Mon Sep 17 00:00:00 2001 From: devmrko Date: Thu, 23 Jul 2026 10:46:11 +0900 Subject: [PATCH] refs #703: fix Smilegate annotation metadata query --- .../README.md | 2 +- .../mapper/SchemaMetadataMapper.java | 7 +----- .../service/SchemaMetadataService.java | 6 ++--- .../resources/mapper/SchemaMetadataMapper.xml | 9 +++----- .../mapper/SchemaMetadataMapperXmlTest.java | 22 +++++++++++++++++++ .../service/SchemaMetadataServiceTest.java | 8 +++---- 6 files changed, 34 insertions(+), 20 deletions(-) create mode 100644 src/test/java/com/cloudhandson/vpdbackoffice/mapper/SchemaMetadataMapperXmlTest.java diff --git a/docs/design/703-smilegate-backoffice-finalization/README.md b/docs/design/703-smilegate-backoffice-finalization/README.md index 80c00ba..3ab9e6f 100644 --- a/docs/design/703-smilegate-backoffice-finalization/README.md +++ b/docs/design/703-smilegate-backoffice-finalization/README.md @@ -54,7 +54,7 @@ MCP tool은 `SGMP_POC_HAIKU45` 프로파일을 기준으로 게임 데이터의 ### 5. 스키마 메타데이터 접근은 MyBatis로 통일한다 -`/schema-metadata`는 화면 카드 목록을 정적 허용 목록에서 만들고, 선택된 테이블의 comment·컬럼·annotation만 조회한다. 서비스 계층에는 JDBC 직접 실행을 두지 않는다. MyBatis mapper의 모든 사전 조회는 `owner = 'SGMP_POC'` 조건을 갖고, DDL에 쓰이는 테이블·컬럼·annotation 이름은 호출 전에 대문자 식별자 규칙과 허용 테이블 목록으로 검증한다. +`/schema-metadata`는 화면 카드 목록을 정적 허용 목록에서 만들고, 선택된 테이블의 comment·컬럼·annotation만 조회한다. 서비스 계층에는 JDBC 직접 실행을 두지 않는다. table/column comment 사전 조회는 `owner = 'SGMP_POC'` 조건을 갖는다. Oracle의 `ALL_ANNOTATIONS_USAGE`에는 객체 owner 컬럼이 없으므로 annotation 조회는 허용 목록에서 선택된 정확한 `OBJECT_NAME`과 `OBJECT_TYPE='TABLE'`로 한정한다. DDL에 쓰이는 테이블·컬럼·annotation 이름은 호출 전에 대문자 식별자 규칙과 허용 테이블 목록으로 검증한다. ## 변경 파일과 책임 diff --git a/src/main/java/com/cloudhandson/vpdbackoffice/mapper/SchemaMetadataMapper.java b/src/main/java/com/cloudhandson/vpdbackoffice/mapper/SchemaMetadataMapper.java index ffaac61..3ca17b1 100644 --- a/src/main/java/com/cloudhandson/vpdbackoffice/mapper/SchemaMetadataMapper.java +++ b/src/main/java/com/cloudhandson/vpdbackoffice/mapper/SchemaMetadataMapper.java @@ -20,10 +20,7 @@ public interface SchemaMetadataMapper { @Param("tableName") String tableName ); - List findAnnotations( - @Param("owner") String owner, - @Param("tableName") String tableName - ); + List findAnnotations(@Param("tableName") String tableName); int countColumn( @Param("owner") String owner, @@ -32,13 +29,11 @@ public interface SchemaMetadataMapper { ); int countTableAnnotation( - @Param("owner") String owner, @Param("tableName") String tableName, @Param("annotationName") String annotationName ); int countColumnAnnotation( - @Param("owner") String owner, @Param("tableName") String tableName, @Param("columnName") String columnName, @Param("annotationName") String annotationName diff --git a/src/main/java/com/cloudhandson/vpdbackoffice/service/SchemaMetadataService.java b/src/main/java/com/cloudhandson/vpdbackoffice/service/SchemaMetadataService.java index 5e7641f..24fb4b9 100644 --- a/src/main/java/com/cloudhandson/vpdbackoffice/service/SchemaMetadataService.java +++ b/src/main/java/com/cloudhandson/vpdbackoffice/service/SchemaMetadataService.java @@ -145,7 +145,7 @@ public class SchemaMetadataService { private Map> annotationsByTarget(String tableName) { Map>> grouped = new LinkedHashMap<>(); - for (SchemaMetadataAnnotationRow row : mapper.findAnnotations(OWNER, tableName)) { + for (SchemaMetadataAnnotationRow row : mapper.findAnnotations(tableName)) { String target = row.columnName() == null ? tableTargetKey() : columnTargetKey(row.columnName()); @@ -169,8 +169,8 @@ public class SchemaMetadataService { private boolean annotationExists(String tableName, String columnName, String annotationName) { return columnName == null - ? mapper.countTableAnnotation(OWNER, tableName, annotationName) > 0 - : mapper.countColumnAnnotation(OWNER, tableName, columnName, annotationName) > 0; + ? mapper.countTableAnnotation(tableName, annotationName) > 0 + : mapper.countColumnAnnotation(tableName, columnName, annotationName) > 0; } private String requireColumn(String tableName, String columnName) { diff --git a/src/main/resources/mapper/SchemaMetadataMapper.xml b/src/main/resources/mapper/SchemaMetadataMapper.xml index 1050d38..7bf1b6e 100644 --- a/src/main/resources/mapper/SchemaMetadataMapper.xml +++ b/src/main/resources/mapper/SchemaMetadataMapper.xml @@ -39,8 +39,7 @@ annotation_name, annotation_value FROM all_annotations_usage - WHERE object_owner = #{owner,jdbcType=VARCHAR} - AND object_name = #{tableName,jdbcType=VARCHAR} + WHERE object_name = #{tableName,jdbcType=VARCHAR} AND object_type = 'TABLE' ORDER BY column_name NULLS FIRST, annotation_name, annotation_value @@ -56,8 +55,7 @@ SELECT COUNT(*) FROM all_annotations_usage - WHERE object_owner = #{owner,jdbcType=VARCHAR} - AND object_name = #{tableName,jdbcType=VARCHAR} + WHERE object_name = #{tableName,jdbcType=VARCHAR} AND object_type = 'TABLE' AND annotation_name = #{annotationName,jdbcType=VARCHAR} AND column_name = #{columnName,jdbcType=VARCHAR} diff --git a/src/test/java/com/cloudhandson/vpdbackoffice/mapper/SchemaMetadataMapperXmlTest.java b/src/test/java/com/cloudhandson/vpdbackoffice/mapper/SchemaMetadataMapperXmlTest.java new file mode 100644 index 0000000..a9ab272 --- /dev/null +++ b/src/test/java/com/cloudhandson/vpdbackoffice/mapper/SchemaMetadataMapperXmlTest.java @@ -0,0 +1,22 @@ +package com.cloudhandson.vpdbackoffice.mapper; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import org.junit.jupiter.api.Test; + +class SchemaMetadataMapperXmlTest { + + @Test + void annotationUsageQueriesUseOnlyColumnsProvidedByTheAllView() throws IOException { + try (var input = getClass().getResourceAsStream("/mapper/SchemaMetadataMapper.xml")) { + String mapperXml = new String(input.readAllBytes(), StandardCharsets.UTF_8); + + assertThat(mapperXml) + .contains("FROM all_annotations_usage") + .contains("object_name = #{tableName,jdbcType=VARCHAR}") + .doesNotContain("object_owner"); + } + } +} diff --git a/src/test/java/com/cloudhandson/vpdbackoffice/service/SchemaMetadataServiceTest.java b/src/test/java/com/cloudhandson/vpdbackoffice/service/SchemaMetadataServiceTest.java index 0408dd5..7147b54 100644 --- a/src/test/java/com/cloudhandson/vpdbackoffice/service/SchemaMetadataServiceTest.java +++ b/src/test/java/com/cloudhandson/vpdbackoffice/service/SchemaMetadataServiceTest.java @@ -39,7 +39,7 @@ class SchemaMetadataServiceTest { void readsOnlyTheSelectedSmilegateTableThroughMapper() { when(mapper.findTableComment("SGMP_POC", "CZN_COMN_USER_MST")) .thenReturn("게임별 사용자 기준 정보"); - when(mapper.findAnnotations("SGMP_POC", "CZN_COMN_USER_MST")).thenReturn(List.of( + when(mapper.findAnnotations("CZN_COMN_USER_MST")).thenReturn(List.of( new SchemaMetadataAnnotationRow(null, "BUSINESS_TERM", "게임 사용자"), new SchemaMetadataAnnotationRow("GUID", "BUSINESS_TERM", "사용자 고유 식별자"), new SchemaMetadataAnnotationRow("GUID", "BUSINESS_TERM", "고객 계정 식별자") @@ -62,14 +62,14 @@ class SchemaMetadataServiceTest { .extracting(annotation -> annotation.value()) .isEqualTo("사용자 고유 식별자\n--- duplicate annotation value ---\n고객 계정 식별자"); }); - verify(mapper).findAnnotations("SGMP_POC", "CZN_COMN_USER_MST"); + verify(mapper).findAnnotations("CZN_COMN_USER_MST"); verify(mapper).findColumns("SGMP_POC", "CZN_COMN_USER_MST"); } @Test void replacesAnExistingColumnAnnotationUsingMapperDdl() { when(mapper.countColumn("SGMP_POC", "CZN_COMN_USER_MST", "GUID")).thenReturn(1); - when(mapper.countColumnAnnotation("SGMP_POC", "CZN_COMN_USER_MST", "GUID", "BUSINESS_TERM")) + when(mapper.countColumnAnnotation("CZN_COMN_USER_MST", "GUID", "BUSINESS_TERM")) .thenReturn(1); service.updateColumnAnnotation("game-users", "guid", "business_term", "사용자 고유 식별자"); @@ -81,7 +81,7 @@ class SchemaMetadataServiceTest { @Test void clearsAnExistingTableAnnotationWithoutAddingAnEmptyValue() { - when(mapper.countTableAnnotation("SGMP_POC", "CZN_COMN_USER_MST", "BUSINESS_TERM")) + when(mapper.countTableAnnotation("CZN_COMN_USER_MST", "BUSINESS_TERM")) .thenReturn(1); service.updateTableAnnotation("game-users", "business_term", " ");