From d719b75c4048546eb0c0a3310a1dd7fd0d34e610 Mon Sep 17 00:00:00 2001 From: Rinjae Date: Fri, 19 Dec 2025 10:02:54 +0900 Subject: [PATCH 1/2] =?UTF-8?q?GwMetric=20ibatis=20->=20jpa=20=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oracle/sqlMapConfigOfOnl.xml | 2 - .../entity/onl/apim/obp/ObpGwMetricDAO.java | 147 ---------- .../onl/apim/obp/ObpGwMetricJpaDAO.java | 255 ++++++++++++++++++ .../onl/apim/obp/ObpGwMetricManService.java | 32 +-- .../eai/apim/obp/ObpGwMetric-oracle.xml | 99 ------- 5 files changed, 267 insertions(+), 268 deletions(-) delete mode 100644 src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricDAO.java create mode 100644 src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricJpaDAO.java delete mode 100644 src/main/resources/com/eactive/eai/apim/obp/ObpGwMetric-oracle.xml diff --git a/WebContent/WEB-INF/sqlmap-config/oracle/sqlMapConfigOfOnl.xml b/WebContent/WEB-INF/sqlmap-config/oracle/sqlMapConfigOfOnl.xml index 0355c0c..44e1f9b 100644 --- a/WebContent/WEB-INF/sqlmap-config/oracle/sqlMapConfigOfOnl.xml +++ b/WebContent/WEB-INF/sqlmap-config/oracle/sqlMapConfigOfOnl.xml @@ -202,6 +202,4 @@ - - \ No newline at end of file diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricDAO.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricDAO.java deleted file mode 100644 index b1cc50f..0000000 --- a/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricDAO.java +++ /dev/null @@ -1,147 +0,0 @@ -package com.eactive.eai.rms.data.entity.onl.apim.obp; - -import com.eactive.eai.rms.common.base.SqlMapClientTemplateDao; -import org.springframework.stereotype.Repository; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -/** - * ObpGwMetric DAO - iBATIS를 사용한 로그 테이블 집계 - */ -@Repository -public class ObpGwMetricDAO extends SqlMapClientTemplateDao { - - /** - * EAIBZWKDSTCD 코드 목록 조회 - */ - @SuppressWarnings("unchecked") - public List selectAllBzwkCodes(String schemaId) { - HashMap paramMap = new HashMap<>(); - paramMap.put("schemaId", schemaId); - return this.template.queryForList("ObpGwMetric.selectAllBzwkCodes", paramMap); - } - - /** - * EAIBZWKDSTCD별 시간대 로그 집계 - */ - @SuppressWarnings("unchecked") - public List selectHourlyMetricsByBzwkCode(HashMap paramMap) { - return this.template.queryForList("ObpGwMetric.selectHourlyMetricsByBzwkCode", paramMap); - } - - /** - * API 이름 조회 (TSEAIHE01) - */ - @SuppressWarnings("unchecked") - public Map selectApiNames(String schemaId, Set apiIds) { - if (apiIds == null || apiIds.isEmpty()) { - return new HashMap<>(); - } - - HashMap paramMap = new HashMap<>(); - paramMap.put("schemaId", schemaId); - paramMap.put("apiIds", new ArrayList<>(apiIds)); // Set → List 변환 (iBATIS iterate 호환) - - List> resultList = this.template.queryForList("ObpGwMetric.selectApiNames", paramMap); - - Map result = new HashMap<>(); - for (Map m : resultList) { - String apiId = m.get("API_ID"); - String apiName = m.get("API_NAME"); - if (apiId != null && apiName != null) { - result.put(apiId, apiName); - } - } - return result; - } - - /** - * URI 및 HTTP Method 조회 결과 - */ - public static class UriMethodResult { - private final Map uriMap; - private final Map methodMap; - - public UriMethodResult(Map uriMap, Map methodMap) { - this.uriMap = uriMap; - this.methodMap = methodMap; - } - - public Map getUriMap() { - return uriMap; - } - - public Map getMethodMap() { - return methodMap; - } - } - - /** - * URI 및 HTTP Method 조회 (HTTP_ADAPTER_EXTRA_LOG) - * - GUID = TSEAILG1X.EAISVCSERNO와 매핑 - * - SERVICE_PROCESS_NUMBER = 100 (인바운드 요청) - * - * @param schemaId DB 스키마 ID - * @param eaiSvcSernos EAI 서비스 일련번호 목록 (GUID) - * @return UriMethodResult (eaiSvcSerno → URI 맵, eaiSvcSerno → METHOD 맵) - */ - @SuppressWarnings("unchecked") - public UriMethodResult selectUrisAndMethods(String schemaId, Set eaiSvcSernos) { - if (eaiSvcSernos == null || eaiSvcSernos.isEmpty()) { - return new UriMethodResult(new HashMap<>(), new HashMap<>()); - } - - HashMap paramMap = new HashMap<>(); - paramMap.put("schemaId", schemaId); - paramMap.put("eaiSvcSernos", new ArrayList<>(eaiSvcSernos)); // Set → List 변환 (iBATIS iterate 호환) - - List> resultList = this.template.queryForList("ObpGwMetric.selectUris", paramMap); - - Map uriMap = new HashMap<>(); - Map methodMap = new HashMap<>(); - for (Map m : resultList) { - String eaiSvcSerno = m.get("EAI_SVC_SERNO"); - String uri = m.get("URI"); - String method = m.get("METHOD"); - if (eaiSvcSerno != null) { - if (uri != null) { - uriMap.put(eaiSvcSerno, uri); - } - if (method != null) { - methodMap.put(eaiSvcSerno, method); - } - } - } - return new UriMethodResult(uriMap, methodMap); - } - - /** - * AppId 조회 (ptl_app_request) - org_id로 조회 - */ - @SuppressWarnings("unchecked") - public Map selectAppIdsByOrgIds(Set orgIds) { - if (orgIds == null || orgIds.isEmpty()) { - return new HashMap<>(); - } - - HashMap paramMap = new HashMap<>(); - paramMap.put("orgIds", new ArrayList<>(orgIds)); // Set → List 변환 (iBATIS iterate 호환) - - List> resultList = this.template.queryForList("ObpGwMetric.selectAppIdsByOrgIds", paramMap); - - Map result = new HashMap<>(); - for (Map m : resultList) { - String orgId = m.get("ORG_ID"); - String appId = m.get("APP_ID"); - if (orgId != null && appId != null) { - result.put(orgId, appId); - } - } - return result; - } -} diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricJpaDAO.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricJpaDAO.java new file mode 100644 index 0000000..c734adc --- /dev/null +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricJpaDAO.java @@ -0,0 +1,255 @@ +package com.eactive.eai.rms.data.entity.onl.apim.obp; + +import com.eactive.apim.portal.apprequest.entity.AppRequest; +import com.eactive.apim.portal.apprequest.entity.QAppRequest; +import com.eactive.eai.data.entity.onl.message.EAIMessageEntity; +import com.eactive.eai.data.entity.onl.message.QEAIMessageEntity; +import com.eactive.eai.data.entity.onl.unifbwk.QUnifBwkTp; +import com.querydsl.core.Tuple; +import com.querydsl.jpa.impl.JPAQueryFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Repository; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.Query; +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; + +/** + * ObpGwMetric JPA DAO - JPA/QueryDSL을 사용한 로그 테이블 집계 + * + *

Multi-tenancy 지원: 호출 전 DataSourceContextHolder 설정 필요

+ */ +@Repository +public class ObpGwMetricJpaDAO { + + private static final Logger log = LoggerFactory.getLogger(ObpGwMetricJpaDAO.class); + + @PersistenceContext + private EntityManager entityManager; + + private JPAQueryFactory getQueryFactory() { + return new JPAQueryFactory(entityManager); + } + + /** + * EAIBZWKDSTCD 코드 목록 조회 (TSEAICM01) + * + * @return 업무구분코드 목록 + */ + public List selectAllBzwkCodes() { + QUnifBwkTp cm01 = QUnifBwkTp.unifBwkTp; + + return getQueryFactory() + .select(cm01.eaibzwkdstcd) + .from(cm01) + .orderBy(cm01.eaibzwkdstcd.asc()) + .fetch(); + } + + /** + * EAIBZWKDSTCD별 시간대 로그 집계 (Native Query) + * + *

동적 테이블명(TSEAILG1X)을 사용하므로 Native Query 필요

+ * + * @param tableName 동적 테이블명 (예: TSEAILG14) + * @param bzwkCode 업무구분코드 + * @param startTime 시작시간 (yyyyMMddHHmmssSSS) + * @param endTime 종료시간 (yyyyMMddHHmmssSSS) + * @param timeslice 타임슬라이스 (yyyyMMddHH0000000) + * @return 집계 결과 목록 + */ + @SuppressWarnings("unchecked") + public List selectHourlyMetricsByBzwkCode( + String tableName, String bzwkCode, String startTime, String endTime, String timeslice) { + + String sql = "SELECT " + + "EAISVCNAME AS API_ID, " + + "CLIENTID AS CLIENT_ID, " + + "EAISEVRINSTNCNAME AS HOSTNAME, " + + "ORGID AS ORG_ID, " + + ":timeslice AS TIMESLICE, " + + "SUM(CASE WHEN LOGPRCSSSERNO = '100' THEN 1 ELSE 0 END) AS ATTEMPTED_COUNT, " + + "SUM(CASE WHEN LOGPRCSSSERNO = '400' " + + " AND (RSPNSERRCDNAME IS NULL OR RSPNSERRCDNAME LIKE 'ESB%') " + + " THEN 1 ELSE 0 END) AS COMPLETED_COUNT, " + + "MAX(TRIM(EAISVCSERNO)) AS EAI_SVC_SERNO " + + "FROM " + tableName + " " + + "WHERE EAIBZWKDSTCD = :bzwkCode " + + " AND MSGDPSTYMS BETWEEN :startTime AND :endTime " + + "GROUP BY EAISVCNAME, CLIENTID, EAISEVRINSTNCNAME, ORGID " + + "HAVING SUM(CASE WHEN LOGPRCSSSERNO = '100' THEN 1 ELSE 0 END) > 0 " + + " OR SUM(CASE WHEN LOGPRCSSSERNO = '400' THEN 1 ELSE 0 END) > 0"; + + Query query = entityManager.createNativeQuery(sql); + query.setParameter("timeslice", timeslice); + query.setParameter("bzwkCode", bzwkCode); + query.setParameter("startTime", startTime); + query.setParameter("endTime", endTime); + + List results = query.getResultList(); + + return results.stream() + .map(this::mapToObpGwMetricVO) + .collect(Collectors.toList()); + } + + /** + * Native Query 결과를 ObpGwMetricVO로 변환 + */ + private ObpGwMetricVO mapToObpGwMetricVO(Object[] row) { + ObpGwMetricVO vo = new ObpGwMetricVO(); + vo.setApiId((String) row[0]); + vo.setClientId((String) row[1]); + vo.setHostname((String) row[2]); + vo.setOrgId((String) row[3]); + vo.setTimeslice((String) row[4]); + vo.setAttemptedCount(toLong(row[5])); + vo.setCompletedCount(toLong(row[6])); + vo.setEaiSvcSerno((String) row[7]); + return vo; + } + + /** + * Number 타입을 Long으로 변환 (Oracle은 BigDecimal 반환) + */ + private Long toLong(Object value) { + if (value == null) return 0L; + if (value instanceof BigDecimal) { + return ((BigDecimal) value).longValue(); + } + if (value instanceof Number) { + return ((Number) value).longValue(); + } + return 0L; + } + + /** + * API 이름 조회 (TSEAIHE01) + * + * @param apiIds API ID 목록 (EAISVCNAME) + * @return API ID → API 이름 맵 + */ + public Map selectApiNames(Set apiIds) { + if (apiIds == null || apiIds.isEmpty()) { + return new HashMap<>(); + } + + QEAIMessageEntity he01 = QEAIMessageEntity.eAIMessageEntity; + + List results = getQueryFactory() + .select(he01.eaisvcname, he01.eaisvcdesc) + .from(he01) + .where(he01.eaisvcname.in(apiIds)) + .fetch(); + + Map result = new HashMap<>(); + for (Tuple tuple : results) { + String apiId = tuple.get(he01.eaisvcname); + String apiName = tuple.get(he01.eaisvcdesc); + if (apiId != null && apiName != null) { + result.put(apiId, apiName); + } + } + return result; + } + + /** + * URI 및 HTTP Method 조회 결과 + */ + public static class UriMethodResult { + private final Map uriMap; + private final Map methodMap; + + public UriMethodResult(Map uriMap, Map methodMap) { + this.uriMap = uriMap; + this.methodMap = methodMap; + } + + public Map getUriMap() { + return uriMap; + } + + public Map getMethodMap() { + return methodMap; + } + } + + /** + * URI 및 HTTP Method 조회 (HTTP_ADAPTER_EXTRA_LOG) + * + *

SERVICE_PROCESS_NUMBER = 100 (인바운드 요청) 조건으로 조회

+ * + * @param eaiSvcSernos EAI 서비스 일련번호 목록 (GUID) + * @return UriMethodResult (eaiSvcSerno → URI 맵, eaiSvcSerno → METHOD 맵) + */ + @SuppressWarnings("unchecked") + public UriMethodResult selectUrisAndMethods(Set eaiSvcSernos) { + if (eaiSvcSernos == null || eaiSvcSernos.isEmpty()) { + return new UriMethodResult(new HashMap<>(), new HashMap<>()); + } + + // IN절 파라미터 바인딩을 위한 처리 + String sql = "SELECT GUID AS EAI_SVC_SERNO, URL AS URI, HTTP_METHOD AS METHOD " + + "FROM HTTP_ADAPTER_EXTRA_LOG " + + "WHERE SERVICE_PROCESS_NUMBER = 100 " + + " AND GUID IN (:eaiSvcSernos)"; + + Query query = entityManager.createNativeQuery(sql); + query.setParameter("eaiSvcSernos", new ArrayList<>(eaiSvcSernos)); + + List results = query.getResultList(); + + Map uriMap = new HashMap<>(); + Map methodMap = new HashMap<>(); + + for (Object[] row : results) { + String eaiSvcSerno = (String) row[0]; + String uri = (String) row[1]; + String method = (String) row[2]; + if (eaiSvcSerno != null) { + if (uri != null) { + uriMap.put(eaiSvcSerno, uri); + } + if (method != null) { + methodMap.put(eaiSvcSerno, method); + } + } + } + + return new UriMethodResult(uriMap, methodMap); + } + + /** + * AppId 조회 (PTL_APP_REQUEST) - org_id로 조회 + * + * @param orgIds 기관 ID 목록 + * @return 기관 ID → 앱 ID 맵 + */ + public Map selectAppIdsByOrgIds(Set orgIds) { + if (orgIds == null || orgIds.isEmpty()) { + return new HashMap<>(); + } + + QAppRequest appRequest = QAppRequest.appRequest; + + List results = getQueryFactory() + .select(appRequest.org.id, appRequest.id) + .from(appRequest) + .where(appRequest.org.id.in(orgIds)) + .fetch(); + + Map result = new HashMap<>(); + for (Tuple tuple : results) { + String orgId = tuple.get(appRequest.org.id); + String appId = tuple.get(appRequest.id); + if (orgId != null && appId != null) { + result.put(orgId, appId); + } + } + return result; + } +} diff --git a/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricManService.java b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricManService.java index 66f9419..61fc417 100644 --- a/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricManService.java +++ b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricManService.java @@ -3,10 +3,9 @@ package com.eactive.eai.rms.onl.apim.obp; import com.eactive.apim.portal.obp.entity.ObpGwMetric; import com.eactive.eai.rms.common.base.BaseService; import com.eactive.eai.rms.common.datasource.DataSourceContextHolder; -import com.eactive.eai.rms.common.datasource.DataSourceType; import com.eactive.eai.rms.common.datasource.DataSourceTypeManager; import com.eactive.eai.rms.common.util.CommonUtil; -import com.eactive.eai.rms.data.entity.onl.apim.obp.ObpGwMetricDAO; +import com.eactive.eai.rms.data.entity.onl.apim.obp.ObpGwMetricJpaDAO; import com.eactive.eai.rms.data.entity.onl.apim.obp.ObpGwMetricService; import com.eactive.eai.rms.data.entity.onl.apim.obp.ObpGwMetricVO; import org.slf4j.Logger; @@ -43,11 +42,11 @@ public class ObpGwMetricManService extends BaseService { private static final DateTimeFormatter TIMESTAMP_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"); private static final DateTimeFormatter YYYYMMDD_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd"); - private final ObpGwMetricDAO obpGwMetricDAO; + private final ObpGwMetricJpaDAO obpGwMetricJpaDAO; private final ObpGwMetricService obpGwMetricService; - public ObpGwMetricManService(ObpGwMetricDAO obpGwMetricDAO, ObpGwMetricService obpGwMetricService) { - this.obpGwMetricDAO = obpGwMetricDAO; + public ObpGwMetricManService(ObpGwMetricJpaDAO obpGwMetricJpaDAO, ObpGwMetricService obpGwMetricService) { + this.obpGwMetricJpaDAO = obpGwMetricJpaDAO; this.obpGwMetricService = obpGwMetricService; } @@ -101,10 +100,10 @@ public class ObpGwMetricManService extends BaseService { // 1단계: GW DB에서 EAIBZWKDSTCD 코드 목록 조회 // ═══════════════════════════════════════════════════════════════════ long step1Start = System.currentTimeMillis(); - DataSourceType apigwDataSourceType = DataSourceTypeManager.getDataSourceType(DataSourceTypeManager.APIGW); - DataSourceContextHolder.setDataSourceType(apigwDataSourceType); + DataSourceContextHolder.setDataSourceType( + DataSourceTypeManager.getDataSourceType(DataSourceTypeManager.APIGW)); - List bzwkCodes = obpGwMetricDAO.selectAllBzwkCodes(apigwDataSourceType.getSchema()); + List bzwkCodes = obpGwMetricJpaDAO.selectAllBzwkCodes(); log.info("[1단계] EAIBZWKDSTCD 코드 조회 완료: count={}, elapsed={}ms", bzwkCodes.size(), System.currentTimeMillis() - step1Start); @@ -118,15 +117,8 @@ public class ObpGwMetricManService extends BaseService { for (String bzwkCode : bzwkCodes) { long codeStart = System.currentTimeMillis(); - HashMap paramMap = new HashMap<>(); - paramMap.put("schemaId", apigwDataSourceType.getSchema()); - paramMap.put("tableName", tableName); - paramMap.put("bzwkCode", bzwkCode); - paramMap.put("startTime", startTime); - paramMap.put("endTime", endTime); - paramMap.put("timeslice", timeslice); - - List partialResult = obpGwMetricDAO.selectHourlyMetricsByBzwkCode(paramMap); + List partialResult = obpGwMetricJpaDAO.selectHourlyMetricsByBzwkCode( + tableName, bzwkCode, startTime, endTime, timeslice); for (ObpGwMetricVO vo : partialResult) { String key = vo.getAggregationKey(); @@ -164,8 +156,8 @@ public class ObpGwMetricManService extends BaseService { .filter(Objects::nonNull) .collect(Collectors.toSet()); - Map apiNameMap = obpGwMetricDAO.selectApiNames(apigwDataSourceType.getSchema(), apiIds); - ObpGwMetricDAO.UriMethodResult uriMethodResult = obpGwMetricDAO.selectUrisAndMethods(apigwDataSourceType.getSchema(), eaiSvcSernos); + Map apiNameMap = obpGwMetricJpaDAO.selectApiNames(apiIds); + ObpGwMetricJpaDAO.UriMethodResult uriMethodResult = obpGwMetricJpaDAO.selectUrisAndMethods(eaiSvcSernos); Map uriMap = uriMethodResult.getUriMap(); Map methodMap = uriMethodResult.getMethodMap(); log.info("[3단계] 부가정보 조회 완료: apiCount={}, eaiSvcSernoCount={}, elapsed={}ms", @@ -182,7 +174,7 @@ public class ObpGwMetricManService extends BaseService { .map(ObpGwMetricVO::getOrgId) .filter(Objects::nonNull) .collect(Collectors.toSet()); - Map appIdMap = obpGwMetricDAO.selectAppIdsByOrgIds(orgIds); + Map appIdMap = obpGwMetricJpaDAO.selectAppIdsByOrgIds(orgIds); int insertCount = 0; int updateCount = 0; diff --git a/src/main/resources/com/eactive/eai/apim/obp/ObpGwMetric-oracle.xml b/src/main/resources/com/eactive/eai/apim/obp/ObpGwMetric-oracle.xml deleted file mode 100644 index 6da8ddb..0000000 --- a/src/main/resources/com/eactive/eai/apim/obp/ObpGwMetric-oracle.xml +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - - - SELECT EAIBZWKDSTCD - FROM $schemaId$.TSEAICM01 - ORDER BY EAIBZWKDSTCD - - - - - - - - - - - - - - - - - SELECT - EAISVCNAME AS API_ID, - CLIENTID AS CLIENT_ID, - EAISEVRINSTNCNAME AS HOSTNAME, - ORGID AS ORG_ID, - '$timeslice$' AS TIMESLICE, - SUM(CASE WHEN LOGPRCSSSERNO = '100' THEN 1 ELSE 0 END) AS ATTEMPTED_COUNT, - SUM(CASE WHEN LOGPRCSSSERNO = '400' - AND (RSPNSERRCDNAME IS NULL OR RSPNSERRCDNAME LIKE 'ESB%') - THEN 1 ELSE 0 END) AS COMPLETED_COUNT, - MAX(TRIM(EAISVCSERNO)) AS EAI_SVC_SERNO - FROM $schemaId$.$tableName$ - WHERE EAIBZWKDSTCD = #bzwkCode# - AND MSGDPSTYMS BETWEEN #startTime# AND #endTime# - GROUP BY EAISVCNAME, CLIENTID, EAISEVRINSTNCNAME, ORGID - HAVING SUM(CASE WHEN LOGPRCSSSERNO = '100' THEN 1 ELSE 0 END) > 0 - OR SUM(CASE WHEN LOGPRCSSSERNO = '400' THEN 1 ELSE 0 END) > 0 - - - - - SELECT - EAISVCNAME AS API_ID, - EAISVCDESC AS API_NAME - FROM $schemaId$.TSEAIHE01 - WHERE EAISVCNAME IN - - #apiIds[]# - - - - - - SELECT - GUID AS EAI_SVC_SERNO, - URL AS URI, - HTTP_METHOD AS METHOD - FROM $schemaId$.HTTP_ADAPTER_EXTRA_LOG - WHERE SERVICE_PROCESS_NUMBER = 100 - AND GUID IN - - #eaiSvcSernos[]# - - - - - - SELECT - ORG_ID, - ID AS APP_ID - FROM PTL_APP_REQUEST - WHERE ORG_ID IN - - #orgIds[]# - - - - From d1e8c29791b526506001ed45b4e0cb13dc123d8e Mon Sep 17 00:00:00 2001 From: Rinjae Date: Fri, 19 Dec 2025 13:30:05 +0900 Subject: [PATCH 2/2] =?UTF-8?q?NPE=20=EB=8C=80=EC=9D=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../eai/rms/data/entity/onl/apim/obp/ObpGwMetricService.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricService.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricService.java index e2496fb..da1d6ef 100644 --- a/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricService.java +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricService.java @@ -23,11 +23,10 @@ public class ObpGwMetricService { } /** - * UPSERT 판단을 위한 기존 데이터 조회 + * UPSERT 판단을 위한 기존 데이터 조회 (NULL 안전) */ public Optional findByUniqueKey(LocalDateTime timeslice, String apiId, String clientId, String hostname, String orgId) { - return repository.findByTimesliceAndApiIdAndClientIdAndHostnameAndOrgId( - timeslice, apiId, clientId, hostname, orgId); + return repository.findByUniqueKeyNullSafe(timeslice, apiId, clientId, hostname, orgId); } /**