diff --git a/src/main/java/com/snp/batch/jobs/common/batch/config/FlagCodeImportJobConfig.java b/src/main/java/com/snp/batch/jobs/common/batch/config/FlagCodeImportJobConfig.java new file mode 100644 index 0000000..3938b45 --- /dev/null +++ b/src/main/java/com/snp/batch/jobs/common/batch/config/FlagCodeImportJobConfig.java @@ -0,0 +1,102 @@ +package com.snp.batch.jobs.common.batch.config; + +import com.snp.batch.common.batch.config.BaseJobConfig; +import com.snp.batch.jobs.common.batch.dto.FlagCodeDto; +import com.snp.batch.jobs.common.batch.entity.FlagCodeEntity; +import com.snp.batch.jobs.common.batch.processor.FlagCodeDataProcessor; +import com.snp.batch.jobs.common.batch.reader.FlagCodeDataReader; +import com.snp.batch.jobs.common.batch.repository.FlagCodeRepository; +import com.snp.batch.jobs.common.batch.writer.FlagCodeDataWriter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.Step; +import org.springframework.batch.core.repository.JobRepository; +import org.springframework.batch.item.ItemProcessor; +import org.springframework.batch.item.ItemReader; +import org.springframework.batch.item.ItemWriter; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.web.reactive.function.client.WebClient; + +/** + * Ship Data Import Job 설정 + * BaseJobConfig를 상속하여 구현 + * + * Maritime API에서 선박 데이터를 받아 PostgreSQL에 저장하는 배치 작업: + * - Maritime API에서 170,000+ 선박 IMO 번호 조회 + * - 중복 체크 및 업데이트 로직 + * - PostgreSQL에 저장 + */ +@Slf4j +@Configuration +public class FlagCodeImportJobConfig extends BaseJobConfig { + + private final FlagCodeRepository flagCodeRepository; + private final WebClient maritimeApiWebClient; + + @Value("${app.batch.chunk-size:1000}") + private int chunkSize; + + /** + * 생성자 주입 + * maritimeApiWebClient: MaritimeApiWebClientConfig에서 등록한 Bean 주입 + */ + public FlagCodeImportJobConfig( + JobRepository jobRepository, + PlatformTransactionManager transactionManager, + FlagCodeRepository flagCodeRepository, + @Qualifier("maritimeApiWebClient") WebClient maritimeApiWebClient) { + super(jobRepository, transactionManager); + this.flagCodeRepository = flagCodeRepository; + this.maritimeApiWebClient = maritimeApiWebClient; + } + + @Override + protected String getJobName() { + return "FlagCodeImportJob"; + } + + @Override + protected String getStepName() { + return "FlagCodeImportStep"; + } + + @Override + protected ItemReader createReader() { + return new FlagCodeDataReader(maritimeApiWebClient); + } + + @Override + protected ItemProcessor createProcessor() { + return new FlagCodeDataProcessor(flagCodeRepository); + } + + @Override + protected ItemWriter createWriter() { + return new FlagCodeDataWriter(flagCodeRepository); + } + + @Override + protected int getChunkSize() { + return chunkSize; + } + + /** + * Job Bean 등록 + */ + @Bean(name = "FlagCodeImportJob") + public Job flagCodeImportJob() { + return job(); + } + + /** + * Step Bean 등록 + */ + @Bean(name = "FlagCodeImportStep") + public Step flagCodeImportStep() { + return step(); + } +} diff --git a/src/main/java/com/snp/batch/jobs/common/batch/config/Stat5CodeImportJobConfig.java b/src/main/java/com/snp/batch/jobs/common/batch/config/Stat5CodeImportJobConfig.java new file mode 100644 index 0000000..c1fcccf --- /dev/null +++ b/src/main/java/com/snp/batch/jobs/common/batch/config/Stat5CodeImportJobConfig.java @@ -0,0 +1,68 @@ +package com.snp.batch.jobs.common.batch.config; + +import com.snp.batch.common.batch.config.BaseJobConfig; +import com.snp.batch.jobs.common.batch.dto.Stat5CodeDto; +import com.snp.batch.jobs.common.batch.entity.Stat5CodeEntity; +import com.snp.batch.jobs.common.batch.processor.Stat5CodeDataProcessor; +import com.snp.batch.jobs.common.batch.reader.Stat5CodeDataReader; +import com.snp.batch.jobs.common.batch.repository.Stat5CodeRepository; +import com.snp.batch.jobs.common.batch.writer.Stat5CodeDataWriter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.Step; +import org.springframework.batch.core.repository.JobRepository; +import org.springframework.batch.item.ItemProcessor; +import org.springframework.batch.item.ItemReader; +import org.springframework.batch.item.ItemWriter; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.web.reactive.function.client.WebClient; + +@Slf4j +@Configuration +public class Stat5CodeImportJobConfig extends BaseJobConfig { + + private final Stat5CodeRepository stat5CodeRepository; + private final WebClient maritimeAisApiWebClient; + public Stat5CodeImportJobConfig( + JobRepository jobRepository, + PlatformTransactionManager transactionManager, + Stat5CodeRepository stat5CodeRepository, + @Qualifier("maritimeAisApiWebClient") WebClient maritimeAisApiWebClient) { + super(jobRepository, transactionManager); + this.stat5CodeRepository = stat5CodeRepository; + this.maritimeAisApiWebClient = maritimeAisApiWebClient; + } + + @Override + protected String getJobName() { return "Stat5CodeImportJob"; } + + @Override + protected String getStepName() { + return "Stat5CodeImportStep"; + } + + @Override + protected ItemReader createReader() { return new Stat5CodeDataReader(maritimeAisApiWebClient); } + + @Override + protected ItemProcessor createProcessor() { return new Stat5CodeDataProcessor(stat5CodeRepository); } + + @Override + protected ItemWriter createWriter() { return new Stat5CodeDataWriter(stat5CodeRepository); } + + @Bean(name = "Stat5CodeImportJob") + public Job stat5CodeImportJob() { + return job(); + } + + /** + * Step Bean 등록 + */ + @Bean(name = "Stat5CodeImportStep") + public Step stat5CodeImportStep() { + return step(); + } +} diff --git a/src/main/java/com/snp/batch/jobs/common/batch/dto/FlagCodeApiResponse.java b/src/main/java/com/snp/batch/jobs/common/batch/dto/FlagCodeApiResponse.java new file mode 100644 index 0000000..3e3a51f --- /dev/null +++ b/src/main/java/com/snp/batch/jobs/common/batch/dto/FlagCodeApiResponse.java @@ -0,0 +1,26 @@ +package com.snp.batch.jobs.common.batch.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class FlagCodeApiResponse { + + @JsonProperty("associatedName") + private String associatedName; + + @JsonProperty("associatedCount") + private Integer associatedCount; + + @JsonProperty("APSAssociatedFlagISODetails") + private List associatedFlagISODetails; + +} diff --git a/src/main/java/com/snp/batch/jobs/common/batch/dto/FlagCodeDto.java b/src/main/java/com/snp/batch/jobs/common/batch/dto/FlagCodeDto.java new file mode 100644 index 0000000..c763da7 --- /dev/null +++ b/src/main/java/com/snp/batch/jobs/common/batch/dto/FlagCodeDto.java @@ -0,0 +1,40 @@ +package com.snp.batch.jobs.common.batch.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class FlagCodeDto { + + @JsonProperty("DataSetVersion") + private DataSetVersion dataSetVersion; + + @JsonProperty("Code") + private String code; + + @JsonProperty("Decode") + private String decode; + + @JsonProperty("ISO2") + private String iso2; + + @JsonProperty("ISO3") + private String iso3; + + @Data + @NoArgsConstructor + @AllArgsConstructor + @JsonIgnoreProperties(ignoreUnknown = true) + public static class DataSetVersion { + @JsonProperty("DataSetVersion") + private String version; + } +} diff --git a/src/main/java/com/snp/batch/jobs/common/batch/dto/Stat5CodeApiResponse.java b/src/main/java/com/snp/batch/jobs/common/batch/dto/Stat5CodeApiResponse.java new file mode 100644 index 0000000..f8f60e1 --- /dev/null +++ b/src/main/java/com/snp/batch/jobs/common/batch/dto/Stat5CodeApiResponse.java @@ -0,0 +1,18 @@ +package com.snp.batch.jobs.common.batch.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class Stat5CodeApiResponse { + @JsonProperty("StatcodeArr") + private List statcodeArr; +} diff --git a/src/main/java/com/snp/batch/jobs/common/batch/dto/Stat5CodeDto.java b/src/main/java/com/snp/batch/jobs/common/batch/dto/Stat5CodeDto.java new file mode 100644 index 0000000..c50fc10 --- /dev/null +++ b/src/main/java/com/snp/batch/jobs/common/batch/dto/Stat5CodeDto.java @@ -0,0 +1,40 @@ +package com.snp.batch.jobs.common.batch.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class Stat5CodeDto { + @JsonProperty("Level1") + private String level1; + @JsonProperty("Level1Decode") + private String Level1Decode; + @JsonProperty("Level2") + private String Level2; + @JsonProperty("Level2Decode") + private String Level2Decode; + @JsonProperty("Level3") + private String Level3; + @JsonProperty("Level3Decode") + private String Level3Decode; + @JsonProperty("Level4") + private String Level4; + @JsonProperty("Level4Decode") + private String Level4Decode; + @JsonProperty("Level5") + private String Level5; + @JsonProperty("Level5Decode") + private String Level5Decode; + @JsonProperty("Description") + private String Description; + @JsonProperty("Release") + private Integer Release; +} diff --git a/src/main/java/com/snp/batch/jobs/common/batch/entity/FlagCodeEntity.java b/src/main/java/com/snp/batch/jobs/common/batch/entity/FlagCodeEntity.java new file mode 100644 index 0000000..b35435c --- /dev/null +++ b/src/main/java/com/snp/batch/jobs/common/batch/entity/FlagCodeEntity.java @@ -0,0 +1,32 @@ +package com.snp.batch.jobs.common.batch.entity; + +import com.snp.batch.common.batch.entity.BaseEntity; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +/** + * 선박 엔티티 - JDBC 전용 + * Maritime API 데이터 저장 + * + * 테이블: ship_data + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class FlagCodeEntity extends BaseEntity { + + private String dataSetVersion; + + private String code; + + private String decode; + + private String iso2; + + private String iso3; +} diff --git a/src/main/java/com/snp/batch/jobs/common/batch/entity/Stat5CodeEntity.java b/src/main/java/com/snp/batch/jobs/common/batch/entity/Stat5CodeEntity.java new file mode 100644 index 0000000..a633f75 --- /dev/null +++ b/src/main/java/com/snp/batch/jobs/common/batch/entity/Stat5CodeEntity.java @@ -0,0 +1,41 @@ +package com.snp.batch.jobs.common.batch.entity; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.snp.batch.common.batch.entity.BaseEntity; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class Stat5CodeEntity extends BaseEntity { + + private String level1; + + private String Level1Decode; + + private String Level2; + + private String Level2Decode; + + private String Level3; + + private String Level3Decode; + + private String Level4; + + private String Level4Decode; + + private String Level5; + + private String Level5Decode; + + private String Description; + + private String Release; +} diff --git a/src/main/java/com/snp/batch/jobs/common/batch/processor/FlagCodeDataProcessor.java b/src/main/java/com/snp/batch/jobs/common/batch/processor/FlagCodeDataProcessor.java new file mode 100644 index 0000000..0b1cb9b --- /dev/null +++ b/src/main/java/com/snp/batch/jobs/common/batch/processor/FlagCodeDataProcessor.java @@ -0,0 +1,38 @@ +package com.snp.batch.jobs.common.batch.processor; + +import com.snp.batch.common.batch.processor.BaseProcessor; +import com.snp.batch.jobs.common.batch.dto.FlagCodeDto; +import com.snp.batch.jobs.common.batch.entity.FlagCodeEntity; +import com.snp.batch.jobs.common.batch.repository.FlagCodeRepository; +import lombok.extern.slf4j.Slf4j; + +/** + * ShipDto를 ShipEntity로 변환하는 Processor + * BaseProcessor를 상속하여 공통 변환 패턴 적용 + * 중복 체크 및 업데이트 로직 포함 + */ +@Slf4j +public class FlagCodeDataProcessor extends BaseProcessor { + + private final FlagCodeRepository commonCodeRepository; + + public FlagCodeDataProcessor(FlagCodeRepository commonCodeRepository) { + this.commonCodeRepository = commonCodeRepository; + } + + @Override + protected FlagCodeEntity processItem(FlagCodeDto dto) throws Exception { + + FlagCodeEntity entity = FlagCodeEntity.builder() + .dataSetVersion(dto.getDataSetVersion().getVersion()) + .code(dto.getCode()) + .decode(dto.getDecode()) + .iso2(dto.getIso2()) + .iso3(dto.getIso3()) + .build(); + + log.debug("국가코드 데이터 처리 완료: FlagCode={}", dto.getCode()); + + return entity; + } +} diff --git a/src/main/java/com/snp/batch/jobs/common/batch/processor/Stat5CodeDataProcessor.java b/src/main/java/com/snp/batch/jobs/common/batch/processor/Stat5CodeDataProcessor.java new file mode 100644 index 0000000..46a56c0 --- /dev/null +++ b/src/main/java/com/snp/batch/jobs/common/batch/processor/Stat5CodeDataProcessor.java @@ -0,0 +1,39 @@ +package com.snp.batch.jobs.common.batch.processor; + +import com.snp.batch.common.batch.processor.BaseProcessor; +import com.snp.batch.jobs.common.batch.dto.Stat5CodeDto; +import com.snp.batch.jobs.common.batch.entity.Stat5CodeEntity; +import com.snp.batch.jobs.common.batch.repository.Stat5CodeRepository; +import lombok.extern.slf4j.Slf4j; + +import java.sql.PreparedStatement; + +@Slf4j +public class Stat5CodeDataProcessor extends BaseProcessor { + private final Stat5CodeRepository stat5CodeRepository; + + public Stat5CodeDataProcessor(Stat5CodeRepository stat5CodeRepository) { + this.stat5CodeRepository = stat5CodeRepository; + } + + @Override + protected Stat5CodeEntity processItem(Stat5CodeDto dto) throws Exception { + Stat5CodeEntity entity = Stat5CodeEntity.builder() + .level1(dto.getLevel1()) + .Level1Decode(dto.getLevel1Decode()) + .Level2(dto.getLevel2()) + .Level2Decode(dto.getLevel2Decode()) + .Level3(dto.getLevel3()) + .Level3Decode(dto.getLevel3Decode()) + .Level4(dto.getLevel4()) + .Level4Decode(dto.getLevel4Decode()) + .Level5(dto.getLevel5()) + .Level5Decode(dto.getLevel5Decode()) + .Description(dto.getDescription()) + .Release(Integer.toString(dto.getRelease())) + .build(); + + log.debug("Stat5Code 데이터 처리 완료: Stat5Code={}", dto.getLevel5()); + return entity; + } +} diff --git a/src/main/java/com/snp/batch/jobs/common/batch/reader/FlagCodeDataReader.java b/src/main/java/com/snp/batch/jobs/common/batch/reader/FlagCodeDataReader.java new file mode 100644 index 0000000..4684b4b --- /dev/null +++ b/src/main/java/com/snp/batch/jobs/common/batch/reader/FlagCodeDataReader.java @@ -0,0 +1,60 @@ +package com.snp.batch.jobs.common.batch.reader; + +import com.snp.batch.common.batch.reader.BaseApiReader; +import com.snp.batch.jobs.common.batch.dto.FlagCodeApiResponse; +import com.snp.batch.jobs.common.batch.dto.FlagCodeDto; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.reactive.function.client.WebClient; + +import java.util.ArrayList; +import java.util.List; + +/** + * Maritime API에서 선박 데이터를 읽어오는 ItemReader + * BaseApiReader v2.0을 상속하여 공통 API 호출 패턴 적용 + */ +@Slf4j +public class FlagCodeDataReader extends BaseApiReader { + + public FlagCodeDataReader(WebClient webClient) { + super(webClient); // BaseApiReader에 WebClient 전달 + } + + // ======================================== + // 필수 구현 메서드 + // ======================================== + + @Override + protected String getReaderName() { + return "FlagCodeDataReader"; + } + + @Override + protected List fetchDataFromApi() { + try { + log.info("GetAssociatedFlagISOByName API 호출 시작"); + + FlagCodeApiResponse response = webClient + .get() + .uri(uriBuilder -> uriBuilder + .path("/MaritimeWCF/APSShipService.svc/RESTFul/GetAssociatedFlagISOByName") + .build()) + .retrieve() + .bodyToMono(FlagCodeApiResponse.class) + .block(); + + if (response != null && response.getAssociatedFlagISODetails() != null) { + log.info("API 응답 성공: 총 {} 건의 국가코드 데이터 수신", response.getAssociatedCount()); + return response.getAssociatedFlagISODetails(); + } else { + log.warn("API 응답이 null이거나 국가코드 데이터가 없습니다"); + return new ArrayList<>(); + } + + } catch (Exception e) { + log.error("GetAssociatedFlagISOByName API 호출 실패", e); + log.error("에러 메시지: {}", e.getMessage()); + return new ArrayList<>(); + } + } +} diff --git a/src/main/java/com/snp/batch/jobs/common/batch/reader/Stat5CodeDataReader.java b/src/main/java/com/snp/batch/jobs/common/batch/reader/Stat5CodeDataReader.java new file mode 100644 index 0000000..238519d --- /dev/null +++ b/src/main/java/com/snp/batch/jobs/common/batch/reader/Stat5CodeDataReader.java @@ -0,0 +1,49 @@ +package com.snp.batch.jobs.common.batch.reader; + +import com.snp.batch.common.batch.reader.BaseApiReader; +import com.snp.batch.jobs.common.batch.dto.Stat5CodeApiResponse; +import com.snp.batch.jobs.common.batch.dto.Stat5CodeDto; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.reactive.function.client.WebClient; + +import java.util.ArrayList; +import java.util.List; + +@Slf4j +public class Stat5CodeDataReader extends BaseApiReader { + public Stat5CodeDataReader(WebClient webClient) { + super(webClient); // BaseApiReader에 WebClient 전달 + } + @Override + protected String getReaderName() { + return "Stat5CodeDataReader"; + } + @Override + protected List fetchDataFromApi() { + try { + log.info("GetStatcodes API 호출 시작"); + + Stat5CodeApiResponse response = webClient + .get() + .uri(uriBuilder -> uriBuilder + .path("/AisSvc.svc/AIS/GetStatcodes") + .build()) + .retrieve() + .bodyToMono(Stat5CodeApiResponse.class) + .block(); + + if (response != null && response.getStatcodeArr() != null) { + log.info("API 응답 성공: 총 {} 건의 Stat5Code 데이터 수신", response.getStatcodeArr().size()); + return response.getStatcodeArr(); + } else { + log.warn("API 응답이 null이거나 Stat5Code 데이터가 없습니다"); + return new ArrayList<>(); + } + + } catch (Exception e) { + log.error("GetAssociatedFlagISOByName API 호출 실패", e); + log.error("에러 메시지: {}", e.getMessage()); + return new ArrayList<>(); + } + } +} diff --git a/src/main/java/com/snp/batch/jobs/common/batch/repository/FlagCodeRepository.java b/src/main/java/com/snp/batch/jobs/common/batch/repository/FlagCodeRepository.java new file mode 100644 index 0000000..bfdfbdf --- /dev/null +++ b/src/main/java/com/snp/batch/jobs/common/batch/repository/FlagCodeRepository.java @@ -0,0 +1,16 @@ +package com.snp.batch.jobs.common.batch.repository; + +import com.snp.batch.jobs.common.batch.entity.FlagCodeEntity; + +import java.util.List; + +/** + * ShipEntity Repository 인터페이스 + * 구현체: ShipRepositoryImpl (JdbcTemplate 기반) + */ +public interface FlagCodeRepository { + + void saveAllFlagCode(List items); + + +} diff --git a/src/main/java/com/snp/batch/jobs/common/batch/repository/FlagCodeRepositoryImpl.java b/src/main/java/com/snp/batch/jobs/common/batch/repository/FlagCodeRepositoryImpl.java new file mode 100644 index 0000000..8d46128 --- /dev/null +++ b/src/main/java/com/snp/batch/jobs/common/batch/repository/FlagCodeRepositoryImpl.java @@ -0,0 +1,98 @@ +package com.snp.batch.jobs.common.batch.repository; + +import com.snp.batch.common.batch.repository.BaseJdbcRepository; +import com.snp.batch.jobs.common.batch.entity.FlagCodeEntity; +import lombok.extern.slf4j.Slf4j; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Repository; + +import java.sql.PreparedStatement; +import java.util.List; + +/** + * ShipEntity Repository (JdbcTemplate 기반) + */ +@Slf4j +@Repository("FlagCodeRepository") +public class FlagCodeRepositoryImpl extends BaseJdbcRepository implements FlagCodeRepository { + + public FlagCodeRepositoryImpl(JdbcTemplate jdbcTemplate) { + super(jdbcTemplate); + } + + @Override + protected String getEntityName() { + return "FlagCodeEntity"; + } + + @Override + protected String getTableName() { + return "snp_data.flagcode"; + } + + + @Override + protected String getInsertSql() { + return null; + } + + @Override + protected String getUpdateSql() { + return """ + INSERT INTO snp_data.flagcode ( + datasetversion, code, decode, iso2, iso3 + ) VALUES (?, ?, ?, ?, ?) + ON CONFLICT (code) + DO UPDATE SET + datasetversion = EXCLUDED.datasetversion, + decode = EXCLUDED.decode, + iso2 = EXCLUDED.iso2, + iso3 = EXCLUDED.iso3, + batch_flag = 'N' + """; + } + + @Override + protected void setInsertParameters(PreparedStatement ps, FlagCodeEntity entity) throws Exception { + + } + + @Override + protected void setUpdateParameters(PreparedStatement ps, FlagCodeEntity entity) throws Exception { + int idx = 1; + ps.setString(idx++, entity.getDataSetVersion()); + ps.setString(idx++, entity.getCode()); + ps.setString(idx++, entity.getDecode()); + ps.setString(idx++, entity.getIso2()); + ps.setString(idx++, entity.getIso3()); + } + + @Override + protected RowMapper getRowMapper() { + return null; + } + + @Override + protected String extractId(FlagCodeEntity entity) { + return null; + } + + @Override + public void saveAllFlagCode(List items) { + if (items == null || items.isEmpty()) { + return; + } + jdbcTemplate.batchUpdate(getUpdateSql(), items, items.size(), + (ps, entity) -> { + try { + setUpdateParameters(ps, entity); + } catch (Exception e) { + log.error("배치 삽입 파라미터 설정 실패", e); + throw new RuntimeException(e); + } + }); + + log.info("{} 전체 저장 완료: {} 건", getEntityName(), items.size()); + } +} diff --git a/src/main/java/com/snp/batch/jobs/common/batch/repository/Stat5CodeRepository.java b/src/main/java/com/snp/batch/jobs/common/batch/repository/Stat5CodeRepository.java new file mode 100644 index 0000000..10edd49 --- /dev/null +++ b/src/main/java/com/snp/batch/jobs/common/batch/repository/Stat5CodeRepository.java @@ -0,0 +1,9 @@ +package com.snp.batch.jobs.common.batch.repository; + +import com.snp.batch.jobs.common.batch.entity.Stat5CodeEntity; + +import java.util.List; + +public interface Stat5CodeRepository { + void saveAllStat5Code(List items); +} diff --git a/src/main/java/com/snp/batch/jobs/common/batch/repository/Stat5CodeRepositoryImpl.java b/src/main/java/com/snp/batch/jobs/common/batch/repository/Stat5CodeRepositoryImpl.java new file mode 100644 index 0000000..90251fb --- /dev/null +++ b/src/main/java/com/snp/batch/jobs/common/batch/repository/Stat5CodeRepositoryImpl.java @@ -0,0 +1,109 @@ +package com.snp.batch.jobs.common.batch.repository; + +import com.snp.batch.common.batch.repository.BaseJdbcRepository; +import com.snp.batch.jobs.common.batch.entity.Stat5CodeEntity; +import lombok.extern.slf4j.Slf4j; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Repository; + +import java.sql.PreparedStatement; +import java.util.List; + +@Slf4j +@Repository("Stat5CodeRepository") +public class Stat5CodeRepositoryImpl extends BaseJdbcRepository implements Stat5CodeRepository{ + public Stat5CodeRepositoryImpl(JdbcTemplate jdbcTemplate) { + super(jdbcTemplate); + } + + @Override + protected String getEntityName() { + return "Stat5CodeEntity"; + } + + @Override + protected String getTableName() { + return "snp_data.stat5code"; + } + + @Override + protected RowMapper getRowMapper() { + return null; + } + + @Override + protected String extractId(Stat5CodeEntity entity) { + return null; + } + + @Override + protected String getInsertSql() { + return null; + } + + @Override + protected String getUpdateSql() { + return """ + INSERT INTO snp_data.stat5code ( + level1, level1decode, level2, level2decode, level3, level3decode, level4, level4decode, level5, level5decode, description, release + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + ON CONFLICT (level1, level2, level3, level4, level5) + DO UPDATE SET + level1 = EXCLUDED.level1, + level1decode = EXCLUDED.level1decode, + level2 = EXCLUDED.level2, + level2decode = EXCLUDED.level2decode, + level3 = EXCLUDED.level3, + level3decode = EXCLUDED.level3decode, + level4 = EXCLUDED.level4, + level4decode = EXCLUDED.level4decode, + level5 = EXCLUDED.level5, + level5decode = EXCLUDED.level5decode, + description = EXCLUDED.description, + release = EXCLUDED.release, + batch_flag = 'N' + """; + } + + @Override + protected void setInsertParameters(PreparedStatement ps, Stat5CodeEntity entity) throws Exception { + + } + + @Override + protected void setUpdateParameters(PreparedStatement ps, Stat5CodeEntity entity) throws Exception { + int idx = 1; + ps.setString(idx++, entity.getLevel1()); + ps.setString(idx++, entity.getLevel1Decode()); + ps.setString(idx++, entity.getLevel2()); + ps.setString(idx++, entity.getLevel2Decode()); + ps.setString(idx++, entity.getLevel3()); + ps.setString(idx++, entity.getLevel3Decode()); + ps.setString(idx++, entity.getLevel4()); + ps.setString(idx++, entity.getLevel4Decode()); + ps.setString(idx++, entity.getLevel5()); + ps.setString(idx++, entity.getLevel5Decode()); + ps.setString(idx++, entity.getDescription()); + ps.setString(idx++, entity.getRelease()); + } + + @Override + public void saveAllStat5Code(List items) { + if (items == null || items.isEmpty()) { + return; + } + jdbcTemplate.batchUpdate(getUpdateSql(), items, items.size(), + (ps, entity) -> { + try { + setUpdateParameters(ps, entity); + } catch (Exception e) { + log.error("배치 삽입 파라미터 설정 실패", e); + throw new RuntimeException(e); + } + }); + + log.info("{} 전체 저장 완료: {} 건", getEntityName(), items.size()); + } + +} diff --git a/src/main/java/com/snp/batch/jobs/common/batch/writer/FlagCodeDataWriter.java b/src/main/java/com/snp/batch/jobs/common/batch/writer/FlagCodeDataWriter.java new file mode 100644 index 0000000..9bf0b6d --- /dev/null +++ b/src/main/java/com/snp/batch/jobs/common/batch/writer/FlagCodeDataWriter.java @@ -0,0 +1,29 @@ +package com.snp.batch.jobs.common.batch.writer; + +import com.snp.batch.common.batch.writer.BaseWriter; +import com.snp.batch.jobs.common.batch.entity.FlagCodeEntity; +import com.snp.batch.jobs.common.batch.repository.FlagCodeRepository; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; + +/** + * ShipEntity를 DB에 저장하는 ItemWriter + * BaseWriter를 상속하여 공통 저장 패턴 적용 + */ +@Slf4j +public class FlagCodeDataWriter extends BaseWriter { + + private final FlagCodeRepository flagCodeRepository; + + public FlagCodeDataWriter(FlagCodeRepository flagCodeRepository) { + super("FlagCodeEntity"); + this.flagCodeRepository = flagCodeRepository; + } + + @Override + protected void writeItems(List items) throws Exception { + flagCodeRepository.saveAllFlagCode(items); + log.info("FlagCode 저장 완료: {} 건", items.size()); + } +} diff --git a/src/main/java/com/snp/batch/jobs/common/batch/writer/Stat5CodeDataWriter.java b/src/main/java/com/snp/batch/jobs/common/batch/writer/Stat5CodeDataWriter.java new file mode 100644 index 0000000..9f8267e --- /dev/null +++ b/src/main/java/com/snp/batch/jobs/common/batch/writer/Stat5CodeDataWriter.java @@ -0,0 +1,25 @@ +package com.snp.batch.jobs.common.batch.writer; + +import com.snp.batch.common.batch.writer.BaseWriter; +import com.snp.batch.jobs.common.batch.entity.Stat5CodeEntity; +import com.snp.batch.jobs.common.batch.repository.Stat5CodeRepository; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; + +@Slf4j +public class Stat5CodeDataWriter extends BaseWriter { + + private final Stat5CodeRepository stat5CodeRepository; + + public Stat5CodeDataWriter(Stat5CodeRepository stat5CodeRepository) { + super("Stat5CodeEntity"); + this.stat5CodeRepository = stat5CodeRepository; + } + + @Override + protected void writeItems(List items) throws Exception { + stat5CodeRepository.saveAllStat5Code(items); + log.info("Stat5Code 저장 완료: {} 건", items.size()); + } +}