🗃️ Event Table Name Change

- SQL Injection Prevent
This commit is contained in:
hyojin kim 2025-12-31 10:37:20 +09:00
부모 1d2a3c53c8
커밋 6aba0f55b0
7개의 변경된 파일46개의 추가작업 그리고 14개의 파일을 삭제

파일 보기

@ -43,7 +43,7 @@ public class ComplianceImportRangeJobConfig extends BaseMultiStepJobConfig<Compl
private final BatchDateService batchDateService; private final BatchDateService batchDateService;
protected String getApiKey() {return "COMPLIANCE_IMPORT_API";} protected String getApiKey() {return "COMPLIANCE_IMPORT_API";}
protected String getBatchUpdateSql() { protected String getBatchUpdateSql() {
return "UPDATE SNP_DATA.BATCH_LAST_EXECUTION SET LAST_SUCCESS_DATE = NOW() WHERE API_KEY = '" + getApiKey() + "'";} return String.format("UPDATE SNP_DATA.BATCH_LAST_EXECUTION SET LAST_SUCCESS_DATE = NOW(), UPDATED_AT = NOW() WHERE API_KEY = '%s'", getApiKey());}
@Override @Override
protected int getChunkSize() { protected int getChunkSize() {

파일 보기

@ -1,6 +1,6 @@
package com.snp.batch.jobs.event.batch.config; package com.snp.batch.jobs.event.batch.config;
import com.snp.batch.common.batch.config.BaseJobConfig; import com.snp.batch.common.batch.config.BaseMultiStepJobConfig;
import com.snp.batch.jobs.event.batch.dto.EventDetailDto; import com.snp.batch.jobs.event.batch.dto.EventDetailDto;
import com.snp.batch.jobs.event.batch.entity.EventDetailEntity; import com.snp.batch.jobs.event.batch.entity.EventDetailEntity;
import com.snp.batch.jobs.event.batch.processor.EventDataProcessor; import com.snp.batch.jobs.event.batch.processor.EventDataProcessor;
@ -10,10 +10,14 @@ import com.snp.batch.service.BatchDateService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.Job; import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step; import org.springframework.batch.core.Step;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.repository.JobRepository; import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.item.ItemProcessor; import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader; import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter; import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -23,7 +27,7 @@ import org.springframework.web.reactive.function.client.WebClient;
@Slf4j @Slf4j
@Configuration @Configuration
public class EventImportJobConfig extends BaseJobConfig<EventDetailDto, EventDetailEntity> { public class EventImportJobConfig extends BaseMultiStepJobConfig<EventDetailDto, EventDetailEntity> {
private final JdbcTemplate jdbcTemplate; private final JdbcTemplate jdbcTemplate;
private final WebClient maritimeApiWebClient; private final WebClient maritimeApiWebClient;
@ -32,6 +36,10 @@ public class EventImportJobConfig extends BaseJobConfig<EventDetailDto, EventDet
private final EventDataWriter eventDataWriter; private final EventDataWriter eventDataWriter;
private final BatchDateService batchDateService; private final BatchDateService batchDateService;
protected String getApiKey() {return "EVENT_IMPORT_API";}
protected String getBatchUpdateSql() {
return String.format("UPDATE SNP_DATA.BATCH_LAST_EXECUTION SET LAST_SUCCESS_DATE = NOW(), UPDATED_AT = NOW() WHERE API_KEY = '%s'", getApiKey());}
@Override @Override
protected int getChunkSize() { protected int getChunkSize() {
return 10; // API에서 5000개씩 가져오므로 chunk도 5000으로 설정 return 10; // API에서 5000개씩 가져오므로 chunk도 5000으로 설정
@ -62,6 +70,14 @@ public class EventImportJobConfig extends BaseJobConfig<EventDetailDto, EventDet
return "eventImportStep"; return "eventImportStep";
} }
@Override
protected Job createJobFlow(JobBuilder jobBuilder) {
return jobBuilder
.start(eventImportStep())
.next(eventLastExecutionUpdateStep())
.build();
}
@Override @Override
protected ItemReader<EventDetailDto> createReader() { protected ItemReader<EventDetailDto> createReader() {
return new EventDataReader(maritimeApiWebClient, jdbcTemplate, batchDateService); return new EventDataReader(maritimeApiWebClient, jdbcTemplate, batchDateService);
@ -85,4 +101,25 @@ public class EventImportJobConfig extends BaseJobConfig<EventDetailDto, EventDet
return step(); return step();
} }
/**
* 2단계: 모든 스텝 성공 배치 실행 로그(날짜) 업데이트
*/
@Bean
public Tasklet eventLastExecutionUpdateTasklet() {
return (contribution, chunkContext) -> {
log.info(">>>>> 모든 스텝 성공: BATCH_LAST_EXECUTION 업데이트 시작");
jdbcTemplate.execute(getBatchUpdateSql());
log.info(">>>>> BATCH_LAST_EXECUTION 업데이트 완료");
return RepeatStatus.FINISHED;
};
}
@Bean(name = "EventLastExecutionUpdateStep")
public Step eventLastExecutionUpdateStep() {
return new StepBuilder("EventLastExecutionUpdateStep", jobRepository)
.tasklet(eventLastExecutionUpdateTasklet(), transactionManager)
.build();
}
} }

파일 보기

@ -44,7 +44,7 @@ public class EventDataReader extends BaseApiReader<EventDetailDto> {
} }
protected String getApiKey() { protected String getApiKey() {
return "EVENT_IMPORT_JOB"; return "EVENT_IMPORT_API";
} }
// 배치 처리 상태 // 배치 처리 상태
@ -66,7 +66,7 @@ public class EventDataReader extends BaseApiReader<EventDetailDto> {
log.info("Event API 호출"); log.info("Event API 호출");
EventResponse response = callEventApiWithBatch(); EventResponse response = callEventApiWithBatch();
// 2-1. Event List 에서 EventID List 추출 // 2-1. Event List 에서 EventID List 추출
// TODO: 2-2. Event List 에서 Map<EventId,Map<StartDate,EndDate>> 추출 // 2-2. Event List 에서 Map<EventId,Map<StartDate,EndDate>> 추출
eventIds = extractEventIdList(response); eventIds = extractEventIdList(response);
log.info("EvnetId List 추출 완료 : {} 개", eventIds.size()); log.info("EvnetId List 추출 완료 : {} 개", eventIds.size());
@ -168,11 +168,6 @@ public class EventDataReader extends BaseApiReader<EventDetailDto> {
int totalBatches = (int) Math.ceil((double) eventIds.size() / batchSize); int totalBatches = (int) Math.ceil((double) eventIds.size() / batchSize);
try { try {
if (data == null) { if (data == null) {
// 3. 배치 성공 상태 업데이트 (트랜잭션 커밋 직전에 실행)
LocalDate successDate = LocalDate.now(); // 현재 배치 실행 시점의 날짜 (Reader의 toDay와 동일한 )
batchDateService.updateLastSuccessDate(getApiKey(), successDate);
log.info("batch_last_execution update 완료 : {}", getApiKey());
log.info("[{}] 전체 {} 개 배치 처리 완료", getReaderName(), totalBatches); log.info("[{}] 전체 {} 개 배치 처리 완료", getReaderName(), totalBatches);
log.info("[{}] 총 {} 개의 Event ID에 대한 API 호출 종료", log.info("[{}] 총 {} 개의 Event ID에 대한 API 호출 종료",
getReaderName(), eventIds.size()); getReaderName(), eventIds.size());

파일 보기

@ -47,7 +47,7 @@ public class EventRepositoryImpl extends BaseJdbcRepository<EventDetailEntity, L
@Override @Override
protected String getUpdateSql() { protected String getUpdateSql() {
return """ return """
INSERT INTO snp_data.event_detail ( INSERT INTO snp_data.event (
Event_ID, Incident_ID, IHSLRorIMOShipNo, Vessel_Name, Vessel_Type, Event_ID, Incident_ID, IHSLRorIMOShipNo, Vessel_Name, Vessel_Type,
Event_Type, Significance, Headline, Location_Name, Event_Type, Significance, Headline, Location_Name,
Published_Date, Event_Start_Date, Event_End_Date, batch_flag Published_Date, Event_Start_Date, Event_End_Date, batch_flag

파일 보기

@ -3,7 +3,7 @@ package com.snp.batch.jobs.event.batch.repository;
public class EventSql { public class EventSql {
public static String getEventDetailUpdateSql(){ public static String getEventDetailUpdateSql(){
return """ return """
INSERT INTO new_snp.event_detail ( INSERT INTO new_snp.event (
event_id, incident_id, ihslrorimoshipno, published_date, event_start_date, event_end_date, event_id, incident_id, ihslrorimoshipno, published_date, event_start_date, event_end_date,
attempted_boarding, cargo_loading_status_code, casualty_action, attempted_boarding, cargo_loading_status_code, casualty_action,
casualty_zone, casualty_zone_code, component2, country_code, casualty_zone, casualty_zone_code, component2, country_code,

파일 보기

@ -13,7 +13,6 @@ import java.util.List;
@Component @Component
public class EventDataWriter extends BaseWriter<EventDetailEntity> { public class EventDataWriter extends BaseWriter<EventDetailEntity> {
private final EventRepository eventRepository; private final EventRepository eventRepository;
protected String getApiKey() {return "EVENT_IMPORT_JOB";}
public EventDataWriter(EventRepository eventRepository) { public EventDataWriter(EventRepository eventRepository) {
super("EventRepository"); super("EventRepository");
this.eventRepository = eventRepository; this.eventRepository = eventRepository;

파일 보기

@ -38,7 +38,8 @@ public class RiskImportRangeJobConfig extends BaseMultiStepJobConfig<RiskDto, Ri
protected String getApiKey() {return "RISK_IMPORT_API";} protected String getApiKey() {return "RISK_IMPORT_API";}
protected String getBatchUpdateSql() { protected String getBatchUpdateSql() {
return "UPDATE SNP_DATA.BATCH_LAST_EXECUTION SET LAST_SUCCESS_DATE = NOW() WHERE API_KEY = '" + getApiKey() + "'";} return String.format("UPDATE SNP_DATA.BATCH_LAST_EXECUTION SET LAST_SUCCESS_DATE = NOW(), UPDATED_AT = NOW() WHERE API_KEY = '%s'", getApiKey());}
@Override @Override
protected int getChunkSize() { protected int getChunkSize() {