ShipDetailUpdateJob 개발

- CrewList
- StowageCommodity
- GroupBeneficialOwnerHistory
- ShipManagerHistory
- OperatorHistory
- TechnicalManagerHistory
- BareBoatCharterHistory
- NameHistory
- FlagHistory
- AdditionalInformation
- PandIHistory
- CallSignAndMmsiHistory
- IceClass
- SafetyManagementCertificateHistory
- ClassHistory
- SurveyDatesHistory
- SurveyDatesHistoryUnique
- SisterShipLinks
- StatusHistory
- SpecialFeature
- Thrusters
This commit is contained in:
hyojin kim 2025-12-10 15:59:47 +09:00
부모 eb81be5f21
커밋 090f009529
57개의 변경된 파일4177개의 추가작업 그리고 133개의 파일을 삭제

파일 보기

@ -15,12 +15,38 @@ public class JsonChangeDetector {
private static final java.util.Set<String> EXCLUDE_KEYS =
java.util.Set.of("DataSetVersion", "APSStatus", "LastUpdateDate", "LastUpdateDateTime");
private static final Map<String, String> LIST_SORT_KEYS = Map.of(
// List 필드명 // 정렬 기준
"OwnerHistory" ,"Sequence", // OwnerHistory는 Sequence를 기준으로 정렬
"SurveyDatesHistoryUnique" , "SurveyDate" // SurveyDatesHistoryUnique는 SurveyDate를 기준으로 정렬
// 추가적인 List/Array 필드가 있다면 여기에 추가
);
// =========================================================================
// LIST_SORT_KEYS: 정적 초기화 블록을 사용한 Map 정의
// =========================================================================
private static final Map<String, String> LIST_SORT_KEYS;
static {
// TreeMap을 사용하여 키를 알파벳 순으로 정렬할 수도 있지만, 여기서는 HashMap을 사용하고 final로 만듭니다.
Map<String, String> map = new HashMap<>();
// List 필드명 // 정렬 기준 복합 (JSON 필드명, 쉼표로 구분)
map.put("OwnerHistory", "OwnerCode,EffectiveDate");
map.put("CrewList", "ID");
map.put("StowageCommodity", "Sequence,CommodityCode,StowageCode");
map.put("GroupBeneficialOwnerHistory", "EffectiveDate,GroupBeneficialOwnerCode,Sequence");
map.put("ShipManagerHistory", "EffectiveDate,ShipManagerCode,Sequence");
map.put("OperatorHistory", "EffectiveDate,OperatorCode,Sequence");
map.put("TechnicalManagerHistory", "EffectiveDate,Sequence,TechnicalManagerCode");
map.put("BareBoatCharterHistory", "Sequence,EffectiveDate,BBChartererCode");
map.put("NameHistory", "Sequence,EffectiveDate");
map.put("FlagHistory", "FlagCode,EffectiveDate,Sequence");
map.put("PandIHistory", "PandIClubCode,EffectiveDate");
map.put("CallSignAndMmsiHistory", "EffectiveDate,SeqNo");
map.put("IceClass", "IceClassCode");
map.put("SafetyManagementCertificateHistory", "Sequence");
map.put("ClassHistory", "ClassCode,EffectiveDate,Sequence");
map.put("SurveyDatesHistory", "ClassSocietyCode");
map.put("SurveyDatesHistoryUnique", "ClassSocietyCode,SurveyDate,SurveyType");
map.put("SisterShipLinks", "LinkedLRNO");
map.put("StatusHistory", "Sequence,StatusCode,StatusDate");
map.put("SpecialFeature", "Sequence,SpecialFeatureCode");
map.put("Thrusters", "Sequence");
LIST_SORT_KEYS = Collections.unmodifiableMap(map);
}
// =========================================================================
// 1. JSON 문자열을 정렬 필터링된 Map으로 변환하는 핵심 로직
@ -90,14 +116,16 @@ public class JsonChangeDetector {
}
}
// 2. 🔑 List 필드명에 따른 순서 정렬 로직 (추가 핵심 로직)
// 2. 🔑 List 필드명에 따른 복합 순서 정렬 로직 (수정 핵심 로직)
String listFieldName = entry.getKey();
String sortKey = LIST_SORT_KEYS.get(listFieldName);
String sortKeysString = LIST_SORT_KEYS.get(listFieldName); // 쉼표로 구분된 복합 문자열
if (sortKeysString != null && !filteredList.isEmpty() && filteredList.get(0) instanceof Map) {
// 복합 문자열을 개별 배열로 분리
final String[] sortKeys = sortKeysString.split(",");
if (sortKey != null && !filteredList.isEmpty() && filteredList.get(0) instanceof Map) {
// Map 요소를 가진 리스트인 경우에만 정렬 실행
try {
// 정렬 기준 키를 사용하여 Comparator를 생성
Collections.sort(filteredList, new Comparator<Object>() {
@Override
@SuppressWarnings("unchecked")
@ -105,22 +133,45 @@ public class JsonChangeDetector {
Map<String, Object> map1 = (Map<String, Object>) o1;
Map<String, Object> map2 = (Map<String, Object>) o2;
// 정렬 기준 (sortKey) 값을 가져와 비교
Object key1 = map1.get(sortKey);
Object key2 = map2.get(sortKey);
// 복합 (sortKeys) 순서대로 순회하며 비교
for (String rawSortKey : sortKeys) {
// 키의 공백 제거
String sortKey = rawSortKey.trim();
if (key1 == null || key2 == null) {
// 값이 null인 경우, Map의 전체 문자열로 비교 (안전장치)
return map1.toString().compareTo(map2.toString());
Object key1 = map1.get(sortKey);
Object key2 = map2.get(sortKey);
// null 처리 로직
if (key1 == null && key2 == null) {
continue; // 값이 동일하므로 다음 키로 이동
}
if (key1 == null) {
// key1이 null이고 key2는 null이 아니면, key2가 크다고 ( 순서) 간주하고 1 반환
return 1;
}
if (key2 == null) {
// key2가 null이고 key1은 null이 아니면, key1이 크다고 ( 순서) 간주하고 -1 반환
return -1;
}
// 값을 문자열로 변환하여 비교 (String, Number, Date 타입 모두 처리 가능)
int comparisonResult = key1.toString().compareTo(key2.toString());
// 현재 키에서 순서가 결정되면 즉시 반환
if (comparisonResult != 0) {
return comparisonResult;
}
// comparisonResult == 0 이면 다음 키로 이동하여 비교를 계속함
}
// String 타입으로 변환하여 비교 (Date, Number 타입도 대부분 String으로 처리 가능)
return key1.toString().compareTo(key2.toString());
// 모든 키를 비교해도 동일한 경우
// 경우 Map은 해시값 측면에서 동일한 것으로 간주되어야 합니다.
return 0;
}
});
} catch (Exception e) {
System.err.println("List sort failed for key " + listFieldName + ": " + e.getMessage());
// 정렬 실패 원래 순서 유지
// 정렬 실패 원래 순서 유지 (filteredList 상태 유지)
}
}
sortedMap.put(key, filteredList);
@ -132,7 +183,6 @@ public class JsonChangeDetector {
return sortedMap;
}
// =========================================================================
// 2. 해시 생성 로직
// =========================================================================

파일 보기

@ -0,0 +1,38 @@
package com.snp.batch.global.model;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import jakarta.persistence.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Entity
@Getter
@Setter
@NoArgsConstructor
@Table(name = "BATCH_LAST_EXECUTION")
@EntityListeners(AuditingEntityListener.class)
public class BatchLastExecution {
@Id
@Column(name = "API_KEY", length = 50)
private String apiKey;
@Column(name = "LAST_SUCCESS_DATE", nullable = false)
private LocalDate lastSuccessDate;
@CreatedDate
@Column(name = "CREATED_AT", updatable = false, nullable = false)
private LocalDateTime createdAt;
@LastModifiedDate
@Column(name = "UPDATED_AT", nullable = false)
private LocalDateTime updatedAt;
public BatchLastExecution(String apiKey, LocalDate lastSuccessDate) {
this.apiKey = apiKey;
this.lastSuccessDate = lastSuccessDate;
}
}

파일 보기

@ -0,0 +1,36 @@
package com.snp.batch.global.repository;
import com.snp.batch.global.model.BatchLastExecution;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.time.LocalDate;
import java.util.Optional;
@Repository
public interface BatchLastExecutionRepository extends JpaRepository<BatchLastExecution, String> {
// 1. findLastSuccessDate 함수 구현
/**
* API 키를 기준으로 마지막 성공 일자를 조회합니다.
* @param apiKey 조회할 API (: "SHIP_UPDATE_API")
* @return 마지막 성공 일자 (LocalDate) 포함하는 Optional
*/
@Query("SELECT b.lastSuccessDate FROM BatchLastExecution b WHERE b.apiKey = :apiKey")
Optional<LocalDate> findLastSuccessDate(@Param("apiKey") String apiKey);
// 2. updateLastSuccessDate 함수 구현 (직접 UPDATE 쿼리 사용)
/**
* 특정 API 키의 마지막 성공 일자를 업데이트합니다.
*
* @param apiKey 업데이트할 API
* @param successDate 업데이트할 성공 일자
* @return 업데이트된 레코드
*/
@Modifying
@Query("UPDATE BatchLastExecution b SET b.lastSuccessDate = :successDate WHERE b.apiKey = :apiKey")
int updateLastSuccessDate(@Param("apiKey") String apiKey, @Param("successDate") LocalDate successDate);
}

파일 보기

@ -0,0 +1,118 @@
package com.snp.batch.jobs.shipdetail.batch.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.snp.batch.common.batch.config.BaseJobConfig;
import com.snp.batch.jobs.shipdetail.batch.dto.ShipDetailComparisonData;
import com.snp.batch.jobs.shipdetail.batch.dto.ShipDetailUpdate;
import com.snp.batch.jobs.shipdetail.batch.processor.ShipDetailDataProcessor;
import com.snp.batch.jobs.shipdetail.batch.reader.ShipDetailUpdateDataReader;
import com.snp.batch.jobs.shipdetail.batch.writer.ShipDetailDataWriter;
import com.snp.batch.service.BatchDateService;
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.jdbc.core.JdbcTemplate;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.web.reactive.function.client.WebClient;
/**
* 선박 상세 정보 Import Job Config
*
* 특징:
* - ship_data 테이블에서 IMO 번호 조회
* - IMO 번호를 100개씩 배치로 분할
* - Maritime API GetShipsByIHSLRorIMONumbers 호출
* TODO : GetShipsByIHSLRorIMONumbersAll 호출로 변경
* - 선박 상세 정보를 ship_detail 테이블에 저장 (UPSERT)
*
* 데이터 흐름:
* ShipDetailDataReader (ship_data Maritime API)
* (ShipDetailDto)
* ShipDetailDataProcessor
* (ShipDetailEntity)
* ShipDetailDataWriter
* (ship_detail 테이블)
*/
/**
* 선박 상세 정보 Import Job Config
* I: ShipDetailComparisonData (Reader 출력)
* O: ShipDetailUpdate (Processor 출력)
*/
@Slf4j
@Configuration
public class ShipDetailUpdateJobConfig extends BaseJobConfig<ShipDetailComparisonData, ShipDetailUpdate> {
private final ShipDetailDataProcessor shipDetailDataProcessor;
private final ShipDetailDataWriter shipDetailDataWriter;
private final JdbcTemplate jdbcTemplate;
private final WebClient maritimeApiWebClient;
private final ObjectMapper objectMapper; // ObjectMapper 주입 추가
private final BatchDateService batchDateService;
public ShipDetailUpdateJobConfig(
JobRepository jobRepository,
PlatformTransactionManager transactionManager,
ShipDetailDataProcessor shipDetailDataProcessor,
ShipDetailDataWriter shipDetailDataWriter,
JdbcTemplate jdbcTemplate,
@Qualifier("maritimeApiWebClient") WebClient maritimeApiWebClient,
ObjectMapper objectMapper,
BatchDateService batchDateService) { // ObjectMapper 주입 추가
super(jobRepository, transactionManager);
this.shipDetailDataProcessor = shipDetailDataProcessor;
this.shipDetailDataWriter = shipDetailDataWriter;
this.jdbcTemplate = jdbcTemplate;
this.maritimeApiWebClient = maritimeApiWebClient;
this.objectMapper = objectMapper; // ObjectMapper 초기화
this.batchDateService = batchDateService;
}
@Override
protected String getJobName() {
return "ShipDetailUpdateJob";
}
@Override
protected String getStepName() {
return "ShipDetailUpdateStep";
}
@Override
protected ItemReader<ShipDetailComparisonData> createReader() { // 타입 변경
// Reader 생성자 수정: ObjectMapper를 전달합니다.
return new ShipDetailUpdateDataReader(maritimeApiWebClient, jdbcTemplate, objectMapper, batchDateService);
}
@Override
protected ItemProcessor<ShipDetailComparisonData, ShipDetailUpdate> createProcessor() {
return shipDetailDataProcessor;
}
@Override
protected ItemWriter<ShipDetailUpdate> createWriter() { // 타입 변경
return shipDetailDataWriter;
}
@Override
protected int getChunkSize() {
return 30; // API에서 100개씩 가져오므로 chunk도 100으로 설정
}
@Bean(name = "ShipDetailUpdateJob")
public Job ShipDetailUpdateJob() {
return job();
}
@Bean(name = "ShipDetailUpdateStep")
public Step ShipDetailUpdateStep() {
return step();
}
}

파일 보기

@ -0,0 +1,45 @@
package com.snp.batch.jobs.shipdetail.batch.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@NoArgsConstructor
public class AdditionalInformationDto {
@Getter @Setter @ToString @NoArgsConstructor
public static class DataSetVersionDto {
@JsonProperty("DataSetVersion")
private String dataSetVersion;
}
@JsonProperty("DataSetVersion")
private DataSetVersionDto dataSetVersion;
@JsonProperty("LRNO")
private String lrno;
@JsonProperty("ShipEmail")
private String shipEmail;
@JsonProperty("WaterDepthMax")
private String waterDepthMax;
@JsonProperty("DrillDepthMax")
private String drillDepthMax;
@JsonProperty("DrillBargeInd")
private String drillBargeInd;
@JsonProperty("ProductionVesselInd")
private String productionVesselInd;
@JsonProperty("DeckHeatExchangerInd")
private String deckHeatExchangerInd;
@JsonProperty("DeckHeatExchangerMaterial")
private String deckHeatExchangerMaterial;
@JsonProperty("TweenDeckPortable")
private String tweenDeckPortable;
@JsonProperty("TweenDeckFixed")
private String tweenDeckFixed;
@JsonProperty("SatComID")
private String satComID;
@JsonProperty("SatComAnsBack")
private String satComAnsBack;
}

파일 보기

@ -0,0 +1,30 @@
package com.snp.batch.jobs.shipdetail.batch.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@NoArgsConstructor
public class BareBoatCharterHistoryDto {
@Getter @Setter @ToString @NoArgsConstructor
public static class DataSetVersionDto {
@JsonProperty("DataSetVersion")
private String dataSetVersion;
}
@JsonProperty("DataSetVersion")
private DataSetVersionDto dataSetVersion;
@JsonProperty("LRNO")
private String lrno;
@JsonProperty("Sequence")
private String sequence;
@JsonProperty("EffectiveDate")
private String effectiveDate;
@JsonProperty("BBChartererCode")
private String bbChartererCode;
@JsonProperty("BBCharterer")
private String bbCharterer;
}

파일 보기

@ -0,0 +1,32 @@
package com.snp.batch.jobs.shipdetail.batch.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@NoArgsConstructor
public class CallSignAndMmsiHistoryDto {
@Getter @Setter @ToString @NoArgsConstructor
public static class DataSetVersionDto {
@JsonProperty("DataSetVersion")
private String dataSetVersion;
}
@JsonProperty("DataSetVersion")
private DataSetVersionDto dataSetVersion;
@JsonProperty("Lrno")
private String lrno;
@JsonProperty("SeqNo")
private String seqNo;
@JsonProperty("CallSign")
private String callSign;
@JsonProperty("Mmsi")
private String mmsi;
@JsonProperty("EffectiveDate")
private String effectiveDate;
// MMSI는 JSON에 없으므로 DTO에 포함하지 않음. Entity에서 처리.
}

파일 보기

@ -0,0 +1,37 @@
package com.snp.batch.jobs.shipdetail.batch.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@NoArgsConstructor
public class ClassHistoryDto {
@Getter @Setter @ToString @NoArgsConstructor
public static class DataSetVersionDto {
@JsonProperty("DataSetVersion")
private String dataSetVersion;
}
@JsonProperty("DataSetVersion")
private DataSetVersionDto dataSetVersion;
@JsonProperty("Class")
private String _class; // 'class' is a reserved keyword in Java
@JsonProperty("ClassCode")
private String classCode;
@JsonProperty("ClassIndicator")
private String classIndicator;
@JsonProperty("ClassID")
private String classID;
@JsonProperty("CurrentIndicator")
private String currentIndicator;
@JsonProperty("EffectiveDate")
private String effectiveDate;
@JsonProperty("LRNO")
private String lrno;
@JsonProperty("Sequence")
private String sequence;
}

파일 보기

@ -0,0 +1,61 @@
package com.snp.batch.jobs.shipdetail.batch.dto;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.*;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class CrewListDto {
// Nested class for DataSetVersion object
@Getter
@Setter
@ToString
@NoArgsConstructor
public static class DataSetVersionDto {
@JsonProperty("DataSetVersion")
private String dataSetVersion;
}
@JsonProperty("DataSetVersion")
private DataSetVersionDto dataSetVersion;
@JsonProperty("ID")
private String id;
@JsonProperty("LRNO")
private String lrno;
@JsonProperty("Shipname")
private String shipname;
@JsonProperty("CrewListDate")
private String crewListDate;
@JsonProperty("Nationality")
private String nationality;
@JsonProperty("TotalCrew")
private String totalCrew;
@JsonProperty("TotalRatings")
private String totalRatings;
@JsonProperty("TotalOfficers")
private String totalOfficers;
@JsonProperty("TotalCadets")
private String totalCadets;
@JsonProperty("TotalTrainees")
private String totalTrainees;
@JsonProperty("TotalRidingSquad")
private String totalRidingSquad;
@JsonProperty("TotalUndeclared")
private String totalUndeclared;
}

파일 보기

@ -0,0 +1,31 @@
package com.snp.batch.jobs.shipdetail.batch.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@NoArgsConstructor
public class FlagHistoryDto {
@Getter @Setter @ToString @NoArgsConstructor
public static class DataSetVersionDto {
@JsonProperty("DataSetVersion")
private String dataSetVersion;
}
@JsonProperty("DataSetVersion")
private DataSetVersionDto dataSetVersion;
@JsonProperty("EffectiveDate")
private String effectiveDate;
@JsonProperty("Flag")
private String flag;
@JsonProperty("FlagCode")
private String flagCode;
@JsonProperty("LRNO")
private String lrno;
@JsonProperty("Sequence")
private String sequence;
}

파일 보기

@ -0,0 +1,45 @@
package com.snp.batch.jobs.shipdetail.batch.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@NoArgsConstructor
public class GroupBeneficialOwnerHistoryDto {
// Nested class for DataSetVersion object
@Getter
@Setter
@ToString
@NoArgsConstructor
public static class DataSetVersionDto {
@JsonProperty("DataSetVersion")
private String dataSetVersion;
}
@JsonProperty("DataSetVersion")
private DataSetVersionDto dataSetVersion; // 데이터셋버전
@JsonProperty("CompanyStatus")
private String companyStatus; // 회사상태
@JsonProperty("EffectiveDate")
private String effectiveDate; // 효력일
@JsonProperty("GroupBeneficialOwner")
private String groupBeneficialOwner; // 그룹실질소유자
@JsonProperty("GroupBeneficialOwnerCode")
private String groupBeneficialOwnerCode; // 그룹실질소유자코드
@JsonProperty("LRNO")
private String lrno; // LR/IMO번호
@JsonProperty("Sequence")
private String sequence; // 순번
}

파일 보기

@ -0,0 +1,27 @@
package com.snp.batch.jobs.shipdetail.batch.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@NoArgsConstructor
public class IceClassDto {
@Getter @Setter @ToString @NoArgsConstructor
public static class DataSetVersionDto {
@JsonProperty("DataSetVersion")
private String dataSetVersion;
}
@JsonProperty("DataSetVersion")
private DataSetVersionDto dataSetVersion;
@JsonProperty("IceClass")
private String iceClass;
@JsonProperty("IceClassCode")
private String iceClassCode;
@JsonProperty("LRNO")
private String lrno;
}

파일 보기

@ -0,0 +1,29 @@
package com.snp.batch.jobs.shipdetail.batch.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@NoArgsConstructor
public class NameHistoryDto {
@Getter @Setter @ToString @NoArgsConstructor
public static class DataSetVersionDto {
@JsonProperty("DataSetVersion")
private String dataSetVersion;
}
@JsonProperty("DataSetVersion")
private DataSetVersionDto dataSetVersion;
@JsonProperty("Effective_Date")
private String effectiveDate;
@JsonProperty("LRNO")
private String lrno;
@JsonProperty("Sequence")
private String sequence;
@JsonProperty("VesselName")
private String vesselName;
}

파일 보기

@ -0,0 +1,43 @@
package com.snp.batch.jobs.shipdetail.batch.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@NoArgsConstructor
public class OperatorHistoryDto {
@Getter
@Setter
@ToString
@NoArgsConstructor
public static class DataSetVersionDto {
@JsonProperty("DataSetVersion")
private String dataSetVersion;
}
@JsonProperty("DataSetVersion")
private DataSetVersionDto dataSetVersion;
@JsonProperty("CompanyStatus")
private String companyStatus;
@JsonProperty("EffectiveDate")
private String effectiveDate;
@JsonProperty("LRNO")
private String lrno;
@JsonProperty("Operator")
private String operator;
@JsonProperty("OperatorCode")
private String operatorCode;
@JsonProperty("Sequence")
private String sequence;
}

파일 보기

@ -0,0 +1,33 @@
package com.snp.batch.jobs.shipdetail.batch.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@NoArgsConstructor
public class PandIHistoryDto {
@Getter @Setter @ToString @NoArgsConstructor
public static class DataSetVersionDto {
@JsonProperty("DataSetVersion")
private String dataSetVersion;
}
@JsonProperty("DataSetVersion")
private DataSetVersionDto dataSetVersion;
@JsonProperty("LRNO")
private String lrno;
@JsonProperty("Sequence")
private String sequence;
@JsonProperty("PandIClubCode")
private String pandIClubCode;
@JsonProperty("PandIClubDecode")
private String pandIClubDecode;
@JsonProperty("EffectiveDate")
private String effectiveDate;
@JsonProperty("Source")
private String source;
}

파일 보기

@ -0,0 +1,49 @@
package com.snp.batch.jobs.shipdetail.batch.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@NoArgsConstructor
public class SafetyManagementCertificateHistoryDto {
@Getter @Setter @ToString @NoArgsConstructor
public static class DataSetVersionDto {
@JsonProperty("DataSetVersion")
private String dataSetVersion;
}
@JsonProperty("DataSetVersion")
private DataSetVersionDto dataSetVersion;
@JsonProperty("LRNO")
private String lrno;
@JsonProperty("SafetyManagementCertificateAuditor")
private String safetyManagementCertificateAuditor;
@JsonProperty("SafetyManagementCertificateConventionOrVol")
private String safetyManagementCertificateConventionOrVol;
@JsonProperty("SafetyManagementCertificateDateExpires")
private String safetyManagementCertificateDateExpires;
@JsonProperty("SafetyManagementCertificateDateIssued")
private String safetyManagementCertificateDateIssued;
@JsonProperty("SafetyManagementCertificateDOCCompany")
private String safetyManagementCertificateDOCCompany;
@JsonProperty("SafetyManagementCertificateFlag")
private String safetyManagementCertificateFlag;
@JsonProperty("SafetyManagementCertificateIssuer")
private String safetyManagementCertificateIssuer;
@JsonProperty("SafetyManagementCertificateOtherDescription")
private String safetyManagementCertificateOtherDescription;
@JsonProperty("SafetyManagementCertificateShipName")
private String safetyManagementCertificateShipName;
@JsonProperty("SafetyManagementCertificateShipType")
private String safetyManagementCertificateShipType;
@JsonProperty("SafetyManagementCertificateSource")
private String safetyManagementCertificateSource;
@JsonProperty("SafetyManagementCertificateCompanyCode")
private String safetyManagementCertificateCompanyCode;
@JsonProperty("Sequence")
private String sequence;
}

파일 보기

@ -183,91 +183,150 @@ public class ShipDetailDto {
@JsonProperty("OwnerHistory")
private List<OwnerHistoryDto> ownerHistory;
/**
* 승선자 정보 List
* API: CrewList
*/
@JsonProperty("CrewList")
private List<CrewListDto> crewList;
/**
* TODO : Core20 Dto 작성
* shipresultindex int8 NOT NULL, -- 결과인덱스
* batch_flag varchar(1) DEFAULT 'N'::character varying NULL -- 업데이트 이력 확인 (N:대기,P:진행,S:완료)
* vesselid varchar(7) NOT NULL, -- 선박ID
* ihslrorimoshipno varchar(7) NOT NULL, -- IMO번호
* maritimemobileserviceidentitymmsinumber varchar(9) NULL, -- MMSI
* shipname varchar(100) NULL, -- 선명
* callsign varchar(5) NULL, -- 호출부호
* flagname varchar(100) NULL, -- 국가
* portofregistry varchar(50) NULL, -- 등록항
* classificationsociety varchar(50) NULL, -- 선급
* shiptypelevel5 varchar(15) NULL, -- 선종(Lv5)
* shiptypelevel5subtype varchar(15) NULL, -- 세부선종
* yearofbuild varchar(4) NULL, -- 건조연도
* shipbuilder varchar(100) NULL, -- 조선소
* lengthoverallloa numeric(3, 3) NULL, -- 전장(LOA)[m]
* breadthmoulded numeric(3, 3) NULL, -- 형폭(몰디드)[m]
* "depth" numeric(3, 3) NULL, -- 깊이[m]
* draught numeric(3, 3) NULL, -- 흘수[m]
* grosstonnage varchar(4) NULL, -- 총톤수(GT)
* deadweight varchar(5) NULL, -- 재화중량톤수(DWT)
* teu varchar(1) NULL, -- 컨테이너(TEU)
* speedservice numeric(2, 2) NULL, -- 항속(kt)
* mainenginetype varchar(2) NULL, -- 주기관 형식
* 화물 적재 정보 List
* API: StowageCommodity
*/
// TODO : List/Array 데이터 추가
@JsonProperty("StowageCommodity")
private List<StowageCommodityDto> stowageCommodity;
/**
* 선박 추가 정보 List
* 그룹 실질 소유자 이력 List
* API: GroupBeneficialOwnerHistory
*/
@JsonProperty("GroupBeneficialOwnerHistory")
private List<GroupBeneficialOwnerHistoryDto> groupBeneficialOwnerHistory;
/**
* 선박 관리자 이력 List
* API: ShipManagerHistory
*/
@JsonProperty("ShipManagerHistory")
private List<ShipManagerHistoryDto> shipManagerHistory;
/**
* 운항사 이력 List
* API: OperatorHistory
*/
@JsonProperty("OperatorHistory")
private List<OperatorHistoryDto> operatorHistory;
/**
* 기술 관리자 이력 List
* API: TechnicalManagerHistory
*/
@JsonProperty("TechnicalManagerHistory")
private List<TechnicalManagerHistoryDto> technicalManagerHistory;
/**
* 나용선(Bare Boat Charter) 이력 List
* API: BareBoatCharterHistory
*/
@JsonProperty("BareBoatCharterHistory")
private List<BareBoatCharterHistoryDto> bareBoatCharterHistory;
/**
* 선박 이름 이력 List
* API: NameHistory
*/
@JsonProperty("NameHistory")
private List<NameHistoryDto> nameHistory;
/**
* 선박 국적/선기 이력 List
* API: FlagHistory
*/
@JsonProperty("FlagHistory")
private List<FlagHistoryDto> flagHistory;
/**
* 추가 정보 List (API 명세에 따라 단일 객체일 있으나 List로 선언)
* API: AdditionalInformation
*/
// @JsonProperty("AdditionalInformation")
// private List<ShipAddInfoDto> additionalInformation;
@JsonProperty("AdditionalInformation")
private List<AdditionalInformationDto> additionalInformation;
/**
* auxengine
* auxgenerator
* ballastwatermanagement
* bareboatcharterhistory
* builderaddress
* callsignandmmsihistory
* capacities
* cargopump
* classcurrent
* classhistory
* companycompliancedetails
* companydetailscomplexwithcodesa
* companyfleetcounts
* companyorderbookcounts
* companyvesselrelationships
* crewlist
* darkactivityconfirmed
* enginebuilder
* flaghistory
* grosstonnagehistory
* groupbeneficialownerhistory
* iceclass
* liftinggear
* mainengine
* namehistory
* operatorhistory
* ownerhistory
* pandihistory
* propellers
* safetymanagementcertificatehist
* sales
* scrubberdetails
* shipbuilderandsubcontractor
* shipbuilderdetail
* shipbuilderhistory
* shipcertificatesall
* shipmanagerhistory
* shiptypehistory
* sistershiplinks
* specialfeature
* statushistory
* stowagecommodity
* surveydates
* surveydateshistoryunique
* tankcoatings
* technicalmanagerhistory
* thrusters
* */
* P&I 보험 이력 List
* API: PandIHistory
*/
@JsonProperty("PandIHistory")
private List<PandIHistoryDto> pandIHistory;
/**
* 호출 부호 MMSI 이력 List
* API: CallSignAndMmsiHistory
*/
@JsonProperty("CallSignAndMmsiHistory")
private List<CallSignAndMmsiHistoryDto> callSignAndMmsiHistory;
/**
* 내빙 등급 List
* API: IceClass
*/
@JsonProperty("IceClass")
private List<IceClassDto> iceClass;
/**
* 안전 관리 증서 이력 List
* API: SafetyManagementCertificateHistory
*/
@JsonProperty("SafetyManagementCertificateHistory")
private List<SafetyManagementCertificateHistoryDto> safetyManagementCertificateHistory;
/**
* 선급 이력 List
* API: ClassHistory
*/
@JsonProperty("ClassHistory")
private List<ClassHistoryDto> classHistory;
/**
* 검사 일자 이력 List (집계된 정보)
* API: SurveyDatesHistory
*/
@JsonProperty("SurveyDates")
private List<SurveyDatesHistoryDto> surveyDatesHistory;
/**
* 검사 일자 이력 List (개별 상세 정보)
* API: SurveyDatesHistoryUnique
*/
@JsonProperty("SurveyDatesHistoryUnique")
private List<SurveyDatesHistoryUniqueDto> surveyDatesHistoryUnique;
/**
* 자매선 연결 정보 List
* API: SisterShipLinks
*/
@JsonProperty("SisterShipLinks")
private List<SisterShipLinksDto> sisterShipLinks;
/**
* 선박 상태 이력 List
* API: StatusHistory
*/
@JsonProperty("StatusHistory")
private List<StatusHistoryDto> statusHistory;
/**
* 특수 기능/설비 List
* API: SpecialFeature
*/
@JsonProperty("SpecialFeature")
private List<SpecialFeatureDto> specialFeature;
/**
* 추진기 정보 List
* API: Thrusters
*/
@JsonProperty("Thrusters")
private List<ThrustersDto> thrusters;
}

파일 보기

@ -1,8 +1,6 @@
package com.snp.batch.jobs.shipdetail.batch.dto;
import com.snp.batch.jobs.shipdetail.batch.entity.OwnerHistoryEntity;
import com.snp.batch.jobs.shipdetail.batch.entity.ShipDetailEntity;
import com.snp.batch.jobs.shipdetail.batch.entity.ShipHashEntity;
import com.snp.batch.jobs.shipdetail.batch.entity.*;
import lombok.Builder;
import lombok.Getter;
@ -24,5 +22,25 @@ public class ShipDetailUpdate {
// 외에 OwnerHistory Entity, Core Entity 증분 데이터를 추가합니다.
private final ShipDetailEntity shipDetailEntity;
private final List<OwnerHistoryEntity> ownerHistoryEntityList;
private final List<CrewListEntity> crewListEntityList;
private final List<StowageCommodityEntity> stowageCommodityEntityList;
private final List<GroupBeneficialOwnerHistoryEntity> groupBeneficialOwnerHistoryEntityList;
private final List<ShipManagerHistoryEntity> shipManagerHistoryEntityList;
private final List<OperatorHistoryEntity> operatorHistoryEntityList;
private final List<TechnicalManagerHistoryEntity> technicalManagerHistoryEntityList;
private final List<BareBoatCharterHistoryEntity> bareBoatCharterHistoryEntityList;
private final List<NameHistoryEntity> nameHistoryEntityList;
private final List<FlagHistoryEntity> flagHistoryEntityList;
private final List<AdditionalInformationEntity> additionalInformationEntityList;
private final List<PandIHistoryEntity> pandIHistoryEntityList;
private final List<CallSignAndMmsiHistoryEntity> callSignAndMmsiHistoryEntityList;
private final List<IceClassEntity> iceClassEntityList;
private final List<SafetyManagementCertificateHistoryEntity> safetyManagementCertificateHistoryEntityList;
private final List<ClassHistoryEntity> classHistoryEntityList;
private final List<SurveyDatesHistoryEntity> surveyDatesHistoryEntityList;
private final List<SurveyDatesHistoryUniqueEntity> surveyDatesHistoryUniqueEntityList;
private final List<SisterShipLinksEntity> sisterShipLinksEntityList;
private final List<StatusHistoryEntity> statusHistoryEntityList;
private final List<SpecialFeatureEntity> specialFeatureEntityList;
private final List<ThrustersEntity> thrustersEntityList;
}

파일 보기

@ -0,0 +1,34 @@
package com.snp.batch.jobs.shipdetail.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 ShipDto {
@JsonProperty("DataSetVersion")
private DataSetVersion dataSetVersion;
@JsonProperty("CoreShipInd")
private String coreShipInd;
@JsonProperty("IHSLRorIMOShipNo")
private String imoNumber;
@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public static class DataSetVersion {
@JsonProperty("DataSetVersion")
private String version;
}
}

파일 보기

@ -0,0 +1,45 @@
package com.snp.batch.jobs.shipdetail.batch.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@NoArgsConstructor
public class ShipManagerHistoryDto {
// Nested class for DataSetVersion object
@Getter
@Setter
@ToString
@NoArgsConstructor
public static class DataSetVersionDto {
@JsonProperty("DataSetVersion")
private String dataSetVersion;
}
@JsonProperty("DataSetVersion")
private DataSetVersionDto dataSetVersion;
@JsonProperty("CompanyStatus")
private String companyStatus;
@JsonProperty("EffectiveDate")
private String effectiveDate;
@JsonProperty("LRNO")
private String lrno;
@JsonProperty("Sequence")
private String sequence;
@JsonProperty("ShipManager")
private String shipManager;
@JsonProperty("ShipManagerCode")
private String shipManagerCode;
}

파일 보기

@ -0,0 +1,23 @@
package com.snp.batch.jobs.shipdetail.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 ShipUpdateApiResponse {
@JsonProperty("shipCount")
private Integer shipCount;
@JsonProperty("Ships")
private List<ShipDto> ships;
}

파일 보기

@ -0,0 +1,25 @@
package com.snp.batch.jobs.shipdetail.batch.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@NoArgsConstructor
public class SisterShipLinksDto {
@Getter @Setter @ToString @NoArgsConstructor
public static class DataSetVersionDto {
@JsonProperty("DataSetVersion")
private String dataSetVersion;
}
@JsonProperty("DataSetVersion")
private DataSetVersionDto dataSetVersion;
@JsonProperty("Lrno")
private String lrno;
@JsonProperty("LinkedLRNO")
private String linkedLRNO;
}

파일 보기

@ -0,0 +1,29 @@
package com.snp.batch.jobs.shipdetail.batch.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@NoArgsConstructor
public class SpecialFeatureDto {
@Getter @Setter @ToString @NoArgsConstructor
public static class DataSetVersionDto {
@JsonProperty("DataSetVersion")
private String dataSetVersion;
}
@JsonProperty("DataSetVersion")
private DataSetVersionDto dataSetVersion;
@JsonProperty("LRNO")
private String lrno;
@JsonProperty("Sequence")
private String sequence;
@JsonProperty("SpecialFeature")
private String specialFeature;
@JsonProperty("SpecialFeatureCode")
private String specialFeatureCode;
}

파일 보기

@ -0,0 +1,31 @@
package com.snp.batch.jobs.shipdetail.batch.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@NoArgsConstructor
public class StatusHistoryDto {
@Getter @Setter @ToString @NoArgsConstructor
public static class DataSetVersionDto {
@JsonProperty("DataSetVersion")
private String dataSetVersion;
}
@JsonProperty("DataSetVersion")
private DataSetVersionDto dataSetVersion;
@JsonProperty("LRNO")
private String lrno;
@JsonProperty("Sequence")
private String sequence;
@JsonProperty("Status")
private String status;
@JsonProperty("StatusCode")
private String statusCode;
@JsonProperty("StatusDate")
private String statusDate;
}

파일 보기

@ -0,0 +1,43 @@
package com.snp.batch.jobs.shipdetail.batch.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@NoArgsConstructor
public class StowageCommodityDto {
// Nested class for DataSetVersion object
@Getter
@Setter
@ToString
@NoArgsConstructor
public static class DataSetVersionDto {
@JsonProperty("DataSetVersion")
private String dataSetVersion;
}
@JsonProperty("DataSetVersion")
private DataSetVersionDto dataSetVersion;
@JsonProperty("CommodityCode")
private String commodityCode;
@JsonProperty("CommodityDecode")
private String commodityDecode;
@JsonProperty("LRNO")
private String lrno;
@JsonProperty("Sequence")
private String sequence;
@JsonProperty("StowageCode")
private String stowageCode;
@JsonProperty("StowageDecode")
private String stowageDecode;
}

파일 보기

@ -0,0 +1,37 @@
package com.snp.batch.jobs.shipdetail.batch.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@NoArgsConstructor
public class SurveyDatesHistoryDto {
@Getter @Setter @ToString @NoArgsConstructor
public static class DataSetVersionDto {
@JsonProperty("DataSetVersion")
private String dataSetVersion;
}
@JsonProperty("DataSetVersion")
private DataSetVersionDto dataSetVersion;
@JsonProperty("AnnualSurvey")
private String annualSurvey;
@JsonProperty("ClassSociety")
private String classSociety;
@JsonProperty("ClassSocietyCode")
private String classSocietyCode;
@JsonProperty("ContinuousMachinerySurvey")
private String continuousMachinerySurvey;
@JsonProperty("DockingSurvey")
private String dockingSurvey;
@JsonProperty("LRNO")
private String lrno;
@JsonProperty("SpecialSurvey")
private String specialSurvey;
@JsonProperty("TailShaftSurvey")
private String tailShaftSurvey;
}

파일 보기

@ -0,0 +1,31 @@
package com.snp.batch.jobs.shipdetail.batch.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@NoArgsConstructor
public class SurveyDatesHistoryUniqueDto {
@Getter @Setter @ToString @NoArgsConstructor
public static class DataSetVersionDto {
@JsonProperty("DataSetVersion")
private String dataSetVersion;
}
@JsonProperty("DataSetVersion")
private DataSetVersionDto dataSetVersion;
@JsonProperty("LRNO")
private String lrno;
@JsonProperty("ClassSocietyCode")
private String classSocietyCode;
@JsonProperty("SurveyDate")
private String surveyDate;
@JsonProperty("SurveyType")
private String surveyType;
@JsonProperty("ClassSociety")
private String classSociety;
}

파일 보기

@ -0,0 +1,32 @@
package com.snp.batch.jobs.shipdetail.batch.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@NoArgsConstructor
public class TechnicalManagerHistoryDto {
@Getter @Setter @ToString @NoArgsConstructor
public static class DataSetVersionDto {
@JsonProperty("DataSetVersion")
private String dataSetVersion;
}
@JsonProperty("DataSetVersion")
private DataSetVersionDto dataSetVersion;
@JsonProperty("CompanyStatus")
private String companyStatus;
@JsonProperty("EffectiveDate")
private String effectiveDate;
@JsonProperty("LRNO")
private String lrno;
@JsonProperty("Sequence")
private String sequence;
@JsonProperty("TechnicalManager")
private String technicalManager;
@JsonProperty("TechnicalManagerCode")
private String technicalManagerCode;
}

파일 보기

@ -0,0 +1,39 @@
package com.snp.batch.jobs.shipdetail.batch.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@NoArgsConstructor
public class ThrustersDto {
@Getter @Setter @ToString @NoArgsConstructor
public static class DataSetVersionDto {
@JsonProperty("DataSetVersion")
private String dataSetVersion;
}
@JsonProperty("DataSetVersion")
private DataSetVersionDto dataSetVersion;
@JsonProperty("LRNO")
private String lrno;
@JsonProperty("Sequence")
private String sequence;
@JsonProperty("ThrusterType")
private String thrusterType;
@JsonProperty("ThrusterTypeCode")
private String thrusterTypeCode;
@JsonProperty("NumberOfThrusters")
private String numberOfThrusters; // numeric(20) in DB
@JsonProperty("ThrusterPosition")
private String thrusterPosition;
@JsonProperty("ThrusterBHP")
private String thrusterBHP; // numeric(20) in DB
@JsonProperty("ThrusterKW")
private String thrusterKW; // numeric(20) in DB
@JsonProperty("TypeOfInstallation")
private String typeOfInstallation;
}

파일 보기

@ -0,0 +1,29 @@
package com.snp.batch.jobs.shipdetail.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;
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class AdditionalInformationEntity extends BaseEntity {
// SQL Table: additionalshipsdata
private String lrno;
private String shipemail;
private String waterdepthmax;
private String drilldepthmax;
private String drillbargeind;
private String productionvesselind;
private String deckheatexchangerind;
private String deckheatexchangermaterial;
private String tweendeckportable;
private String tweendeckfixed;
private String satcomid;
private String satcomansback;
}

파일 보기

@ -0,0 +1,21 @@
package com.snp.batch.jobs.shipdetail.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;
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class BareBoatCharterHistoryEntity extends BaseEntity {
private String dataSetVersion;
private String lrno;
private String sequence;
private String effectiveDate;
private String bbChartererCode;
private String bbCharterer;
}

파일 보기

@ -0,0 +1,22 @@
package com.snp.batch.jobs.shipdetail.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;
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class CallSignAndMmsiHistoryEntity extends BaseEntity {
private String dataSetVersion;
private String lrno;
private String sequence; // JSON: SeqNo
private String callsign;
private String mmsi; // JSON에 없음
private String effectiveDate;
}

파일 보기

@ -0,0 +1,25 @@
package com.snp.batch.jobs.shipdetail.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;
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class ClassHistoryEntity extends BaseEntity {
private String dataSetVersion;
private String _class; // 'class' in DB
private String classCode;
private String classIndicator;
private String classID;
private String currentIndicator;
private String effectiveDate;
private String lrno;
private String sequence; // "sequence" in DB
}

파일 보기

@ -0,0 +1,31 @@
package com.snp.batch.jobs.shipdetail.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;
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class CrewListEntity extends BaseEntity {
private String dataSetVersion; // varchar(5)
private String id; // varchar(7)
private String lrno; // varchar(7)
private String shipname; // varchar(200)
private String crewlistdate; // varchar(20)
private String nationality; // varchar(200)
private String totalcrew; // varchar(2)
private String totalratings; // varchar(2)
private String totalofficers; // varchar(2)
private String totalcadets; // varchar(1)
private String totaltrainees; // varchar(1)
private String totalridingsquad; // varchar(1)
private String totalundeclared; // varchar(1)
}

파일 보기

@ -0,0 +1,22 @@
package com.snp.batch.jobs.shipdetail.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;
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class FlagHistoryEntity extends BaseEntity {
private String dataSetVersion;
private String effectiveDate;
private String flag;
private String flagCode;
private String lrno;
private String sequence;
}

파일 보기

@ -0,0 +1,28 @@
package com.snp.batch.jobs.shipdetail.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;
// BaseEntity는 프로젝트에 정의되어 있어야 합니다.
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class GroupBeneficialOwnerHistoryEntity extends BaseEntity {
// JSON & DDL 컬럼
private String dataSetVersion;
private String companyStatus;
private String effectiveDate;
private String groupBeneficialOwner;
private String groupBeneficialOwnerCode;
private String lrno;
private String sequence;
// DB 관리 컬럼 (shipresultindex, vesselid, rowindex) Entity에서 제거됨
}

파일 보기

@ -0,0 +1,20 @@
package com.snp.batch.jobs.shipdetail.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;
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class IceClassEntity extends BaseEntity {
private String dataSetVersion;
private String iceClass;
private String iceClassCode;
private String lrno;
}

파일 보기

@ -0,0 +1,21 @@
package com.snp.batch.jobs.shipdetail.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;
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class NameHistoryEntity extends BaseEntity {
private String dataSetVersion;
private String effectiveDate;
private String lrno;
private String sequence;
private String vesselName;
}

파일 보기

@ -0,0 +1,23 @@
package com.snp.batch.jobs.shipdetail.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;
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class OperatorHistoryEntity extends BaseEntity {
private String dataSetVersion;
private String companyStatus;
private String effectiveDate;
private String lrno;
private String operator;
private String operatorCode;
private String sequence;
}

파일 보기

@ -0,0 +1,23 @@
package com.snp.batch.jobs.shipdetail.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;
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class PandIHistoryEntity extends BaseEntity {
private String dataSetVersion;
private String lrno;
private String sequence;
private String pandiclubcode;
private String pandiclubdecode;
private String effectiveDate;
private String source;
}

파일 보기

@ -0,0 +1,31 @@
package com.snp.batch.jobs.shipdetail.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;
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class SafetyManagementCertificateHistoryEntity extends BaseEntity {
private String dataSetVersion;
private String lrno;
private String safetyManagementCertificateAuditor;
private String safetyManagementCertificateConventionOrVol;
private String safetyManagementCertificateDateExpires;
private String safetyManagementCertificateDateIssued;
private String safetyManagementCertificateDOCCompany;
private String safetyManagementCertificateFlag;
private String safetyManagementCertificateIssuer;
private String safetyManagementCertificateOtherDescription;
private String safetyManagementCertificateShipName;
private String safetyManagementCertificateShipType;
private String safetyManagementCertificateSource;
private String safetyManagementCertificateCompanyCode;
private String sequence;
}

파일 보기

@ -0,0 +1,28 @@
package com.snp.batch.jobs.shipdetail.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;
// BaseEntity는 프로젝트에 정의되어 있다고 가정합니다.
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class ShipManagerHistoryEntity extends BaseEntity {
// JSON & DDL 컬럼
private String dataSetVersion;
private String companyStatus;
private String effectiveDate; // DDL: bpchar(8) 또는 varchar(8)
private String lrno;
private String sequence;
private String shipManager;
private String shipManagerCode;
// DB 관리 컬럼 (shipresultindex, vesselid, rowindex) Entity에서 제거됨
}

파일 보기

@ -0,0 +1,19 @@
package com.snp.batch.jobs.shipdetail.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;
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class SisterShipLinksEntity extends BaseEntity {
private String dataSetVersion;
private String lrno;
private String linkedLRNO;
}

파일 보기

@ -0,0 +1,21 @@
package com.snp.batch.jobs.shipdetail.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;
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class SpecialFeatureEntity extends BaseEntity {
private String dataSetVersion;
private String lrno;
private String sequence;
private String specialFeature;
private String specialFeatureCode;
}

파일 보기

@ -0,0 +1,22 @@
package com.snp.batch.jobs.shipdetail.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;
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class StatusHistoryEntity extends BaseEntity {
private String dataSetVersion;
private String lrno;
private String sequence;
private String status;
private String statusCode;
private String statusDate;
}

파일 보기

@ -0,0 +1,28 @@
package com.snp.batch.jobs.shipdetail.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;
// BaseEntity는 프로젝트에 정의되어 있어야 합니다.
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class StowageCommodityEntity extends BaseEntity {
// JSON & DDL 컬럼
private String dataSetVersion; // varchar(5)
private String commodityCode; // varchar(5)
private String commodityDecode; // varchar(50)
private String lrno; // varchar(7)
private String sequence; // varchar(2)
private String stowageCode; // varchar(2)
private String stowageDecode; // varchar(50)
// DB 관리 컬럼 (shipresultindex, vesselid, rowindex) Entity에서 제거됨
}

파일 보기

@ -0,0 +1,25 @@
package com.snp.batch.jobs.shipdetail.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;
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class SurveyDatesHistoryEntity extends BaseEntity {
private String dataSetVersion;
private String annualSurvey;
private String classSociety;
private String classSocietyCode;
private String continuousMachinerySurvey;
private String dockingSurvey;
private String lrno;
private String specialSurvey;
private String tailShaftSurvey;
}

파일 보기

@ -0,0 +1,22 @@
package com.snp.batch.jobs.shipdetail.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;
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class SurveyDatesHistoryUniqueEntity extends BaseEntity {
private String dataSetVersion;
private String lrno;
private String classSocietyCode;
private String surveyDate;
private String surveyType;
private String classSociety;
}

파일 보기

@ -0,0 +1,22 @@
package com.snp.batch.jobs.shipdetail.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;
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class TechnicalManagerHistoryEntity extends BaseEntity {
private String dataSetVersion;
private String companyStatus;
private String effectiveDate;
private String lrno;
private String sequence;
private String technicalManager;
private String technicalManagerCode;
}

파일 보기

@ -0,0 +1,26 @@
package com.snp.batch.jobs.shipdetail.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;
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class ThrustersEntity extends BaseEntity {
private String dataSetVersion;
private String lrno;
private String sequence;
private String thrusterType;
private String thrusterTypeCode;
private String numberOfThrusters;
private String thrusterPosition;
private String thrusterBHP;
private String thrusterKW;
private String typeOfInstallation;
}

파일 보기

@ -1,13 +1,8 @@
package com.snp.batch.jobs.shipdetail.batch.processor;
import com.snp.batch.common.batch.processor.BaseProcessor;
import com.snp.batch.jobs.shipdetail.batch.dto.OwnerHistoryDto;
import com.snp.batch.jobs.shipdetail.batch.dto.ShipDetailComparisonData;
import com.snp.batch.jobs.shipdetail.batch.dto.ShipDetailDto;
import com.snp.batch.jobs.shipdetail.batch.dto.ShipDetailUpdate;
import com.snp.batch.jobs.shipdetail.batch.entity.OwnerHistoryEntity;
import com.snp.batch.jobs.shipdetail.batch.entity.ShipDetailEntity;
import com.snp.batch.jobs.shipdetail.batch.entity.ShipHashEntity;
import com.snp.batch.jobs.shipdetail.batch.dto.*;
import com.snp.batch.jobs.shipdetail.batch.entity.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@ -33,7 +28,7 @@ public class ShipDetailDataProcessor extends BaseProcessor<ShipDetailComparisonD
String previousHash = comparisonData.getPreviousMasterHash();
String currentHash = comparisonData.getCurrentMasterHash();
log.debug("선박 해시 비교 시작: imoNumber={}, DB Hash={}, API Hash={}", imo, previousHash, currentHash);
log.info("선박 해시 비교 시작: imoNumber={}, DB Hash={}, API Hash={}", imo, previousHash, currentHash);
// 1. 해시 비교 (변경 감지)
if (isChanged(previousHash, currentHash)) {
@ -42,13 +37,28 @@ public class ShipDetailDataProcessor extends BaseProcessor<ShipDetailComparisonD
// 2. 증분 데이터 추출 (Map 기반으로 업데이트 데이터 구성)
// Core20
ShipDetailEntity shipDetailEntity = makeShipDetailEntity(comparisonData.getStructuredDto());
// OwnerHistory
List<OwnerHistoryEntity> ownerHistoryEntityList = makeOwnerHistoryEntityList(comparisonData.getStructuredDto().getOwnerHistory());
log.info("선박 데이터: shipDetailEntity={}", shipDetailEntity.toString());
log.info("선박 데이터: ownerHistoryEntityList={}", ownerHistoryEntityList.toString());
List<CrewListEntity> crewListEntityList = makeCrewListEntityList(comparisonData.getStructuredDto().getCrewList());
List<StowageCommodityEntity> stowageCommodityEntityList = makeStowageCommodityEntityList(comparisonData.getStructuredDto().getStowageCommodity());
List<GroupBeneficialOwnerHistoryEntity> groupBeneficialOwnerHistoryEntityList = makeGroupBeneficialOwnerHistoryEntityList(comparisonData.getStructuredDto().getGroupBeneficialOwnerHistory());
List<ShipManagerHistoryEntity> shipManagerHistoryEntityList = makeShipManagerHistoryEntityList(comparisonData.getStructuredDto().getShipManagerHistory());
List<OperatorHistoryEntity> operatorHistoryEntityList = makeOperatorHistoryEntityList(comparisonData.getStructuredDto().getOperatorHistory());
List<TechnicalManagerHistoryEntity> technicalManagerHistoryEntityList = makeTechnicalManagerHistoryEntityList(comparisonData.getStructuredDto().getTechnicalManagerHistory());
List<BareBoatCharterHistoryEntity> bareBoatCharterHistoryEntityList = makeBareBoatCharterHistoryEntityList(comparisonData.getStructuredDto().getBareBoatCharterHistory());
List<NameHistoryEntity> nameHistoryEntityList = makeNameHistoryEntityList(comparisonData.getStructuredDto().getNameHistory());
List<FlagHistoryEntity> flagHistoryEntityList = makeFlagHistoryEntityList(comparisonData.getStructuredDto().getFlagHistory());
List<AdditionalInformationEntity> additionalInformationEntityList = makeAdditionalInformationEntityList(comparisonData.getStructuredDto().getAdditionalInformation());
List<PandIHistoryEntity> pandIHistoryEntityList = makePandIHistoryEntityList(comparisonData.getStructuredDto().getPandIHistory());
List<CallSignAndMmsiHistoryEntity> callSignAndMmsiHistoryEntityList = makeCallSignAndMmsiHistoryEntityList(comparisonData.getStructuredDto().getCallSignAndMmsiHistory());
List<IceClassEntity> iceClassEntityList = makeIceClassEntityList(comparisonData.getStructuredDto().getIceClass());
List<SafetyManagementCertificateHistoryEntity> safetyManagementCertificateHistoryEntityList = makeSafetyManagementCertificateHistoryEntityList(comparisonData.getStructuredDto().getSafetyManagementCertificateHistory());
List<ClassHistoryEntity> classHistoryEntityList = makeClassHistoryEntityList(comparisonData.getStructuredDto().getClassHistory());
List<SurveyDatesHistoryEntity> surveyDatesHistoryEntityList = makeSurveyDatesHistoryEntityList(comparisonData.getStructuredDto().getSurveyDatesHistory());
List<SurveyDatesHistoryUniqueEntity> surveyDatesHistoryUniqueEntityList = makeSurveyDatesHistoryUniqueEntityList(comparisonData.getStructuredDto().getSurveyDatesHistoryUnique());
List<SisterShipLinksEntity> sisterShipLinksEntityList = makeSisterShipLinksEntityList(comparisonData.getStructuredDto().getSisterShipLinks());
List<StatusHistoryEntity> statusHistoryEntityList = makeStatusHistoryEntityList(comparisonData.getStructuredDto().getStatusHistory());
List<SpecialFeatureEntity> specialFeatureEntityList = makeSpecialFeatureEntityList(comparisonData.getStructuredDto().getSpecialFeature());
List<ThrustersEntity> thrustersEntityList = makeThrustersEntityList(comparisonData.getStructuredDto().getThrusters());
// 3. 최종 업데이트 DTO 생성 (Writer에 전달)
return ShipDetailUpdate.builder()
@ -57,6 +67,27 @@ public class ShipDetailDataProcessor extends BaseProcessor<ShipDetailComparisonD
.currentMasterMap(comparisonData.getCurrentMasterMap()) // DB JSONB 컬럼 업데이트용 Map
.shipDetailEntity(shipDetailEntity)
.ownerHistoryEntityList(ownerHistoryEntityList)
.crewListEntityList(crewListEntityList)
.stowageCommodityEntityList(stowageCommodityEntityList)
.groupBeneficialOwnerHistoryEntityList(groupBeneficialOwnerHistoryEntityList)
.shipManagerHistoryEntityList(shipManagerHistoryEntityList)
.operatorHistoryEntityList(operatorHistoryEntityList)
.technicalManagerHistoryEntityList(technicalManagerHistoryEntityList)
.bareBoatCharterHistoryEntityList(bareBoatCharterHistoryEntityList)
.nameHistoryEntityList(nameHistoryEntityList)
.flagHistoryEntityList(flagHistoryEntityList)
.additionalInformationEntityList(additionalInformationEntityList)
.pandIHistoryEntityList(pandIHistoryEntityList)
.callSignAndMmsiHistoryEntityList(callSignAndMmsiHistoryEntityList)
.iceClassEntityList(iceClassEntityList)
.safetyManagementCertificateHistoryEntityList(safetyManagementCertificateHistoryEntityList)
.classHistoryEntityList(classHistoryEntityList)
.surveyDatesHistoryEntityList(surveyDatesHistoryEntityList)
.surveyDatesHistoryUniqueEntityList(surveyDatesHistoryUniqueEntityList)
.sisterShipLinksEntityList(sisterShipLinksEntityList)
.statusHistoryEntityList(statusHistoryEntityList)
.specialFeatureEntityList(specialFeatureEntityList)
.thrustersEntityList(thrustersEntityList)
.shipHashEntity(makeShipHashEntity(imo, currentHash))
.build();
}
@ -127,7 +158,502 @@ public class ShipDetailDataProcessor extends BaseProcessor<ShipDetailComparisonD
return ownerHistoryEntityList;
}
private List<CrewListEntity> makeCrewListEntityList(List<CrewListDto> dtoList){
List<CrewListEntity> crewListEntityList = new ArrayList<>();
if (dtoList == null || dtoList.isEmpty()) {
return crewListEntityList; // 리스트 또는 null 입력 리스트 반환
}
for (CrewListDto dto : dtoList) {
String datasetVersion = null;
if (dto.getDataSetVersion() != null) {
datasetVersion = dto.getDataSetVersion().getDataSetVersion();
}
// superBuilder() 대신 builder() 사용
CrewListEntity entity = CrewListEntity.builder()
.dataSetVersion(safeGetString(datasetVersion))
.id(safeGetString(dto.getId()))
.lrno(safeGetString(dto.getLrno()))
.shipname(safeGetString(dto.getShipname()))
.crewlistdate(safeGetString(dto.getCrewListDate()))
.nationality(safeGetString(dto.getNationality()))
.totalcrew(safeGetString(dto.getTotalCrew()))
.totalratings(safeGetString(dto.getTotalRatings()))
.totalofficers(safeGetString(dto.getTotalOfficers()))
.totalcadets(safeGetString(dto.getTotalCadets()))
.totaltrainees(safeGetString(dto.getTotalTrainees()))
.totalridingsquad(safeGetString(dto.getTotalRidingSquad()))
.totalundeclared(safeGetString(dto.getTotalUndeclared()))
// DB에서 관리되는 컬럼 임시 셋팅 로직 제거
.build();
crewListEntityList.add(entity);
}
return crewListEntityList;
}
private List<StowageCommodityEntity> makeStowageCommodityEntityList(List<StowageCommodityDto> dtoList){
List<StowageCommodityEntity> entityList = new ArrayList<>();
if (dtoList == null || dtoList.isEmpty()) {
return entityList; // 리스트 또는 null 입력 리스트 반환
}
for (StowageCommodityDto dto : dtoList) {
String datasetVersion = null;
if (dto.getDataSetVersion() != null) {
datasetVersion = dto.getDataSetVersion().getDataSetVersion();
}
// builder() 사용 DB 관리 컬럼 셋팅 로직 제거
StowageCommodityEntity entity = StowageCommodityEntity.builder()
.dataSetVersion(safeGetString(datasetVersion))
.commodityCode(safeGetString(dto.getCommodityCode()))
.commodityDecode(safeGetString(dto.getCommodityDecode()))
.lrno(safeGetString(dto.getLrno()))
.sequence(safeGetString(dto.getSequence()))
.stowageCode(safeGetString(dto.getStowageCode()))
.stowageDecode(safeGetString(dto.getStowageDecode()))
.build();
entityList.add(entity);
}
return entityList;
}
private List<GroupBeneficialOwnerHistoryEntity> makeGroupBeneficialOwnerHistoryEntityList(List<GroupBeneficialOwnerHistoryDto> dtoList){
List<GroupBeneficialOwnerHistoryEntity> entityList = new ArrayList<>();
if (dtoList == null || dtoList.isEmpty()) {
return entityList; // 리스트 또는 null 입력 리스트 반환
}
for (GroupBeneficialOwnerHistoryDto dto : dtoList) {
String datasetVersion = null;
if (dto.getDataSetVersion() != null) {
datasetVersion = dto.getDataSetVersion().getDataSetVersion();
}
// builder() 사용 DB 관리 컬럼 셋팅 로직 제거
GroupBeneficialOwnerHistoryEntity entity = GroupBeneficialOwnerHistoryEntity.builder()
.dataSetVersion(safeGetString(datasetVersion))
.companyStatus(safeGetString(dto.getCompanyStatus()))
.effectiveDate(safeGetString(dto.getEffectiveDate()))
.groupBeneficialOwner(safeGetString(dto.getGroupBeneficialOwner()))
.groupBeneficialOwnerCode(safeGetString(dto.getGroupBeneficialOwnerCode()))
.lrno(safeGetString(dto.getLrno()))
.sequence(safeGetString(dto.getSequence()))
.build();
entityList.add(entity);
}
return entityList;
}
private List<ShipManagerHistoryEntity> makeShipManagerHistoryEntityList(List<ShipManagerHistoryDto> dtoList){
List<ShipManagerHistoryEntity> entityList = new ArrayList<>();
if (dtoList == null || dtoList.isEmpty()) {
return entityList; // 리스트 또는 null 입력 리스트 반환
}
for (ShipManagerHistoryDto dto : dtoList) {
String datasetVersion = null;
if (dto.getDataSetVersion() != null) {
datasetVersion = dto.getDataSetVersion().getDataSetVersion();
}
// builder() 사용 DB 관리 컬럼 셋팅 로직 제거
ShipManagerHistoryEntity entity = ShipManagerHistoryEntity.builder()
.dataSetVersion(safeGetString(datasetVersion))
.companyStatus(safeGetString(dto.getCompanyStatus()))
.effectiveDate(safeGetString(dto.getEffectiveDate()))
.lrno(safeGetString(dto.getLrno()))
.sequence(safeGetString(dto.getSequence()))
.shipManager(safeGetString(dto.getShipManager()))
.shipManagerCode(safeGetString(dto.getShipManagerCode()))
.build();
entityList.add(entity);
}
return entityList;
}
private List<OperatorHistoryEntity> makeOperatorHistoryEntityList(List<OperatorHistoryDto> dtoList){
List<OperatorHistoryEntity> entityList = new ArrayList<>();
if (dtoList == null || dtoList.isEmpty()) {
return entityList;
}
for (OperatorHistoryDto dto : dtoList) {
String datasetVersion = null;
if (dto.getDataSetVersion() != null) {
datasetVersion = dto.getDataSetVersion().getDataSetVersion();
}
// builder() 사용 DB 관리 컬럼 셋팅 로직 제거
OperatorHistoryEntity entity = OperatorHistoryEntity.builder()
.dataSetVersion(safeGetString(datasetVersion))
.companyStatus(safeGetString(dto.getCompanyStatus()))
.effectiveDate(safeGetString(dto.getEffectiveDate()))
.lrno(safeGetString(dto.getLrno()))
.operator(safeGetString(dto.getOperator()))
.operatorCode(safeGetString(dto.getOperatorCode()))
.sequence(safeGetString(dto.getSequence()))
.build();
entityList.add(entity);
}
return entityList;
}
private List<TechnicalManagerHistoryEntity> makeTechnicalManagerHistoryEntityList(List<TechnicalManagerHistoryDto> dtoList){
List<TechnicalManagerHistoryEntity> entityList = new ArrayList<>();
if (dtoList == null || dtoList.isEmpty()) {
return entityList;
}
for (TechnicalManagerHistoryDto dto : dtoList) {
String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null;
TechnicalManagerHistoryEntity entity = TechnicalManagerHistoryEntity.builder()
.dataSetVersion(safeGetString(datasetVersion))
.companyStatus(safeGetString(dto.getCompanyStatus()))
.effectiveDate(safeGetString(dto.getEffectiveDate()))
.lrno(safeGetString(dto.getLrno()))
.sequence(safeGetString(dto.getSequence()))
.technicalManager(safeGetString(dto.getTechnicalManager()))
.technicalManagerCode(safeGetString(dto.getTechnicalManagerCode()))
.build();
entityList.add(entity);
}
return entityList;
}
private List<BareBoatCharterHistoryEntity> makeBareBoatCharterHistoryEntityList(List<BareBoatCharterHistoryDto> dtoList){
List<BareBoatCharterHistoryEntity> entityList = new ArrayList<>();
if (dtoList == null || dtoList.isEmpty()) {
return entityList;
}
for (BareBoatCharterHistoryDto dto : dtoList) {
String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null;
BareBoatCharterHistoryEntity entity = BareBoatCharterHistoryEntity.builder()
.dataSetVersion(safeGetString(datasetVersion))
.lrno(safeGetString(dto.getLrno()))
.sequence(safeGetString(dto.getSequence()))
.effectiveDate(safeGetString(dto.getEffectiveDate()))
.bbChartererCode(safeGetString(dto.getBbChartererCode()))
.bbCharterer(safeGetString(dto.getBbCharterer()))
.build();
entityList.add(entity);
}
return entityList;
}
private List<NameHistoryEntity> makeNameHistoryEntityList(List<NameHistoryDto> dtoList){
List<NameHistoryEntity> entityList = new ArrayList<>();
if (dtoList == null || dtoList.isEmpty()) {
return entityList;
}
for (NameHistoryDto dto : dtoList) {
String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null;
NameHistoryEntity entity = NameHistoryEntity.builder()
.dataSetVersion(safeGetString(datasetVersion))
.effectiveDate(safeGetString(dto.getEffectiveDate()))
.lrno(safeGetString(dto.getLrno()))
.sequence(safeGetString(dto.getSequence()))
.vesselName(safeGetString(dto.getVesselName()))
.build();
entityList.add(entity);
}
return entityList;
}
private List<FlagHistoryEntity> makeFlagHistoryEntityList(List<FlagHistoryDto> dtoList){
List<FlagHistoryEntity> entityList = new ArrayList<>();
if (dtoList == null || dtoList.isEmpty()) {
return entityList;
}
for (FlagHistoryDto dto : dtoList) {
String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null;
FlagHistoryEntity entity = FlagHistoryEntity.builder()
.dataSetVersion(safeGetString(datasetVersion))
.effectiveDate(safeGetString(dto.getEffectiveDate()))
.flag(safeGetString(dto.getFlag()))
.flagCode(safeGetString(dto.getFlagCode()))
.lrno(safeGetString(dto.getLrno()))
.sequence(safeGetString(dto.getSequence()))
.build();
entityList.add(entity);
}
return entityList;
}
private List<AdditionalInformationEntity> makeAdditionalInformationEntityList(List<AdditionalInformationDto> dtoList){
List<AdditionalInformationEntity> entityList = new ArrayList<>();
if (dtoList == null || dtoList.isEmpty()) {
return entityList;
}
for (AdditionalInformationDto dto : dtoList) {
// DataSetVersion은 DB 스키마에 없으므로 Entity에 매핑하지 않음
AdditionalInformationEntity entity = AdditionalInformationEntity.builder()
.lrno(safeGetString(dto.getLrno()))
.shipemail(safeGetString(dto.getShipEmail()))
.waterdepthmax(safeGetString(dto.getWaterDepthMax()))
.drilldepthmax(safeGetString(dto.getDrillDepthMax()))
.drillbargeind(safeGetString(dto.getDrillBargeInd()))
.productionvesselind(safeGetString(dto.getProductionVesselInd()))
.deckheatexchangerind(safeGetString(dto.getDeckHeatExchangerInd()))
.deckheatexchangermaterial(safeGetString(dto.getDeckHeatExchangerMaterial()))
.tweendeckportable(safeGetString(dto.getTweenDeckPortable()))
.tweendeckfixed(safeGetString(dto.getTweenDeckFixed()))
.satcomid(safeGetString(dto.getSatComID()))
.satcomansback(safeGetString(dto.getSatComAnsBack()))
.build();
entityList.add(entity);
}
return entityList;
}
private List<PandIHistoryEntity> makePandIHistoryEntityList(List<PandIHistoryDto> dtoList){
List<PandIHistoryEntity> entityList = new ArrayList<>();
if (dtoList == null || dtoList.isEmpty()) {
return entityList;
}
for (PandIHistoryDto dto : dtoList) {
String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null;
PandIHistoryEntity entity = PandIHistoryEntity.builder()
.dataSetVersion(safeGetString(datasetVersion))
.lrno(safeGetString(dto.getLrno()))
.sequence(safeGetString(dto.getSequence()))
.pandiclubcode(safeGetString(dto.getPandIClubCode()))
.pandiclubdecode(safeGetString(dto.getPandIClubDecode()))
.effectiveDate(safeGetString(dto.getEffectiveDate()))
.source(safeGetString(dto.getSource()))
.build();
entityList.add(entity);
}
return entityList;
}
private List<CallSignAndMmsiHistoryEntity> makeCallSignAndMmsiHistoryEntityList(List<CallSignAndMmsiHistoryDto> dtoList){
List<CallSignAndMmsiHistoryEntity> entityList = new ArrayList<>();
if (dtoList == null || dtoList.isEmpty()) {
return entityList;
}
for (CallSignAndMmsiHistoryDto dto : dtoList) {
String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null;
CallSignAndMmsiHistoryEntity entity = CallSignAndMmsiHistoryEntity.builder()
.dataSetVersion(safeGetString(datasetVersion))
.lrno(safeGetString(dto.getLrno()))
.sequence(safeGetString(dto.getSeqNo())) // SeqNo -> sequence
.callsign(safeGetString(dto.getCallSign()))
.mmsi(dto.getMmsi()) // JSON에 MMSI 필드가 없으므로 null 또는 기본값 (필요 수정)
.effectiveDate(safeGetString(dto.getEffectiveDate()))
.build();
entityList.add(entity);
}
return entityList;
}
private List<IceClassEntity> makeIceClassEntityList(List<IceClassDto> dtoList){
List<IceClassEntity> entityList = new ArrayList<>();
if (dtoList == null || dtoList.isEmpty()) {
return entityList;
}
for (IceClassDto dto : dtoList) {
String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null;
IceClassEntity entity = IceClassEntity.builder()
.dataSetVersion(safeGetString(datasetVersion))
.iceClass(safeGetString(dto.getIceClass()))
.iceClassCode(safeGetString(dto.getIceClassCode()))
.lrno(safeGetString(dto.getLrno()))
.build();
entityList.add(entity);
}
return entityList;
}
private List<SafetyManagementCertificateHistoryEntity> makeSafetyManagementCertificateHistoryEntityList(List<SafetyManagementCertificateHistoryDto> dtoList){
List<SafetyManagementCertificateHistoryEntity> entityList = new ArrayList<>();
if (dtoList == null || dtoList.isEmpty()) {
return entityList;
}
for (SafetyManagementCertificateHistoryDto dto : dtoList) {
String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null;
SafetyManagementCertificateHistoryEntity entity = SafetyManagementCertificateHistoryEntity.builder()
.dataSetVersion(safeGetString(datasetVersion))
.lrno(safeGetString(dto.getLrno()))
.safetyManagementCertificateAuditor(safeGetString(dto.getSafetyManagementCertificateAuditor()))
.safetyManagementCertificateConventionOrVol(safeGetString(dto.getSafetyManagementCertificateConventionOrVol()))
.safetyManagementCertificateDateExpires(safeGetString(dto.getSafetyManagementCertificateDateExpires()))
.safetyManagementCertificateDateIssued(safeGetString(dto.getSafetyManagementCertificateDateIssued()))
.safetyManagementCertificateDOCCompany(safeGetString(dto.getSafetyManagementCertificateDOCCompany()))
.safetyManagementCertificateFlag(safeGetString(dto.getSafetyManagementCertificateFlag()))
.safetyManagementCertificateIssuer(safeGetString(dto.getSafetyManagementCertificateIssuer()))
.safetyManagementCertificateOtherDescription(safeGetString(dto.getSafetyManagementCertificateOtherDescription()))
.safetyManagementCertificateShipName(safeGetString(dto.getSafetyManagementCertificateShipName()))
.safetyManagementCertificateShipType(safeGetString(dto.getSafetyManagementCertificateShipType()))
.safetyManagementCertificateSource(safeGetString(dto.getSafetyManagementCertificateSource()))
.safetyManagementCertificateCompanyCode(safeGetString(dto.getSafetyManagementCertificateCompanyCode()))
.sequence(safeGetString(dto.getSequence()))
.build();
entityList.add(entity);
}
return entityList;
}
private List<ClassHistoryEntity> makeClassHistoryEntityList(List<ClassHistoryDto> dtoList){
List<ClassHistoryEntity> entityList = new ArrayList<>();
if (dtoList == null || dtoList.isEmpty()) {
return entityList;
}
for (ClassHistoryDto dto : dtoList) {
String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null;
ClassHistoryEntity entity = ClassHistoryEntity.builder()
.dataSetVersion(safeGetString(datasetVersion))
._class(safeGetString(dto.get_class()))
.classCode(safeGetString(dto.getClassCode()))
.classIndicator(safeGetString(dto.getClassIndicator()))
.classID(safeGetString(dto.getClassID()))
.currentIndicator(safeGetString(dto.getCurrentIndicator()))
.effectiveDate(safeGetString(dto.getEffectiveDate()))
.lrno(safeGetString(dto.getLrno()))
.sequence(safeGetString(dto.getSequence()))
.build();
entityList.add(entity);
}
return entityList;
}
private List<SurveyDatesHistoryEntity> makeSurveyDatesHistoryEntityList(List<SurveyDatesHistoryDto> dtoList){
List<SurveyDatesHistoryEntity> entityList = new ArrayList<>();
if (dtoList == null || dtoList.isEmpty()) {
return entityList;
}
for (SurveyDatesHistoryDto dto : dtoList) {
String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null;
SurveyDatesHistoryEntity entity = SurveyDatesHistoryEntity.builder()
.dataSetVersion(safeGetString(datasetVersion))
.annualSurvey(safeGetString(dto.getAnnualSurvey()))
.classSociety(safeGetString(dto.getClassSociety()))
.classSocietyCode(safeGetString(dto.getClassSocietyCode()))
.continuousMachinerySurvey(safeGetString(dto.getContinuousMachinerySurvey()))
.dockingSurvey(safeGetString(dto.getDockingSurvey()))
.lrno(safeGetString(dto.getLrno()))
.specialSurvey(safeGetString(dto.getSpecialSurvey()))
.tailShaftSurvey(safeGetString(dto.getTailShaftSurvey()))
.build();
entityList.add(entity);
}
return entityList;
}
private List<SurveyDatesHistoryUniqueEntity> makeSurveyDatesHistoryUniqueEntityList(List<SurveyDatesHistoryUniqueDto> dtoList){
List<SurveyDatesHistoryUniqueEntity> entityList = new ArrayList<>();
if (dtoList == null || dtoList.isEmpty()) {
return entityList;
}
for (SurveyDatesHistoryUniqueDto dto : dtoList) {
String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null;
SurveyDatesHistoryUniqueEntity entity = SurveyDatesHistoryUniqueEntity.builder()
.dataSetVersion(safeGetString(datasetVersion))
.lrno(safeGetString(dto.getLrno()))
.classSocietyCode(safeGetString(dto.getClassSocietyCode()))
.surveyDate(safeGetString(dto.getSurveyDate()))
.surveyType(safeGetString(dto.getSurveyType()))
.classSociety(safeGetString(dto.getClassSociety()))
.build();
entityList.add(entity);
}
return entityList;
}
private List<SisterShipLinksEntity> makeSisterShipLinksEntityList(List<SisterShipLinksDto> dtoList){
List<SisterShipLinksEntity> entityList = new ArrayList<>();
if (dtoList == null || dtoList.isEmpty()) {
return entityList;
}
for (SisterShipLinksDto dto : dtoList) {
String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null;
SisterShipLinksEntity entity = SisterShipLinksEntity.builder()
.dataSetVersion(safeGetString(datasetVersion))
.lrno(safeGetString(dto.getLrno()))
.linkedLRNO(safeGetString(dto.getLinkedLRNO()))
.build();
entityList.add(entity);
}
return entityList;
}
private List<StatusHistoryEntity> makeStatusHistoryEntityList(List<StatusHistoryDto> dtoList){
List<StatusHistoryEntity> entityList = new ArrayList<>();
if (dtoList == null || dtoList.isEmpty()) {
return entityList;
}
for (StatusHistoryDto dto : dtoList) {
String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null;
StatusHistoryEntity entity = StatusHistoryEntity.builder()
.dataSetVersion(safeGetString(datasetVersion))
.lrno(safeGetString(dto.getLrno()))
.sequence(safeGetString(dto.getSequence()))
.status(safeGetString(dto.getStatus()))
.statusCode(safeGetString(dto.getStatusCode()))
.statusDate(safeGetString(dto.getStatusDate()))
.build();
entityList.add(entity);
}
return entityList;
}
private List<SpecialFeatureEntity> makeSpecialFeatureEntityList(List<SpecialFeatureDto> dtoList){
List<SpecialFeatureEntity> entityList = new ArrayList<>();
if (dtoList == null || dtoList.isEmpty()) {
return entityList;
}
for (SpecialFeatureDto dto : dtoList) {
String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null;
SpecialFeatureEntity entity = SpecialFeatureEntity.builder()
.dataSetVersion(safeGetString(datasetVersion))
.lrno(safeGetString(dto.getLrno()))
.sequence(safeGetString(dto.getSequence()))
.specialFeature(safeGetString(dto.getSpecialFeature()))
.specialFeatureCode(safeGetString(dto.getSpecialFeatureCode()))
.build();
entityList.add(entity);
}
return entityList;
}
private List<ThrustersEntity> makeThrustersEntityList(List<ThrustersDto> dtoList){
List<ThrustersEntity> entityList = new ArrayList<>();
if (dtoList == null || dtoList.isEmpty()) {
return entityList;
}
for (ThrustersDto dto : dtoList) {
String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null;
ThrustersEntity entity = ThrustersEntity.builder()
.dataSetVersion(safeGetString(datasetVersion))
.lrno(safeGetString(dto.getLrno()))
.sequence(safeGetString(dto.getSequence()))
.thrusterType(safeGetString(dto.getThrusterType()))
.thrusterTypeCode(safeGetString(dto.getThrusterTypeCode()))
.numberOfThrusters(safeGetString(dto.getNumberOfThrusters()))
.thrusterPosition(safeGetString(dto.getThrusterPosition()))
.thrusterBHP(safeGetString(dto.getThrusterBHP()))
.thrusterKW(safeGetString(dto.getThrusterKW()))
.typeOfInstallation(safeGetString(dto.getTypeOfInstallation()))
.build();
entityList.add(entity);
}
return entityList;
}
/**
* 해시값을 비교하여 변경 여부를 판단합니다.

파일 보기

@ -0,0 +1,309 @@
package com.snp.batch.jobs.shipdetail.batch.reader;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.snp.batch.common.batch.reader.BaseApiReader;
import com.snp.batch.common.util.JsonChangeDetector;
import com.snp.batch.jobs.shipdetail.batch.dto.*;
import com.snp.batch.service.BatchDateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.reactive.function.client.WebClient;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;
/**
* 선박 상세 정보 Reader (v2.0 - Chunk 기반)
*
* 기능:
* 1. ship_data 테이블에서 IMO 번호 전체 조회 (최초 1회)
* 2. IMO 번호를 100개씩 분할하여 배치 단위로 처리
* 3. fetchNextBatch() 호출 시마다 100개씩 API 호출
* 4. Spring Batch가 100건씩 Process Write 수행
*
* Chunk 처리 흐름:
* - beforeFetch() IMO 전체 조회 (1회)
* - fetchNextBatch() 100개 IMO로 API 호출 (1,718회)
* - read() 1건씩 반환 (100번)
* - Processor/Writer 100건 처리
* - 반복... (1,718번의 Chunk)
*
* 기존 방식과의 차이:
* - 기존: 17만건 전체 메모리 로드 Process Write
* - 신규: 100건씩 로드 Process Write (Chunk 1,718회)
*/
@Slf4j
public class ShipDetailUpdateDataReader extends BaseApiReader<ShipDetailComparisonData> {
private final JdbcTemplate jdbcTemplate;
private final ObjectMapper objectMapper;
private final BatchDateService batchDateService; // BatchDateService 필드 추가
// 배치 처리 상태
private List<String> allImoNumbers;
// DB 해시값을 저장할
private Map<String, String> dbMasterHashes;
private int currentBatchIndex = 0;
private final int batchSize = 30;
public ShipDetailUpdateDataReader(WebClient webClient, JdbcTemplate jdbcTemplate, ObjectMapper objectMapper,BatchDateService batchDateService) {
super(webClient);
this.jdbcTemplate = jdbcTemplate;
this.objectMapper = objectMapper;
this.batchDateService = batchDateService;
enableChunkMode(); // Chunk 모드 활성화
}
@Override
protected String getReaderName() {
return "ShipDetailUpdateDataReader";
}
@Override
protected void resetCustomState() {
this.currentBatchIndex = 0;
this.allImoNumbers = null;
this.dbMasterHashes = null;
}
@Override
protected String getApiPath() {
return "/MaritimeWCF/APSShipService.svc/RESTFul/GetShipsByIHSLRorIMONumbersAll";
}
protected String getShipUpdateApiPath(){
return "MaritimeWCF/APSShipService.svc/RESTFul/GetShipChangesByLastUpdateDateRange";
}
private static final String FETCH_ALL_HASHES_QUERY =
"SELECT imo_number, ship_detail_hash FROM snp_data.ship_detail_hash_json ORDER BY imo_number";
/**
* 최초 1회만 실행: ship_data 테이블에서 IMO 번호 전체 조회
*/
@Override
protected void beforeFetch() {
// 전처리 과정
// 💡 Step 1. 기간 변경된 IMO 번호 리스트 조회
log.info("[{}] 변경된 IMO 번호 조회 시작...", getReaderName());
ShipUpdateApiResponse response = callShipUpdateApi();
allImoNumbers = extractUpdateImoNumbers(response);
log.info("[{}] 변경된 IMO 번호 수: {} 개", getReaderName(), response.getShipCount());
int totalBatches = (int) Math.ceil((double) allImoNumbers.size() / batchSize);
log.info("[{}] 총 {} 개의 변경된 IMO 번호 조회 완료", getReaderName(), allImoNumbers.size());
log.info("[{}] {}개씩 배치로 분할하여 API 호출 예정", getReaderName(), batchSize);
log.info("[{}] 예상 배치 수: {} 개", getReaderName(), totalBatches);
// Step 2. 배치 결과 imo_number, ship_detail_json, ship_detail_hash 데이터 전체 조회
log.info("[{}] DB Master Hash 전체 조회 시작...", getReaderName());
// 1-1. DB에서 모든 IMO와 Hash 조회
dbMasterHashes = jdbcTemplate.query(FETCH_ALL_HASHES_QUERY, rs -> {
Map<String, String> map = new HashMap<>();
while (rs.next()) {
map.put(rs.getString("imo_number"), rs.getString("ship_detail_hash"));
}
return map;
});
log.info("[{}] DB Master Hash 조회 완료. 총 {}건.", getReaderName(), dbMasterHashes.size());
// API 통계 초기화
updateApiCallStats(totalBatches, 0);
}
/**
* Chunk 기반 핵심 메서드: 다음 100개 배치를 조회하여 반환
*
* Spring Batch가 100건씩 read() 호출 완료 메서드 재호출
*
* @return 다음 배치 100건 ( 이상 없으면 null)
*/
@Override
protected List<ShipDetailComparisonData> fetchNextBatch() throws Exception {
// 모든 배치 처리 완료 확인
if (allImoNumbers == null || currentBatchIndex >= allImoNumbers.size()) {
return null; // Job 종료
}
// 현재 배치의 시작/ 인덱스 계산
int startIndex = currentBatchIndex;
int endIndex = Math.min(currentBatchIndex + batchSize, allImoNumbers.size());
// 현재 배치의 IMO 번호 추출 (100개)
List<String> currentBatch = allImoNumbers.subList(startIndex, endIndex);
int currentBatchNumber = (currentBatchIndex / batchSize) + 1;
int totalBatches = (int) Math.ceil((double) allImoNumbers.size() / batchSize);
log.info("[{}] 배치 {}/{} 처리 중 (IMO {} 개)...",
getReaderName(), currentBatchNumber, totalBatches, currentBatch.size());
try {
// IMO 번호를 쉼표로 연결 (: "1000019,1000021,1000033,...")
String imoParam = String.join(",", currentBatch);
// API 호출
ShipDetailApiResponse response = callApiWithBatch(imoParam);
// 다음 배치로 인덱스 이동
currentBatchIndex = endIndex;
List<ShipDetailComparisonData> comparisonList = new ArrayList<>();
// 응답 처리
if (response != null && response.getShipResult() != null) {
// ShipDetailComparisonData 생성
for (ShipResultDto shipResult : response.getShipResult()) {
JsonNode rawShipDetailNode = shipResult.getShipDetailNode();
if (rawShipDetailNode == null) continue;
// 💡 1) DTO로 매핑하여 IMO와 구조화된 DTO 확보
ShipDetailDto structuredDto = null;
try {
structuredDto = objectMapper.treeToValue(rawShipDetailNode, ShipDetailDto.class);
} catch (Exception e) {
log.error("ShipDetailDto 매핑 실패: {}", e.getMessage());
continue;
}
String imo = structuredDto.getIhslrorimoshipno();
if (imo == null || imo.isEmpty()) continue;
// 💡 2) 원본 JSON 문자열 생성
String originalJsonString = rawShipDetailNode.toString();
// 💡 3) API response json을 Map 형태로 변환, 정렬 해시 생성
Map<String, Object> currentMasterMap = JsonChangeDetector.jsonToSortedFilteredMap(originalJsonString);
String currentMasterHash = JsonChangeDetector.getSha256HashFromMap(currentMasterMap);
// 💡 4) DB Master Hash 조회 (beforeFetch에서 로드된 사용)
String previousMasterHash = dbMasterHashes.getOrDefault(imo, null);
// 💡 5) ShipDetailComparisonData DTO 생성
comparisonList.add(ShipDetailComparisonData.builder()
.imoNumber(imo)
.previousMasterHash(previousMasterHash)
.currentMasterMap(currentMasterMap)
.currentMasterHash(currentMasterHash)
.structuredDto(structuredDto)
.build());
}
log.info("[{}] 배치 {}/{} 완료: {} 건 조회",
getReaderName(), currentBatchNumber, totalBatches, comparisonList.size());
// API 호출 통계 업데이트
updateApiCallStats(totalBatches, currentBatchNumber);
// API 과부하 방지 (다음 배치 0.5초 대기)
if (currentBatchIndex < allImoNumbers.size()) {
Thread.sleep(500);
}
return comparisonList;
} else {
log.warn("[{}] 배치 {}/{} 응답 없음",
getReaderName(), currentBatchNumber, totalBatches);
// API 호출 통계 업데이트 (실패도 카운트)
updateApiCallStats(totalBatches, currentBatchNumber);
return Collections.emptyList();
}
} catch (Exception e) {
log.error("[{}] 배치 {}/{} 처리 중 오류: {}",
getReaderName(), currentBatchNumber, totalBatches, e.getMessage(), e);
// 오류 발생 시에도 다음 배치로 이동 (부분 실패 허용)
currentBatchIndex = endIndex;
// 리스트 반환 (Job 계속 진행)
return Collections.emptyList();
}
}
/**
* Query Parameter를 사용한 API 호출
*
* @param imoNumbers 쉼표로 연결된 IMO 번호 (: "1000019,1000021,...")
* @return API 응답
*/
private ShipDetailApiResponse callApiWithBatch(String imoNumbers) {
String url = getApiPath() + "?IMONumbers=" + imoNumbers;
log.info("[{}] API 호출: {}", getReaderName(), url);
return webClient.get()
.uri(url)
.retrieve()
.bodyToMono(ShipDetailApiResponse.class)
.block();
}
private ShipUpdateApiResponse callShipUpdateApi(){
// 1. BatchDateService를 통해 동적 날짜 파라미터 조회
Map<String, String> params = batchDateService.getShipUpdateApiDateParams();
String url = getShipUpdateApiPath();
log.info("[{}] API 호출: {}", getReaderName(), url);
return webClient.get()
.uri(url, uriBuilder -> uriBuilder
// 맵에서 파라미터 값을 동적으로 가져와 세팅
.queryParam("shipsCategory", params.get("shipsCategory"))
.queryParam("fromYear", params.get("fromYear"))
.queryParam("fromMonth", params.get("fromMonth"))
.queryParam("fromDay", params.get("fromDay"))
.queryParam("toYear", params.get("toYear"))
.queryParam("toMonth", params.get("toMonth"))
.queryParam("toDay", params.get("toDay"))
.build())
.retrieve()
.bodyToMono(ShipUpdateApiResponse.class)
.block();
}
private List<String> extractUpdateImoNumbers(ShipUpdateApiResponse response) {
if (response.getShips() == null) {
return Collections.emptyList();
}
return response.getShips() .stream()
// ShipDto 객체에서 imoNumber 필드 (String 타입) 추출
.map(ShipDto::getImoNumber)
// IMO 번호가 null이 아닌 경우만 필터링 (선택 사항이지만 안전성을 위해)
.filter(imoNumber -> imoNumber != null)
// 추출된 String imoNumber들을 List<String>으로 수집
.collect(Collectors.toList());
}
@Override
protected void afterFetch(List<ShipDetailComparisonData> data) {
int totalBatches = (int) Math.ceil((double) allImoNumbers.size() / batchSize);
try{
if (data == null) {
// 3. 배치 성공 상태 업데이트 (트랜잭션 커밋 직전에 실행)
LocalDate successDate = LocalDate.now(); // 현재 배치 실행 시점의 날짜 (Reader의 toDay와 동일한 )
batchDateService.updateLastSuccessDate(successDate);
log.info("batch_last_execution update 완료");
log.info("[{}] 전체 {} 개 배치 처리 완료", getReaderName(), totalBatches);
log.info("[{}] 총 {} 개의 IMO 번호에 대한 API 호출 종료",
getReaderName(), allImoNumbers.size());
}
}catch (Exception e){
log.info("[{}] 전체 {} 개 배치 처리 실패", getReaderName(), totalBatches);
log.info("[{}] 총 {} 개의 IMO 번호에 대한 API 호출 종료",
getReaderName(), allImoNumbers.size());
}
}
}

파일 보기

@ -1,7 +1,6 @@
package com.snp.batch.jobs.shipdetail.batch.repository;
import com.snp.batch.jobs.shipdetail.batch.entity.OwnerHistoryEntity;
import com.snp.batch.jobs.shipdetail.batch.entity.ShipDetailEntity;
import com.snp.batch.jobs.shipdetail.batch.entity.*;
import java.util.List;
@ -16,6 +15,69 @@ public interface ShipDetailRepository {
void saveAllOwnerHistoryData(List<OwnerHistoryEntity> entities);
//TODO : CrewList 저장 함수 선언
void saveAllCrewListData(List<CrewListEntity> entities);
//TODO : StowageCommodity 저장 함수 선언
void saveAllStowageCommodityData(List<StowageCommodityEntity> entities);
//TODO : GroupBeneficialOwnerHistory 저장 함수 선언
void saveAllGroupBeneficialOwnerHistoryData(List<GroupBeneficialOwnerHistoryEntity> entities);
//TODO : ShipManagerHistory 저장 함수 선언
void saveAllShipManagerHistoryData(List<ShipManagerHistoryEntity> entities);
//TODO : OperatorHistory 저장 함수 선언
void saveAllOperatorHistoryData(List<OperatorHistoryEntity> entities);
//TODO : TechnicalManagerHistory 저장 함수 선언
void saveAllTechnicalManagerHistoryData(List<TechnicalManagerHistoryEntity> entities);
//TODO : BareBoatCharterHistory 저장 함수 선언
void saveAllBareBoatCharterHistoryData(List<BareBoatCharterHistoryEntity> entities);
//TODO : NameHistory 저장 함수 선언
void saveAllNameHistoryData(List<NameHistoryEntity> entities);
//TODO : FlagHistory 저장 함수 선언
void saveAllFlagHistoryData(List<FlagHistoryEntity> entities);
//TODO : AdditionalInformation 저장 함수 선언
void saveAllAdditionalInformationData(List<AdditionalInformationEntity> entities);
//TODO : PandIHistory 저장 함수 선언
void saveAllPandIHistoryData(List<PandIHistoryEntity> entities);
//TODO : CallSignAndMmsiHistory 저장 함수 선언
void saveAllCallSignAndMmsiHistoryData(List<CallSignAndMmsiHistoryEntity> entities);
//TODO : IceClass 저장 함수 선언
void saveAllIceClassData(List<IceClassEntity> entities);
//TODO : SafetyManagementCertificateHistory 저장 함수 선언
void saveAllSafetyManagementCertificateHistoryData(List<SafetyManagementCertificateHistoryEntity> entities);
//TODO : ClassHistory 저장 함수 선언
void saveAllClassHistoryData(List<ClassHistoryEntity> entities);
//TODO : SurveyDatesHistory 저장 함수 선언
void saveAllSurveyDatesHistoryData(List<SurveyDatesHistoryEntity> entities);
//TODO : SurveyDatesHistoryUnique 저장 함수 선언
void saveAllSurveyDatesHistoryUniqueData(List<SurveyDatesHistoryUniqueEntity> entities);
//TODO : SisterShipLinks 저장 함수 선언
void saveAllSisterShipLinksData(List<SisterShipLinksEntity> entities);
//TODO : StatusHistory 저장 함수 선언
void saveAllStatusHistoryData(List<StatusHistoryEntity> entities);
//TODO : SpecialFeature 저장 함수 선언
void saveAllSpecialFeatureData(List<SpecialFeatureEntity> entities);
//TODO : Thrusters 저장 함수 선언
void saveAllThrustersData(List<ThrustersEntity> entities);
void delete(String id);
}

파일 보기

@ -1,8 +1,7 @@
package com.snp.batch.jobs.shipdetail.batch.repository;
import com.snp.batch.common.batch.repository.BaseJdbcRepository;
import com.snp.batch.jobs.shipdetail.batch.entity.OwnerHistoryEntity;
import com.snp.batch.jobs.shipdetail.batch.entity.ShipDetailEntity;
import com.snp.batch.jobs.shipdetail.batch.entity.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
@ -157,6 +156,21 @@ public class ShipDetailRepositoryImpl extends BaseJdbcRepository<ShipDetailEntit
}
}
private void setIntegerOrNull(PreparedStatement ps, int index, String value) throws Exception {
if (value == null || value.trim().isEmpty()) {
ps.setNull(index, java.sql.Types.INTEGER);
} else {
try {
int parsedValue = Integer.parseInt(value.trim());
ps.setInt(index, parsedValue);
} catch (NumberFormatException e) {
// 유효하지 않은 문자열이 들어왔을 경우, 데이터 로드 실패 대신 NULL 처리
System.err.println("Warning: Invalid integer format for index " + index + ". Value: '" + value + "'. Setting NULL.");
ps.setNull(index, java.sql.Types.INTEGER);
}
}
}
@Override
protected void setUpdateParameters(PreparedStatement ps, ShipDetailEntity entity) throws Exception {
int idx = 1;
@ -243,6 +257,510 @@ public class ShipDetailRepositoryImpl extends BaseJdbcRepository<ShipDetailEntit
log.info("{} 배치 삽입 완료: {} 건", getEntityName(), entities.size());
}
@Override
public void saveAllCrewListData(List<CrewListEntity> entities) {
String entityName = "CrewListEntity";
String sql = ShipDetailSql.getCrewListSql();
if (entities == null || entities.isEmpty()) {
return;
}
log.info("{} 배치 삽입 시작: {} 건", entityName, entities.size());
jdbcTemplate.batchUpdate(sql, entities, entities.size(),
(ps, entity) -> {
try {
setCrewListInsertParameters(ps, (CrewListEntity) entity);
} catch (Exception e) {
log.error("배치 삽입 파라미터 설정 실패 - " + entityName, e);
throw new RuntimeException(e);
}
});
log.info("{} 배치 삽입 완료: {} 건", entityName, entities.size());
}
@Override
public void saveAllStowageCommodityData(List<StowageCommodityEntity> entities) {
String entityName = "StowageCommodityEntity";
String sql = ShipDetailSql.getStowageCommoditySql();
if (entities == null || entities.isEmpty()) {
return;
}
log.info("{} 배치 삽입 시작: {} 건", entityName, entities.size());
jdbcTemplate.batchUpdate(sql, entities, entities.size(),
(ps, entity) -> {
try {
setStowageCommodityInsertParameters(ps, (StowageCommodityEntity) entity);
} catch (Exception e) {
log.error("배치 삽입 파라미터 설정 실패 - " + entityName, e);
throw new RuntimeException(e);
}
});
log.info("{} 배치 삽입 완료: {} 건", entityName, entities.size());
}
@Override
public void saveAllGroupBeneficialOwnerHistoryData(List<GroupBeneficialOwnerHistoryEntity> entities) {
String entityName = "GroupBeneficialOwnerHistoryEntity";
String sql = ShipDetailSql.getGroupBeneficialOwnerHistorySql();
if (entities == null || entities.isEmpty()) {
return;
}
log.info("{} 배치 삽입 시작: {} 건", entityName, entities.size());
jdbcTemplate.batchUpdate(sql, entities, entities.size(),
(ps, entity) -> {
try {
setGroupBeneficialOwnerHistoryInsertParameters(ps, (GroupBeneficialOwnerHistoryEntity) entity);
} catch (Exception e) {
log.error("배치 삽입 파라미터 설정 실패 - " + entityName, e);
throw new RuntimeException(e);
}
});
log.info("{} 배치 삽입 완료: {} 건", entityName, entities.size());
}
@Override
public void saveAllShipManagerHistoryData(List<ShipManagerHistoryEntity> entities) {
String entityName = "ShipManagerHistoryEntity";
String sql = ShipDetailSql.getShipManagerHistorySql();
if (entities == null || entities.isEmpty()) {
return;
}
log.info("{} 배치 삽입 시작: {} 건", entityName, entities.size());
jdbcTemplate.batchUpdate(sql, entities, entities.size(),
(ps, entity) -> {
try {
setShipManagerHistoryInsertParameters(ps, (ShipManagerHistoryEntity) entity);
} catch (Exception e) {
log.error("배치 삽입 파라미터 설정 실패 - " + entityName, e);
throw new RuntimeException(e);
}
});
log.info("{} 배치 삽입 완료: {} 건", entityName, entities.size());
}
@Override
public void saveAllOperatorHistoryData(List<OperatorHistoryEntity> entities) {
String entityName = "OperatorHistoryEntity";
String sql = ShipDetailSql.getOperatorHistorySql();
if (entities == null || entities.isEmpty()) {
return;
}
log.info("{} 배치 삽입 시작: {} 건", entityName, entities.size());
jdbcTemplate.batchUpdate(sql, entities, entities.size(),
(ps, entity) -> {
try {
setOperatorHistoryInsertParameters(ps, (OperatorHistoryEntity) entity);
} catch (Exception e) {
log.error("배치 삽입 파라미터 설정 실패 - " + entityName, e);
throw new RuntimeException(e);
}
});
log.info("{} 배치 삽입 완료: {} 건", entityName, entities.size());
}
@Override
public void saveAllTechnicalManagerHistoryData(List<TechnicalManagerHistoryEntity> entities) {
String entityName = "TechnicalManagerHistoryEntity";
String sql = ShipDetailSql.getTechnicalManagerHistorySql();
if (entities == null || entities.isEmpty()) {
return;
}
log.info("{} 배치 삽입 시작: {} 건", entityName, entities.size());
jdbcTemplate.batchUpdate(sql, entities, entities.size(),
(ps, entity) -> {
try {
setTechnicalManagerHistoryInsertParameters(ps, (TechnicalManagerHistoryEntity) entity);
} catch (Exception e) {
log.error("배치 삽입 파라미터 설정 실패 - " + entityName, e);
throw new RuntimeException(e);
}
});
log.info("{} 배치 삽입 완료: {} 건", entityName, entities.size());
}
@Override
public void saveAllBareBoatCharterHistoryData(List<BareBoatCharterHistoryEntity> entities) {
String entityName = "BareBoatCharterHistoryEntity";
String sql = ShipDetailSql.getBareBoatCharterHistorySql();
if (entities == null || entities.isEmpty()) {
return;
}
log.info("{} 배치 삽입 시작: {} 건", entityName, entities.size());
jdbcTemplate.batchUpdate(sql, entities, entities.size(),
(ps, entity) -> {
try {
setBareBoatCharterHistoryInsertParameters(ps, (BareBoatCharterHistoryEntity) entity);
} catch (Exception e) {
log.error("배치 삽입 파라미터 설정 실패 - " + entityName, e);
throw new RuntimeException(e);
}
});
log.info("{} 배치 삽입 완료: {} 건", entityName, entities.size());
}
@Override
public void saveAllNameHistoryData(List<NameHistoryEntity> entities) {
String entityName = "NameHistoryEntity";
String sql = ShipDetailSql.getNameHistorySql();
if (entities == null || entities.isEmpty()) {
return;
}
log.info("{} 배치 삽입 시작: {} 건", entityName, entities.size());
jdbcTemplate.batchUpdate(sql, entities, entities.size(),
(ps, entity) -> {
try {
setNameHistoryInsertParameters(ps, (NameHistoryEntity) entity);
} catch (Exception e) {
log.error("배치 삽입 파라미터 설정 실패 - " + entityName, e);
throw new RuntimeException(e);
}
});
log.info("{} 배치 삽입 완료: {} 건", entityName, entities.size());
}
@Override
public void saveAllFlagHistoryData(List<FlagHistoryEntity> entities) {
String entityName = "FlagHistoryEntity";
String sql = ShipDetailSql.getFlagHistorySql();
if (entities == null || entities.isEmpty()) {
return;
}
log.info("{} 배치 삽입 시작: {} 건", entityName, entities.size());
jdbcTemplate.batchUpdate(sql, entities, entities.size(),
(ps, entity) -> {
try {
setFlagHistoryInsertParameters(ps, (FlagHistoryEntity) entity);
} catch (Exception e) {
log.error("배치 삽입 파라미터 설정 실패 - " + entityName, e);
throw new RuntimeException(e);
}
});
log.info("{} 배치 삽입 완료: {} 건", entityName, entities.size());
}
@Override
public void saveAllAdditionalInformationData(List<AdditionalInformationEntity> entities) {
String entityName = "AdditionalInformationEntity";
String sql = ShipDetailSql.getAdditionalInformationSql();
if (entities == null || entities.isEmpty()) {
return;
}
log.info("{} 배치 삽입 시작: {} 건", entityName, entities.size());
jdbcTemplate.batchUpdate(sql, entities, entities.size(),
(ps, entity) -> {
try {
setAdditionalInformationInsertParameters(ps, (AdditionalInformationEntity) entity);
} catch (Exception e) {
log.error("배치 삽입 파라미터 설정 실패 - " + entityName, e);
throw new RuntimeException(e);
}
});
log.info("{} 배치 삽입 완료: {} 건", entityName, entities.size());
}
@Override
public void saveAllPandIHistoryData(List<PandIHistoryEntity> entities) {
String entityName = "PandIHistoryEntity";
String sql = ShipDetailSql.getPandIHistorySql();
if (entities == null || entities.isEmpty()) {
return;
}
log.info("{} 배치 삽입 시작: {} 건", entityName, entities.size());
jdbcTemplate.batchUpdate(sql, entities, entities.size(),
(ps, entity) -> {
try {
setPandIHistoryInsertParameters(ps, (PandIHistoryEntity) entity);
} catch (Exception e) {
log.error("배치 삽입 파라미터 설정 실패 - " + entityName, e);
throw new RuntimeException(e);
}
});
log.info("{} 배치 삽입 완료: {} 건", entityName, entities.size());
}
@Override
public void saveAllCallSignAndMmsiHistoryData(List<CallSignAndMmsiHistoryEntity> entities) {
String entityName = "CallSignAndMmsiHistoryEntity";
String sql = ShipDetailSql.getCallSignAndMmsiHistorySql();
if (entities == null || entities.isEmpty()) {
return;
}
log.info("{} 배치 삽입 시작: {} 건", entityName, entities.size());
jdbcTemplate.batchUpdate(sql, entities, entities.size(),
(ps, entity) -> {
try {
setCallSignAndMmsiHistoryInsertParameters(ps, (CallSignAndMmsiHistoryEntity) entity);
} catch (Exception e) {
log.error("배치 삽입 파라미터 설정 실패 - " + entityName, e);
throw new RuntimeException(e);
}
});
log.info("{} 배치 삽입 완료: {} 건", entityName, entities.size());
}
@Override
public void saveAllIceClassData(List<IceClassEntity> entities) {
String entityName = "IceClassEntity";
String sql = ShipDetailSql.getIceClassSql();
if (entities == null || entities.isEmpty()) {
return;
}
log.info("{} 배치 삽입 시작: {} 건", entityName, entities.size());
jdbcTemplate.batchUpdate(sql, entities, entities.size(),
(ps, entity) -> {
try {
setIceClassInsertParameters(ps, (IceClassEntity) entity);
} catch (Exception e) {
log.error("배치 삽입 파라미터 설정 실패 - " + entityName, e);
throw new RuntimeException(e);
}
});
log.info("{} 배치 삽입 완료: {} 건", entityName, entities.size());
}
@Override
public void saveAllSafetyManagementCertificateHistoryData(List<SafetyManagementCertificateHistoryEntity> entities) {
String entityName = "SafetyManagementCertificateHistoryEntity";
String sql = ShipDetailSql.getSafetyManagementCertificateHistorySql();
if (entities == null || entities.isEmpty()) {
return;
}
log.info("{} 배치 삽입 시작: {} 건", entityName, entities.size());
jdbcTemplate.batchUpdate(sql, entities, entities.size(),
(ps, entity) -> {
try {
setSafetyManagementCertificateHistoryInsertParameters(ps, (SafetyManagementCertificateHistoryEntity) entity);
} catch (Exception e) {
log.error("배치 삽입 파라미터 설정 실패 - " + entityName, e);
throw new RuntimeException(e);
}
});
log.info("{} 배치 삽입 완료: {} 건", entityName, entities.size());
}
@Override
public void saveAllClassHistoryData(List<ClassHistoryEntity> entities) {
String entityName = "ClassHistoryEntity";
String sql = ShipDetailSql.getClassHistorySql();
if (entities == null || entities.isEmpty()) {
return;
}
log.info("{} 배치 삽입 시작: {} 건", entityName, entities.size());
jdbcTemplate.batchUpdate(sql, entities, entities.size(),
(ps, entity) -> {
try {
setClassHistoryInsertParameters(ps, (ClassHistoryEntity) entity);
} catch (Exception e) {
log.error("배치 삽입 파라미터 설정 실패 - " + entityName, e);
throw new RuntimeException(e);
}
});
log.info("{} 배치 삽입 완료: {} 건", entityName, entities.size());
}
@Override
public void saveAllSurveyDatesHistoryData(List<SurveyDatesHistoryEntity> entities) {
String entityName = "SurveyDatesHistoryEntity";
String sql = ShipDetailSql.getSurveyDatesHistorySql();
if (entities == null || entities.isEmpty()) {
return;
}
log.info("{} 배치 삽입 시작: {} 건", entityName, entities.size());
jdbcTemplate.batchUpdate(sql, entities, entities.size(),
(ps, entity) -> {
try {
setSurveyDatesHistoryInsertParameters(ps, (SurveyDatesHistoryEntity) entity);
} catch (Exception e) {
log.error("배치 삽입 파라미터 설정 실패 - " + entityName, e);
throw new RuntimeException(e);
}
});
log.info("{} 배치 삽입 완료: {} 건", entityName, entities.size());
}
@Override
public void saveAllSurveyDatesHistoryUniqueData(List<SurveyDatesHistoryUniqueEntity> entities) {
String entityName = "SurveyDatesHistoryUniqueEntity";
String sql = ShipDetailSql.getSurveyDatesHistoryUniqueSql();
if (entities == null || entities.isEmpty()) {
return;
}
log.info("{} 배치 삽입 시작: {} 건", entityName, entities.size());
jdbcTemplate.batchUpdate(sql, entities, entities.size(),
(ps, entity) -> {
try {
setSurveyDatesHistoryUniqueInsertParameters(ps, (SurveyDatesHistoryUniqueEntity) entity);
} catch (Exception e) {
log.error("배치 삽입 파라미터 설정 실패 - " + entityName, e);
throw new RuntimeException(e);
}
});
log.info("{} 배치 삽입 완료: {} 건", entityName, entities.size());
}
@Override
public void saveAllSisterShipLinksData(List<SisterShipLinksEntity> entities) {
String entityName = "SisterShipLinksEntity";
String sql = ShipDetailSql.getSisterShipLinksSql();
if (entities == null || entities.isEmpty()) {
return;
}
log.info("{} 배치 삽입 시작: {} 건", entityName, entities.size());
jdbcTemplate.batchUpdate(sql, entities, entities.size(),
(ps, entity) -> {
try {
setSisterShipLinksInsertParameters(ps, (SisterShipLinksEntity) entity);
} catch (Exception e) {
log.error("배치 삽입 파라미터 설정 실패 - " + entityName, e);
throw new RuntimeException(e);
}
});
log.info("{} 배치 삽입 완료: {} 건", entityName, entities.size());
}
@Override
public void saveAllStatusHistoryData(List<StatusHistoryEntity> entities) {
String entityName = "StatusHistoryEntity";
String sql = ShipDetailSql.getStatusHistorySql();
if (entities == null || entities.isEmpty()) {
return;
}
log.info("{} 배치 삽입 시작: {} 건", entityName, entities.size());
jdbcTemplate.batchUpdate(sql, entities, entities.size(),
(ps, entity) -> {
try {
setStatusHistoryInsertParameters(ps, (StatusHistoryEntity) entity);
} catch (Exception e) {
log.error("배치 삽입 파라미터 설정 실패 - " + entityName, e);
throw new RuntimeException(e);
}
});
log.info("{} 배치 삽입 완료: {} 건", entityName, entities.size());
}
@Override
public void saveAllSpecialFeatureData(List<SpecialFeatureEntity> entities) {
String entityName = "SpecialFeatureEntity";
String sql = ShipDetailSql.getSpecialFeatureSql();
if (entities == null || entities.isEmpty()) {
return;
}
log.info("{} 배치 삽입 시작: {} 건", entityName, entities.size());
jdbcTemplate.batchUpdate(sql, entities, entities.size(),
(ps, entity) -> {
try {
setSpecialFeatureInsertParameters(ps, (SpecialFeatureEntity) entity);
} catch (Exception e) {
log.error("배치 삽입 파라미터 설정 실패 - " + entityName, e);
throw new RuntimeException(e);
}
});
log.info("{} 배치 삽입 완료: {} 건", entityName, entities.size());
}
@Override
public void saveAllThrustersData(List<ThrustersEntity> entities) {
String entityName = "ThrustersEntity";
String sql = ShipDetailSql.getThrustersSql();
if (entities == null || entities.isEmpty()) {
return;
}
log.info("{} 배치 삽입 시작: {} 건", entityName, entities.size());
jdbcTemplate.batchUpdate(sql, entities, entities.size(),
(ps, entity) -> {
try {
setThrustersInsertParameters(ps, (ThrustersEntity) entity);
} catch (Exception e) {
log.error("배치 삽입 파라미터 설정 실패 - " + entityName, e);
throw new RuntimeException(e);
}
});
log.info("{} 배치 삽입 완료: {} 건", entityName, entities.size());
}
public boolean existsByImo(String imo) {
String sql = String.format("SELECT COUNT(*) FROM %s WHERE %s = ?", getTableName(), getIdColumnName("ihslrorimoshipno"));
Long count = jdbcTemplate.queryForObject(sql, Long.class, imo);
@ -270,6 +788,301 @@ public class ShipDetailRepositoryImpl extends BaseJdbcRepository<ShipDetailEntit
ps.setString(idx++, entity.getLRNO()); //vesselId
}
private void setCrewListInsertParameters(PreparedStatement ps, CrewListEntity entity)throws Exception{
int idx = 1;
ps.setString(idx++, entity.getDataSetVersion()); // 1. datasetversion
ps.setString(idx++, entity.getId()); // 2. id
ps.setString(idx++, entity.getLrno()); // 3. lrno
ps.setString(idx++, entity.getShipname()); // 4. shipname
ps.setString(idx++, entity.getCrewlistdate()); // 5. crewlistdate
ps.setString(idx++, entity.getNationality()); // 6. nationality
setIntegerOrNull(ps, idx++, entity.getTotalcrew()); // 7. totalcrew
setIntegerOrNull(ps, idx++, entity.getTotalratings()); // 8. totalratings
setIntegerOrNull(ps, idx++, entity.getTotalofficers()); // 9. totalofficers
setIntegerOrNull(ps, idx++, entity.getTotalcadets()); // 10. totalcadets
setIntegerOrNull(ps, idx++, entity.getTotaltrainees()); // 11. totaltrainees
setIntegerOrNull(ps, idx++, entity.getTotalridingsquad()); // 12. totalridingsquad
setIntegerOrNull(ps, idx++, entity.getTotalundeclared()); // 13. totalundeclared
ps.setString(idx++, entity.getLrno()); // 14. vesselid
}
private void setStowageCommodityInsertParameters(PreparedStatement ps, StowageCommodityEntity entity)throws Exception{
int idx = 1;
// 1. datasetversion
ps.setString(idx++, entity.getDataSetVersion());
// 2. commoditycode
ps.setString(idx++, entity.getCommodityCode());
// 3. commoditydecode
ps.setString(idx++, entity.getCommodityDecode());
// 4. lrno
ps.setString(idx++, entity.getLrno());
// 5. sequence
ps.setString(idx++, entity.getSequence());
// 6. stowagecode
ps.setString(idx++, entity.getStowageCode());
// 7. stowagedecode
ps.setString(idx++, entity.getStowageDecode());
// 8. vesselid (SQL의 8번째 ? 이며, LRNO 값을 사용한다고 가정합니다.)
ps.setString(idx++, entity.getLrno());
}
private void setGroupBeneficialOwnerHistoryInsertParameters(PreparedStatement ps, GroupBeneficialOwnerHistoryEntity entity)throws Exception{
int idx = 1;
// 1. datasetversion
ps.setString(idx++, entity.getDataSetVersion());
// 2. companystatus
ps.setString(idx++, entity.getCompanyStatus());
// 3. effectivedate
ps.setString(idx++, entity.getEffectiveDate());
// 4. groupbeneficialowner
ps.setString(idx++, entity.getGroupBeneficialOwner());
// 5. groupbeneficialownercode
ps.setString(idx++, entity.getGroupBeneficialOwnerCode());
// 6. lrno
ps.setString(idx++, entity.getLrno());
// 7. sequence
ps.setString(idx++, entity.getSequence());
// 8. vesselid (SQL의 8번째 ? 이며, LRNO 값을 사용한다고 가정합니다.)
ps.setString(idx++, entity.getLrno());
}
private void setShipManagerHistoryInsertParameters(PreparedStatement ps, ShipManagerHistoryEntity entity)throws Exception{
int idx = 1;
// 1. datasetversion
ps.setString(idx++, entity.getDataSetVersion());
// 2. companystatus
ps.setString(idx++, entity.getCompanyStatus());
// 3. effectivedate
ps.setString(idx++, entity.getEffectiveDate());
// 4. lrno
ps.setString(idx++, entity.getLrno());
// 5. sequence
ps.setString(idx++, entity.getSequence());
// 6. shipmanager
ps.setString(idx++, entity.getShipManager());
// 7. shipmanagercode
ps.setString(idx++, entity.getShipManagerCode());
// 8. vesselid (SQL의 8번째 ? 이며, LRNO 값을 사용)
ps.setString(idx++, entity.getLrno());
}
private void setOperatorHistoryInsertParameters(PreparedStatement ps, OperatorHistoryEntity entity)throws Exception{
int idx = 1;
// 1. datasetversion
ps.setString(idx++, entity.getDataSetVersion());
// 2. companystatus
ps.setString(idx++, entity.getCompanyStatus());
// 3. effectivedate
ps.setString(idx++, entity.getEffectiveDate());
// 4. lrno
ps.setString(idx++, entity.getLrno());
// 5. operator
ps.setString(idx++, entity.getOperator());
// 6. operatorcode
ps.setString(idx++, entity.getOperatorCode());
// 7. sequence
ps.setString(idx++, entity.getSequence());
// 8. vesselid (SQL의 8번째 ? 이며, LRNO 값을 사용)
ps.setString(idx++, entity.getLrno());
}
private void setTechnicalManagerHistoryInsertParameters(PreparedStatement ps, TechnicalManagerHistoryEntity entity)throws Exception{
int idx = 1;
ps.setString(idx++, entity.getDataSetVersion()); // 1. datasetversion
ps.setString(idx++, entity.getCompanyStatus()); // 2. companystatus
ps.setString(idx++, entity.getEffectiveDate()); // 3. effectivedate
ps.setString(idx++, entity.getLrno()); // 4. lrno
ps.setString(idx++, entity.getSequence()); // 5. sequence
ps.setString(idx++, entity.getTechnicalManager()); // 6. technicalmanager
ps.setString(idx++, entity.getTechnicalManagerCode()); // 7. technicalmanagercode
ps.setString(idx++, entity.getLrno()); // 8. vesselid (LRNO 사용)
}
private void setBareBoatCharterHistoryInsertParameters(PreparedStatement ps, BareBoatCharterHistoryEntity entity)throws Exception{
int idx = 1;
ps.setString(idx++, entity.getDataSetVersion()); // 1. datasetversion
ps.setString(idx++, entity.getLrno()); // 2. lrno
ps.setString(idx++, entity.getSequence()); // 3. sequence
ps.setString(idx++, entity.getEffectiveDate()); // 4. effectivedate
ps.setString(idx++, entity.getBbChartererCode()); // 5. bbcharterercode
ps.setString(idx++, entity.getBbCharterer()); // 6. bbcharterer
ps.setString(idx++, entity.getLrno()); // 7. vesselid (LRNO 사용)
}
private void setNameHistoryInsertParameters(PreparedStatement ps, NameHistoryEntity entity)throws Exception{
int idx = 1;
ps.setString(idx++, entity.getDataSetVersion()); // 1. datasetversion
ps.setString(idx++, entity.getEffectiveDate()); // 2. effectivedate
ps.setString(idx++, entity.getLrno()); // 3. lrno
ps.setString(idx++, entity.getSequence()); // 4. sequence
ps.setString(idx++, entity.getVesselName()); // 5. vesselname
ps.setString(idx++, entity.getLrno()); // 6. vesselid (LRNO 사용)
}
private void setFlagHistoryInsertParameters(PreparedStatement ps, FlagHistoryEntity entity)throws Exception{
int idx = 1;
ps.setString(idx++, entity.getDataSetVersion()); // 1. datasetversion
ps.setString(idx++, entity.getEffectiveDate()); // 2. effectivedate
ps.setString(idx++, entity.getFlag()); // 3. flag
ps.setString(idx++, entity.getFlagCode()); // 4. flagcode
ps.setString(idx++, entity.getLrno()); // 5. lrno
ps.setString(idx++, entity.getSequence()); // 6. sequence
ps.setString(idx++, entity.getLrno()); // 7. vesselid (LRNO 사용)
}
private void setAdditionalInformationInsertParameters(PreparedStatement ps, AdditionalInformationEntity entity)throws Exception{
int idx = 1;
ps.setString(idx++, entity.getLrno()); // 1. lrno
ps.setString(idx++, entity.getShipemail()); // 2. shipemail
ps.setString(idx++, entity.getWaterdepthmax()); // 3. waterdepthmax
ps.setString(idx++, entity.getDrilldepthmax()); // 4. drilldepthmax
ps.setString(idx++, entity.getDrillbargeind()); // 5. drillbargeind
ps.setString(idx++, entity.getProductionvesselind()); // 6. productionvesselind
ps.setString(idx++, entity.getDeckheatexchangerind()); // 7. deckheatexchangerind
ps.setString(idx++, entity.getDeckheatexchangermaterial()); // 8. deckheatexchangermaterial
ps.setString(idx++, entity.getTweendeckportable()); // 9. tweendeckportable
ps.setString(idx++, entity.getTweendeckfixed()); // 10. tweendeckfixed
ps.setString(idx++, entity.getSatcomid()); // 11. satcomid
ps.setString(idx++, entity.getSatcomansback()); // 12. satcomansback
}
private void setPandIHistoryInsertParameters(PreparedStatement ps, PandIHistoryEntity entity)throws Exception{
int idx = 1;
ps.setString(idx++, entity.getDataSetVersion()); // 1. datasetversion
ps.setString(idx++, entity.getLrno()); // 2. lrno
ps.setString(idx++, entity.getSequence()); // 3. sequence
ps.setString(idx++, entity.getPandiclubcode()); // 4. pandiclubcode
ps.setString(idx++, entity.getPandiclubdecode()); // 5. pandiclubdecode
ps.setString(idx++, entity.getEffectiveDate()); // 6. effectivedate
ps.setString(idx++, entity.getSource()); // 7. source
ps.setString(idx++, entity.getLrno()); // 8. vesselid (LRNO 사용)
}
private void setCallSignAndMmsiHistoryInsertParameters(PreparedStatement ps, CallSignAndMmsiHistoryEntity entity)throws Exception{
int idx = 1;
ps.setString(idx++, entity.getDataSetVersion()); // 1. datasetversion
ps.setString(idx++, entity.getLrno()); // 2. lrno
ps.setString(idx++, entity.getSequence()); // 3. sequence
ps.setString(idx++, entity.getCallsign()); // 4. callsign
ps.setString(idx++, entity.getMmsi()); // 5. mmsi (JSON에 없으므로 Entity에서 null 처리)
ps.setString(idx++, entity.getEffectiveDate()); // 6. effectivedate
ps.setString(idx++, entity.getLrno()); // 7. vesselid (LRNO 사용)
}
private void setIceClassInsertParameters(PreparedStatement ps, IceClassEntity entity)throws Exception{
int idx = 1;
ps.setString(idx++, entity.getDataSetVersion()); // 1. datasetversion
ps.setString(idx++, entity.getIceClass()); // 2. iceclass
ps.setString(idx++, entity.getIceClassCode()); // 3. iceclasscode
ps.setString(idx++, entity.getLrno()); // 4. lrno
ps.setString(idx++, entity.getLrno()); // 5. vesselid (LRNO 사용)
}
private void setSafetyManagementCertificateHistoryInsertParameters(PreparedStatement ps, SafetyManagementCertificateHistoryEntity entity)throws Exception{
int idx = 1;
ps.setString(idx++, entity.getDataSetVersion()); // 1. datasetversion
ps.setString(idx++, entity.getLrno()); // 2. lrno
ps.setString(idx++, entity.getSafetyManagementCertificateAuditor()); // 3. safetymanagementcertificateauditor
ps.setString(idx++, entity.getSafetyManagementCertificateConventionOrVol()); // 4. safetymanagementcertificateconventionorvol
ps.setString(idx++, entity.getSafetyManagementCertificateDateExpires()); // 5. safetymanagementcertificatedateexpires
ps.setString(idx++, entity.getSafetyManagementCertificateDateIssued()); // 6. safetymanagementcertificatedateissued
ps.setString(idx++, entity.getSafetyManagementCertificateDOCCompany()); // 7. safetymanagementcertificatedoccompany
ps.setString(idx++, entity.getSafetyManagementCertificateFlag()); // 8. safetymanagementcertificateflag
ps.setString(idx++, entity.getSafetyManagementCertificateIssuer()); // 9. safetymanagementcertificateissuer
ps.setString(idx++, entity.getSafetyManagementCertificateOtherDescription()); // 10. safetymanagementcertificateotherdescription
ps.setString(idx++, entity.getSafetyManagementCertificateShipName()); // 11. safetymanagementcertificateshipname
ps.setString(idx++, entity.getSafetyManagementCertificateShipType()); // 12. safetymanagementcertificateshiptype
ps.setString(idx++, entity.getSafetyManagementCertificateSource()); // 13. safetymanagementcertificatesource
ps.setString(idx++, entity.getSafetyManagementCertificateCompanyCode()); // 14. safetymanagementcertificatecompanycode
ps.setString(idx++, entity.getSequence()); // 15. sequence
ps.setString(idx++, entity.getLrno()); // 16. vesselid (LRNO 사용)
}
private void setClassHistoryInsertParameters(PreparedStatement ps, ClassHistoryEntity entity)throws Exception{
int idx = 1;
ps.setString(idx++, entity.getDataSetVersion()); // 1. datasetversion
ps.setString(idx++, entity.get_class()); // 2. "class"
ps.setString(idx++, entity.getClassCode()); // 3. classcode
ps.setString(idx++, entity.getClassIndicator()); // 4. classindicator
ps.setString(idx++, entity.getCurrentIndicator()); // 5. currentindicator
ps.setString(idx++, entity.getEffectiveDate()); // 6. effectivedate
ps.setString(idx++, entity.getLrno()); // 7. lrno
ps.setString(idx++, entity.getSequence()); // 8. "sequence"
ps.setString(idx++, entity.getClassID()); // 9. classid
ps.setString(idx++, entity.getLrno()); // 10. vesselid (LRNO 사용)
}
private void setSurveyDatesHistoryInsertParameters(PreparedStatement ps, SurveyDatesHistoryEntity entity)throws Exception{
int idx = 1;
ps.setString(idx++, entity.getDataSetVersion()); // 1. datasetversion
ps.setString(idx++, entity.getClassSociety()); // 2. classsociety
ps.setString(idx++, entity.getClassSocietyCode()); // 3. classsocietycode
ps.setString(idx++, entity.getDockingSurvey()); // 4. dockingsurvey
ps.setString(idx++, entity.getLrno()); // 5. lrno
ps.setString(idx++, entity.getSpecialSurvey()); // 6. specialsurvey
ps.setString(idx++, entity.getAnnualSurvey()); // 7. annualsurvey
ps.setString(idx++, entity.getContinuousMachinerySurvey()); // 8. continuousmachinerysurvey
ps.setString(idx++, entity.getTailShaftSurvey()); // 9. tailshaftsurvey
ps.setString(idx++, entity.getLrno()); // 10. vesselid (LRNO 사용)
}
private void setSurveyDatesHistoryUniqueInsertParameters(PreparedStatement ps, SurveyDatesHistoryUniqueEntity entity)throws Exception{
int idx = 1;
ps.setString(idx++, entity.getDataSetVersion()); // 1. datasetversion
ps.setString(idx++, entity.getLrno()); // 2. lrno
ps.setString(idx++, entity.getClassSocietyCode()); // 3. classsocietycode
ps.setString(idx++, entity.getSurveyDate()); // 4. surveydate
ps.setString(idx++, entity.getSurveyType()); // 5. surveytype
ps.setString(idx++, entity.getClassSociety()); // 6. classsociety
ps.setString(idx++, entity.getLrno()); // 7. vesselid (LRNO 사용)
}
private void setSisterShipLinksInsertParameters(PreparedStatement ps, SisterShipLinksEntity entity)throws Exception{
int idx = 1;
ps.setString(idx++, entity.getDataSetVersion()); // 1. datasetversion
ps.setString(idx++, entity.getLrno()); // 2. lrno
ps.setString(idx++, entity.getLinkedLRNO()); // 3. linkedlrno
ps.setString(idx++, entity.getLrno()); // 4. vesselid (LRNO 사용)
}
private void setStatusHistoryInsertParameters(PreparedStatement ps, StatusHistoryEntity entity)throws Exception{
int idx = 1;
ps.setString(idx++, entity.getDataSetVersion()); // 1. datasetversion
ps.setString(idx++, entity.getLrno()); // 2. lrno
ps.setString(idx++, entity.getSequence()); // 3. sequence
ps.setString(idx++, entity.getStatus()); // 4. status
ps.setString(idx++, entity.getStatusCode()); // 5. statuscode
ps.setString(idx++, entity.getStatusDate()); // 6. statusdate
ps.setString(idx++, entity.getLrno()); // 7. vesselid (LRNO 사용)
}
private void setSpecialFeatureInsertParameters(PreparedStatement ps, SpecialFeatureEntity entity)throws Exception{
int idx = 1;
ps.setString(idx++, entity.getDataSetVersion()); // 1. datasetversion
ps.setString(idx++, entity.getLrno()); // 2. lrno
ps.setString(idx++, entity.getSequence()); // 3. sequence
ps.setString(idx++, entity.getSpecialFeature()); // 4. specialfeature
ps.setString(idx++, entity.getSpecialFeatureCode()); // 5. specialfeaturecode
ps.setString(idx++, entity.getLrno()); // 6. vesselid (LRNO 사용)
}
private void setThrustersInsertParameters(PreparedStatement ps, ThrustersEntity entity)throws Exception{
int idx = 1;
ps.setString(idx++, entity.getDataSetVersion()); // 1. datasetversion
ps.setString(idx++, entity.getLrno()); // 2. lrno
ps.setString(idx++, entity.getSequence()); // 3. sequence
ps.setString(idx++, entity.getThrusterType()); // 4. thrustertype
ps.setString(idx++, entity.getThrusterTypeCode()); // 5. thrustertypecode
setIntegerOrNull(ps, idx++, entity.getNumberOfThrusters()); // 6. numberofthrusters
ps.setString(idx++, entity.getThrusterPosition()); // 7. thrusterposition
setIntegerOrNull(ps, idx++, entity.getThrusterBHP()); // 8. thrusterbhp
setIntegerOrNull(ps, idx++, entity.getThrusterKW()); // 9. thrusterkw
ps.setString(idx++, entity.getTypeOfInstallation()); // 10. typeofinstallation
ps.setString(idx++, entity.getLrno()); // 11. vesselid (LRNO 사용)
}
/**
* ShipDetailEntity RowMapper
*/

파일 보기

@ -19,4 +19,495 @@ public class ShipDetailSql {
batch_flag = 'N'
""";
}
public static String getCrewListSql(){
// DDL에 정의된 모든 컬럼 포함
// DB 관리 컬럼 (shipresultindex, vesselid, rowindex) 쿼리문에서 nextval 또는 특정 값으로 처리합니다.
return """
INSERT INTO snp_data.crewlist(
datasetversion, id, lrno, shipname, crewlistdate,
nationality, totalcrew, totalratings, totalofficers,
totalcadets, totaltrainees, totalridingsquad, totalundeclared, vesselid
)VALUES(
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
)
ON CONFLICT (id, lrno)
DO UPDATE SET
datasetversion = EXCLUDED.datasetversion,
shipname = EXCLUDED.shipname,
crewlistdate = EXCLUDED.crewlistdate,
nationality = EXCLUDED.nationality,
totalcrew = EXCLUDED.totalcrew,
totalratings = EXCLUDED.totalratings,
totalofficers = EXCLUDED.totalofficers,
totalcadets = EXCLUDED.totalcadets,
totaltrainees = EXCLUDED.totaltrainees,
totalridingsquad = EXCLUDED.totalridingsquad,
totalundeclared = EXCLUDED.totalundeclared,
vesselid = EXCLUDED.vesselid,
batch_flag = 'N'
""";
}
public static String getStowageCommoditySql(){
// DDL에 정의된 모든 컬럼 포함
return """
INSERT INTO snp_data.stowagecommodity(
datasetversion, commoditycode, commoditydecode, lrno, "sequence",
stowagecode, stowagedecode, shipresultindex, vesselid, rowindex
)VALUES(
?, ?, ?, ?, ?, ?, ?,
nextval('snp_data.stowagecommodity_shipresultindex_seq'::regclass),
?, -- vesselid는 LRNO와 동일하다고 가정하고 ? 처리
nextval('snp_data.stowagecommodity_rowindex_seq'::regclass)
)
ON CONFLICT (lrno, sequence, commoditycode, stowagecode)
DO UPDATE SET
datasetversion = EXCLUDED.datasetversion,
commoditydecode = EXCLUDED.commoditydecode,
"sequence" = EXCLUDED."sequence",
stowagecode = EXCLUDED.stowagecode,
stowagedecode = EXCLUDED.stowagedecode,
vesselid = EXCLUDED.vesselid
""";
}
public static String getGroupBeneficialOwnerHistorySql(){
// UNIQUE INDEX: groupbeneficialownerhistory_effectivedate_idx (effectivedate, groupbeneficialownercode, lrno, sequence)
return """
INSERT INTO snp_data.groupbeneficialownerhistory(
datasetversion, companystatus, effectivedate, groupbeneficialowner,
groupbeneficialownercode, lrno, "sequence",
shipresultindex, vesselid, rowindex
)VALUES(
?, ?, ?, ?, ?, ?, ?,
nextval('snp_data.groupbeneficialownerhistory_shipresultindex_seq'::regclass),
?,
nextval('snp_data.groupbeneficialownerhistory_rowindex_seq'::regclass)
)
ON CONFLICT (effectivedate, groupbeneficialownercode, lrno, "sequence")
DO UPDATE SET
datasetversion = EXCLUDED.datasetversion,
companystatus = EXCLUDED.companystatus,
groupbeneficialowner = EXCLUDED.groupbeneficialowner,
vesselid = EXCLUDED.vesselid
""";
}
public static String getShipManagerHistorySql(){
// UNIQUE INDEX: shipmanagerhistory_effectivedate_idx (effectivedate, lrno, shipmanagercode, sequence)
return """
INSERT INTO snp_data.shipmanagerhistory(
datasetversion, companystatus, effectivedate, lrno, "sequence",
shipmanager, shipmanagercode, shipresultindex, vesselid, rowindex
)VALUES(
?, ?, ?, ?, ?, ?, ?,
nextval('snp_data.shipmanagerhistory_shipresultindex_seq'::regclass),
?,
nextval('snp_data.shipmanagerhistory_rowindex_seq'::regclass)
)
ON CONFLICT (effectivedate, lrno, shipmanagercode, "sequence")
DO UPDATE SET
datasetversion = EXCLUDED.datasetversion,
companystatus = EXCLUDED.companystatus,
shipmanager = EXCLUDED.shipmanager,
vesselid = EXCLUDED.vesselid
""";
}
public static String getOperatorHistorySql(){
// UNIQUE INDEX: operatorhistory_effectivedate_idx (effectivedate, lrno, operatorcode, sequence)
return """
INSERT INTO snp_data.operatorhistory(
datasetversion, companystatus, effectivedate, lrno, "operator",
operatorcode, "sequence", shipresultindex, vesselid, rowindex
)VALUES(
?, ?, ?, ?, ?, ?, ?,
nextval('snp_data.operatorhistory_shipresultindex_seq'::regclass),
?,
nextval('snp_data.operatorhistory_rowindex_seq'::regclass)
)
ON CONFLICT (effectivedate, lrno, operatorcode, "sequence")
DO UPDATE SET
datasetversion = EXCLUDED.datasetversion,
companystatus = EXCLUDED.companystatus,
"operator" = EXCLUDED."operator",
vesselid = EXCLUDED.vesselid
""";
}
public static String getTechnicalManagerHistorySql(){
// UNIQUE INDEX: technicalmanagerhistory_effectivedate_idx (effectivedate, lrno, sequence, technicalmanagercode)
return """
INSERT INTO snp_data.technicalmanagerhistory(
datasetversion, companystatus, effectivedate, lrno, "sequence",
technicalmanager, technicalmanagercode, shipresultindex, vesselid, rowindex
)VALUES(
?, ?, ?, ?, ?, ?, ?,
nextval('snp_data.technicalmanagerhistory_shipresultindex_seq'::regclass),
?,
nextval('snp_data.technicalmanagerhistory_rowindex_seq'::regclass)
)
ON CONFLICT (effectivedate, lrno, "sequence", technicalmanagercode)
DO UPDATE SET
datasetversion = EXCLUDED.datasetversion,
companystatus = EXCLUDED.companystatus,
technicalmanager = EXCLUDED.technicalmanager,
vesselid = EXCLUDED.vesselid
""";
}
public static String getBareBoatCharterHistorySql(){
// UNIQUE INDEX: bareboatcharterhistory_lrno_idx (lrno, sequence, effectivedate, bbcharterercode)
return """
INSERT INTO snp_data.bareboatcharterhistory(
datasetversion, lrno, "sequence", effectivedate, bbcharterercode,
bbcharterer, shipresultindex, vesselid, rowindex
)VALUES(
?, ?, ?, ?, ?, ?,
nextval('snp_data.bareboatcharterhistory_shipresultindex_seq'::regclass),
?,
nextval('snp_data.bareboatcharterhistory_rowindex_seq'::regclass)
)
ON CONFLICT (lrno, "sequence", effectivedate, bbcharterercode)
DO UPDATE SET
datasetversion = EXCLUDED.datasetversion,
bbcharterer = EXCLUDED.bbcharterer,
vesselid = EXCLUDED.vesselid
""";
}
public static String getNameHistorySql(){
// UNIQUE INDEX: namehistory_lrno_idx (lrno, sequence, effectivedate)
return """
INSERT INTO snp_data.namehistory(
datasetversion, effectivedate, lrno, "sequence", vesselname,
shipresultindex, vesselid, rowindex
)VALUES(
?, ?, ?, ?, ?,
nextval('snp_data.namehistory_shipresultindex_seq'::regclass),
?,
nextval('snp_data.namehistory_rowindex_seq'::regclass)
)
ON CONFLICT (lrno, "sequence", effectivedate)
DO UPDATE SET
datasetversion = EXCLUDED.datasetversion,
vesselname = EXCLUDED.vesselname,
vesselid = EXCLUDED.vesselid
""";
}
public static String getFlagHistorySql(){
// UNIQUE INDEX: flaghistory_flagcode_idx (flagcode, lrno)
return """
INSERT INTO snp_data.flaghistory(
datasetversion, effectivedate, flag, flagcode, lrno, "sequence",
shipresultindex, vesselid, rowindex
)VALUES(
?, ?, ?, ?, ?, ?,
nextval('snp_data.flaghistory_shipresultindex_seq'::regclass),
?,
nextval('snp_data.flaghistory_rowindex_seq'::regclass)
)
ON CONFLICT (lrno, flagcode, effectivedate, sequence)
DO UPDATE SET
datasetversion = EXCLUDED.datasetversion,
effectivedate = EXCLUDED.effectivedate,
flag = EXCLUDED.flag,
"sequence" = EXCLUDED."sequence",
vesselid = EXCLUDED.vesselid
""";
}
public static String getAdditionalInformationSql(){
// SQL Table Name: additionalshipsdata
// UNIQUE INDEX: additionalshipsdata_lrno_idx (lrno)
// rowindex는 serial이므로 INSERT 목록에서 제외
return """
INSERT INTO snp_data.additionalshipsdata(
lrno, shipemail, waterdepthmax, drilldepthmax, drillbargeind,
productionvesselind, deckheatexchangerind, deckheatexchangermaterial,
tweendeckportable, tweendeckfixed, satcomid, satcomansback
)VALUES(
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
)
ON CONFLICT (lrno)
DO UPDATE SET
shipemail = EXCLUDED.shipemail,
waterdepthmax = EXCLUDED.waterdepthmax,
drilldepthmax = EXCLUDED.drilldepthmax,
drillbargeind = EXCLUDED.drillbargeind,
productionvesselind = EXCLUDED.productionvesselind,
deckheatexchangerind = EXCLUDED.deckheatexchangerind,
deckheatexchangermaterial = EXCLUDED.deckheatexchangermaterial,
tweendeckportable = EXCLUDED.tweendeckportable,
tweendeckfixed = EXCLUDED.tweendeckfixed,
satcomid = EXCLUDED.satcomid,
satcomansback = EXCLUDED.satcomansback
""";
}
public static String getPandIHistorySql(){
// UNIQUE INDEX: pandihistory_lrno_idx (lrno, pandiclubcode, effectivedate)
return """
INSERT INTO snp_data.pandihistory(
datasetversion, lrno, "sequence", pandiclubcode, pandiclubdecode,
effectivedate, "source", shipresultindex, vesselid, rowindex
)VALUES(
?, ?, ?, ?, ?, ?, ?,
nextval('snp_data.pandihistory_shipresultindex_seq'::regclass),
?,
nextval('snp_data.pandihistory_rowindex_seq'::regclass)
)
ON CONFLICT (lrno, pandiclubcode, effectivedate)
DO UPDATE SET
datasetversion = EXCLUDED.datasetversion,
"sequence" = EXCLUDED."sequence",
pandiclubdecode = EXCLUDED.pandiclubdecode,
"source" = EXCLUDED."source",
vesselid = EXCLUDED.vesselid
""";
}
public static String getCallSignAndMmsiHistorySql(){
// UNIQUE INDEX: callsignandmmsihistory_lrno_idx (lrno, effectivedate, sequence)
return """
INSERT INTO snp_data.callsignandmmsihistory(
datasetversion, lrno, "sequence", callsign, mmsi,
effectivedate, shipresultindex, vesselid, rowindex
)VALUES(
?, ?, ?, ?, ?, ?,
nextval('snp_data.callsignandmmsihistory_shipresultindex_seq'::regclass),
?,
nextval('snp_data.callsignandmmsihistory_rowindex_seq'::regclass)
)
ON CONFLICT (lrno, effectivedate, "sequence")
DO UPDATE SET
datasetversion = EXCLUDED.datasetversion,
callsign = EXCLUDED.callsign,
mmsi = EXCLUDED.mmsi,
vesselid = EXCLUDED.vesselid
""";
}
public static String getIceClassSql(){
// UNIQUE INDEX: iceclass_iceclasscode_idx (iceclasscode, lrno)
return """
INSERT INTO snp_data.iceclass(
datasetversion, iceclass, iceclasscode, lrno,
shipresultindex, vesselid, rowindex
)VALUES(
?, ?, ?, ?,
nextval('snp_data.iceclass_shipresultindex_seq'::regclass),
?,
nextval('snp_data.iceclass_rowindex_seq'::regclass)
)
ON CONFLICT (iceclasscode, lrno)
DO UPDATE SET
datasetversion = EXCLUDED.datasetversion,
iceclass = EXCLUDED.iceclass,
vesselid = EXCLUDED.vesselid
""";
}
public static String getSafetyManagementCertificateHistorySql(){
// SQL Table Name: safetymanagementcertificatehist
// UNIQUE INDEX: safetymanagementcertificatehist_lrno_idx (lrno, safetymanagementcertificatecompanycode, sequence)
return """
INSERT INTO snp_data.safetymanagementcertificatehist(
datasetversion, lrno, safetymanagementcertificateauditor,
safetymanagementcertificateconventionorvol, safetymanagementcertificatedateexpires,
safetymanagementcertificatedateissued, safetymanagementcertificatedoccompany,
safetymanagementcertificateflag, safetymanagementcertificateissuer,
safetymanagementcertificateotherdescription, safetymanagementcertificateshipname,
safetymanagementcertificateshiptype, safetymanagementcertificatesource,
safetymanagementcertificatecompanycode, "sequence", shipresultindex,
vesselid, rowindex
)VALUES(
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
nextval('snp_data.safetymanagementcertificatehist_shipresultindex_seq'::regclass),
?,
nextval('snp_data.safetymanagementcertificatehist_rowindex_seq'::regclass)
)
ON CONFLICT (lrno, "sequence")
DO UPDATE SET
datasetversion = EXCLUDED.datasetversion,
safetymanagementcertificateauditor = EXCLUDED.safetymanagementcertificateauditor,
safetymanagementcertificateconventionorvol = EXCLUDED.safetymanagementcertificateconventionorvol,
safetymanagementcertificatedateexpires = EXCLUDED.safetymanagementcertificatedateexpires,
safetymanagementcertificatedateissued = EXCLUDED.safetymanagementcertificatedateissued,
safetymanagementcertificatedoccompany = EXCLUDED.safetymanagementcertificatedoccompany,
safetymanagementcertificateflag = EXCLUDED.safetymanagementcertificateflag,
safetymanagementcertificateissuer = EXCLUDED.safetymanagementcertificateissuer,
safetymanagementcertificateotherdescription = EXCLUDED.safetymanagementcertificateotherdescription,
safetymanagementcertificateshipname = EXCLUDED.safetymanagementcertificateshipname,
safetymanagementcertificateshiptype = EXCLUDED.safetymanagementcertificateshiptype,
safetymanagementcertificatesource = EXCLUDED.safetymanagementcertificatesource,
vesselid = EXCLUDED.vesselid
""";
}
public static String getClassHistorySql(){
// UNIQUE INDEX: classhistory_classcode_idx (classcode, effectivedate, lrno, sequence)
return """
INSERT INTO snp_data.classhistory(
datasetversion, "class", classcode, classindicator, currentindicator,
effectivedate, lrno, "sequence", classid, shipresultindex,
vesselid, rowindex
)VALUES(
?, ?, ?, ?, ?, ?, ?, ?, ?,
nextval('snp_data.classhistory_shipresultindex_seq'::regclass),
?,
nextval('snp_data.classhistory_rowindex_seq'::regclass)
)
ON CONFLICT (classcode, effectivedate, lrno, "sequence")
DO UPDATE SET
datasetversion = EXCLUDED.datasetversion,
"class" = EXCLUDED."class",
classindicator = EXCLUDED.classindicator,
currentindicator = EXCLUDED.currentindicator,
classid = EXCLUDED.classid,
vesselid = EXCLUDED.vesselid
""";
}
public static String getSurveyDatesHistorySql(){
// SQL Table Name: surveydates
// UNIQUE INDEX: surveydates_lrno_idx (lrno, classsocietycode)
return """
INSERT INTO snp_data.surveydates(
datasetversion, classsociety, classsocietycode, dockingsurvey,
lrno, specialsurvey, annualsurvey, continuousmachinerysurvey,
tailshaftsurvey, shipresultindex, vesselid, rowindex
)VALUES(
?, ?, ?, ?, ?, ?, ?, ?, ?,
nextval('snp_data.surveydates_shipresultindex_seq'::regclass),
?,
nextval('snp_data.surveydates_rowindex_seq'::regclass)
)
ON CONFLICT (lrno, classsocietycode)
DO UPDATE SET
datasetversion = EXCLUDED.datasetversion,
classsociety = EXCLUDED.classsociety,
dockingsurvey = EXCLUDED.dockingsurvey,
specialsurvey = EXCLUDED.specialsurvey,
annualsurvey = EXCLUDED.annualsurvey,
continuousmachinerysurvey = EXCLUDED.continuousmachinerysurvey,
tailshaftsurvey = EXCLUDED.tailshaftsurvey,
vesselid = EXCLUDED.vesselid
""";
}
public static String getSurveyDatesHistoryUniqueSql(){
// SQL Table Name: surveydateshistoryunique
// UNIQUE INDEX: surveydateshistoryunique_lrno_idx (lrno, classsocietycode, surveydate, surveytype)
return """
INSERT INTO snp_data.surveydateshistoryunique(
datasetversion, lrno, classsocietycode, surveydate,
surveytype, classsociety, shipresultindex, vesselid, rowindex
)VALUES(
?, ?, ?, ?, ?, ?,
nextval('snp_data.surveydateshistoryunique_shipresultindex_seq'::regclass),
?,
nextval('snp_data.surveydateshistoryunique_rowindex_seq'::regclass)
)
ON CONFLICT (lrno, classsocietycode, surveydate, surveytype)
DO UPDATE SET
datasetversion = EXCLUDED.datasetversion,
classsociety = EXCLUDED.classsociety,
vesselid = EXCLUDED.vesselid
""";
}
public static String getSisterShipLinksSql(){
// SQL Table Name: sistershiplinks
// UNIQUE INDEX: sistershiplinks_lrno_idx (lrno, linkedlrno)
return """
INSERT INTO snp_data.sistershiplinks(
datasetversion, lrno, linkedlrno, shipresultindex,
vesselid, rowindex
)VALUES(
?, ?, ?,
nextval('snp_data.sistershiplinks_shipresultindex_seq'::regclass),
?,
nextval('snp_data.sistershiplinks_rowindex_seq'::regclass)
)
ON CONFLICT (lrno, linkedlrno)
DO UPDATE SET
datasetversion = EXCLUDED.datasetversion,
vesselid = EXCLUDED.vesselid
""";
}
public static String getStatusHistorySql(){
// SQL Table Name: statushistory
// UNIQUE INDEX: statushistory_lrno_idx (lrno, sequence, statuscode, statusdate)
return """
INSERT INTO snp_data.statushistory(
datasetversion, lrno, "sequence", status, statuscode, statusdate,
shipresultindex, vesselid, rowindex
)VALUES(
?, ?, ?, ?, ?, ?,
nextval('snp_data.statushistory_shipresultindex_seq'::regclass),
?,
nextval('snp_data.statushistory_rowindex_seq'::regclass)
)
ON CONFLICT (lrno, "sequence", statuscode, statusdate)
DO UPDATE SET
datasetversion = EXCLUDED.datasetversion,
status = EXCLUDED.status,
vesselid = EXCLUDED.vesselid
""";
}
public static String getSpecialFeatureSql(){
// SQL Table Name: specialfeature
// UNIQUE INDEX: specialfeature_lrno_idx (lrno, specialfeaturecode)
return """
INSERT INTO snp_data.specialfeature(
datasetversion, lrno, "sequence", specialfeature, specialfeaturecode,
shipresultindex, vesselid, rowindex
)VALUES(
?, ?, ?, ?, ?,
nextval('snp_data.specialfeature_shipresultindex_seq'::regclass),
?,
nextval('snp_data.specialfeature_rowindex_seq'::regclass)
)
ON CONFLICT (lrno, sequence, specialfeaturecode)
DO UPDATE SET
datasetversion = EXCLUDED.datasetversion,
"sequence" = EXCLUDED."sequence",
specialfeature = EXCLUDED.specialfeature,
vesselid = EXCLUDED.vesselid
""";
}
public static String getThrustersSql(){
// SQL Table Name: thrusters
// UNIQUE INDEX: thrusters_lrno_idx (lrno, sequence, thrustertypecode)
return """
INSERT INTO snp_data.thrusters(
datasetversion, lrno, "sequence", thrustertype, thrustertypecode,
numberofthrusters, thrusterposition, thrusterbhp, thrusterkw,
typeofinstallation, shipresultindex, vesselid, rowindex
)VALUES(
?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
nextval('snp_data.thrusters_shipresultindex_seq'::regclass),
?,
nextval('snp_data.thrusters_rowindex_seq'::regclass)
)
ON CONFLICT (lrno, "sequence")
DO UPDATE SET
datasetversion = EXCLUDED.datasetversion,
thrustertype = EXCLUDED.thrustertype,
numberofthrusters = EXCLUDED.numberofthrusters,
thrusterposition = EXCLUDED.thrusterposition,
thrusterbhp = EXCLUDED.thrusterbhp,
thrusterkw = EXCLUDED.thrusterkw,
typeofinstallation = EXCLUDED.typeofinstallation,
vesselid = EXCLUDED.vesselid
""";
}
}

파일 보기

@ -2,15 +2,14 @@ package com.snp.batch.jobs.shipdetail.batch.writer;
import com.snp.batch.common.batch.writer.BaseWriter;
import com.snp.batch.jobs.shipdetail.batch.dto.ShipDetailUpdate;
import com.snp.batch.jobs.shipdetail.batch.entity.OwnerHistoryEntity;
import com.snp.batch.jobs.shipdetail.batch.entity.ShipDetailEntity;
import com.snp.batch.jobs.shipdetail.batch.entity.ShipHashEntity;
import com.snp.batch.jobs.shipdetail.batch.entity.*;
import com.snp.batch.jobs.shipdetail.batch.repository.ShipDetailRepository;
import com.snp.batch.jobs.shipdetail.batch.repository.ShipHashRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
@ -44,14 +43,28 @@ public class ShipDetailDataWriter extends BaseWriter<ShipDetailUpdate> {
.collect(Collectors.toList());
// 1-2. List<List<OwnerHistoryEntity>> -> List<OwnerHistoryEntity> (OwnerHistory 데이터 처리용)
// OwnerHistory는 Bulk 처리를 위해 단일 평탄화된 리스트로 만듭니다.
List<OwnerHistoryEntity> ownerHistoriyListEntities = items.stream()
.flatMap(item -> {
// ShipDetailUpdate DTO에 List<OwnerHistoryEntity> 필드가 존재해야 합니다.
List<OwnerHistoryEntity> histories = item.getOwnerHistoryEntityList();
return histories != null ? histories.stream() : new ArrayList<OwnerHistoryEntity>().stream();
})
.collect(Collectors.toList());
List<OwnerHistoryEntity> ownerHistoriyListEntities = flattenEntities(items, ShipDetailUpdate::getOwnerHistoryEntityList);
List<CrewListEntity> crewListEntities = flattenEntities(items, ShipDetailUpdate::getCrewListEntityList);
List<StowageCommodityEntity> stowageCommodityListEntities = flattenEntities(items, ShipDetailUpdate::getStowageCommodityEntityList);
List<GroupBeneficialOwnerHistoryEntity> groupBeneficialOwnerHistoryListEntities = flattenEntities(items, ShipDetailUpdate::getGroupBeneficialOwnerHistoryEntityList);
List<ShipManagerHistoryEntity> shipManagerHistoryListEntities = flattenEntities(items, ShipDetailUpdate::getShipManagerHistoryEntityList);
List<OperatorHistoryEntity> operatorHistoryListEntities = flattenEntities(items, ShipDetailUpdate::getOperatorHistoryEntityList);
List<TechnicalManagerHistoryEntity> technicalManagerHistoryListEntities = flattenEntities(items, ShipDetailUpdate::getTechnicalManagerHistoryEntityList);
List<BareBoatCharterHistoryEntity> bareBoatCharterHistoryListEntities = flattenEntities(items, ShipDetailUpdate::getBareBoatCharterHistoryEntityList);
List<NameHistoryEntity> nameHistoryListEntities = flattenEntities(items, ShipDetailUpdate::getNameHistoryEntityList);
List<FlagHistoryEntity> flagHistoryListEntities = flattenEntities(items, ShipDetailUpdate::getFlagHistoryEntityList);
List<AdditionalInformationEntity> additionalInformationListEntities = flattenEntities(items, ShipDetailUpdate::getAdditionalInformationEntityList);
List<PandIHistoryEntity> pandIHistoryListEntities = flattenEntities(items, ShipDetailUpdate::getPandIHistoryEntityList);
List<CallSignAndMmsiHistoryEntity> callSignAndMmsiHistoryListEntities = flattenEntities(items, ShipDetailUpdate::getCallSignAndMmsiHistoryEntityList);
List<IceClassEntity> iceClassListEntities = flattenEntities(items, ShipDetailUpdate::getIceClassEntityList);
List<SafetyManagementCertificateHistoryEntity> safetyManagementCertificateHistoryListEntities = flattenEntities(items, ShipDetailUpdate::getSafetyManagementCertificateHistoryEntityList);
List<ClassHistoryEntity> classHistoryListEntities = flattenEntities(items, ShipDetailUpdate::getClassHistoryEntityList);
List<SurveyDatesHistoryEntity> surveyDatesHistoryListEntities = flattenEntities(items, ShipDetailUpdate::getSurveyDatesHistoryEntityList);
List<SurveyDatesHistoryUniqueEntity> surveyDatesHistoryUniqueListEntities = flattenEntities(items, ShipDetailUpdate::getSurveyDatesHistoryUniqueEntityList);
List<SisterShipLinksEntity> sisterShipLinksListEntities = flattenEntities(items, ShipDetailUpdate::getSisterShipLinksEntityList);
List<StatusHistoryEntity> statusHistoryListEntities = flattenEntities(items, ShipDetailUpdate::getStatusHistoryEntityList);
List<SpecialFeatureEntity> specialFeatureListEntities = flattenEntities(items, ShipDetailUpdate::getSpecialFeatureEntityList);
List<ThrustersEntity> thrustersListEntities = flattenEntities(items, ShipDetailUpdate::getThrustersEntityList);
// 1-3. List<ShipHashEntity> (Hash값 데이터 처리용)
List<ShipHashEntity> hashEntities = items.stream()
@ -63,11 +76,94 @@ public class ShipDetailDataWriter extends BaseWriter<ShipDetailUpdate> {
log.debug("Core20 데이터 저장 시작: {} 건", coreEntities.size());
shipDetailRepository.saveAllCoreData(coreEntities);
// 2-2. OwnerHistory (OwnerHistory 데이터)
// 2-2. 추가적인 Array/List 데이터
// OwnerHistory 저장
log.debug("OwnerHistory 데이터 저장 시작: {} 건", ownerHistoriyListEntities.size());
shipDetailRepository.saveAllOwnerHistoryData(ownerHistoriyListEntities);
// TODO : 추가적인 Array/List 데이터 저장 로직 추가
// CrewList 저장
log.debug("CrewList 데이터 저장 시작: {} 건", crewListEntities.size());
shipDetailRepository.saveAllCrewListData(crewListEntities);
// StowageCommodity 저장
log.debug("StowageCommodity 저장 시작: {} 건", stowageCommodityListEntities.size());
shipDetailRepository.saveAllStowageCommodityData(stowageCommodityListEntities);
// GroupBeneficialOwnerHistory 저장
log.debug("GroupBeneficialOwnerHistory 저장 시작: {} 건", groupBeneficialOwnerHistoryListEntities.size());
shipDetailRepository.saveAllGroupBeneficialOwnerHistoryData(groupBeneficialOwnerHistoryListEntities);
// ShipManagerHistory 저장
log.debug("ShipManagerHistory 저장 시작: {} 건", shipManagerHistoryListEntities.size());
shipDetailRepository.saveAllShipManagerHistoryData(shipManagerHistoryListEntities);
// OperatorHistory 저장
log.debug("OperatorHistory 저장 시작: {} 건", operatorHistoryListEntities.size());
shipDetailRepository.saveAllOperatorHistoryData(operatorHistoryListEntities);
// TechnicalManagerHistory 저장
log.debug("TechnicalManagerHistory 저장 시작: {} 건", technicalManagerHistoryListEntities.size());
shipDetailRepository.saveAllTechnicalManagerHistoryData(technicalManagerHistoryListEntities);
// BareBoatCharterHistory 저장
log.debug("BareBoatCharterHistory 저장 시작: {} 건", bareBoatCharterHistoryListEntities.size());
shipDetailRepository.saveAllBareBoatCharterHistoryData(bareBoatCharterHistoryListEntities);
// NameHistory 저장
log.debug("NameHistory 저장 시작: {} 건", nameHistoryListEntities.size());
shipDetailRepository.saveAllNameHistoryData(nameHistoryListEntities);
// FlagHistory 저장
log.debug("FlagHistory 저장 시작: {} 건", flagHistoryListEntities.size());
shipDetailRepository.saveAllFlagHistoryData(flagHistoryListEntities);
// AdditionalInformation 저장
log.debug("AdditionalInformation 저장 시작: {} 건", additionalInformationListEntities.size());
shipDetailRepository.saveAllAdditionalInformationData(additionalInformationListEntities);
// PandIHistory 저장
log.debug("PandIHistory 저장 시작: {} 건", pandIHistoryListEntities.size());
shipDetailRepository.saveAllPandIHistoryData(pandIHistoryListEntities);
// CallSignAndMmsiHistory 저장
log.debug("CallSignAndMmsiHistory 저장 시작: {} 건", callSignAndMmsiHistoryListEntities.size());
shipDetailRepository.saveAllCallSignAndMmsiHistoryData(callSignAndMmsiHistoryListEntities);
// IceClass 저장
log.debug("IceClass 저장 시작: {} 건", iceClassListEntities.size());
shipDetailRepository.saveAllIceClassData(iceClassListEntities);
// SafetyManagementCertificateHistory 저장
log.debug("SafetyManagementCertificateHistory 저장 시작: {} 건", safetyManagementCertificateHistoryListEntities.size());
shipDetailRepository.saveAllSafetyManagementCertificateHistoryData(safetyManagementCertificateHistoryListEntities);
// ClassHistory 저장
log.debug("ClassHistory 저장 시작: {} 건", classHistoryListEntities.size());
shipDetailRepository.saveAllClassHistoryData(classHistoryListEntities);
// SurveyDatesHistory 저장
log.debug("SurveyDatesHistory 저장 시작: {} 건", surveyDatesHistoryListEntities.size());
shipDetailRepository.saveAllSurveyDatesHistoryData(surveyDatesHistoryListEntities);
// SurveyDatesHistoryUnique 저장
log.debug("SurveyDatesHistoryUnique 저장 시작: {} 건", surveyDatesHistoryUniqueListEntities.size());
shipDetailRepository.saveAllSurveyDatesHistoryUniqueData(surveyDatesHistoryUniqueListEntities);
// SisterShipLinks 저장
log.debug("SisterShipLinks 저장 시작: {} 건", sisterShipLinksListEntities.size());
shipDetailRepository.saveAllSisterShipLinksData(sisterShipLinksListEntities);
// StatusHistory 저장
log.debug("StatusHistory 저장 시작: {} 건", statusHistoryListEntities.size());
shipDetailRepository.saveAllStatusHistoryData(statusHistoryListEntities);
// SpecialFeature 저장
log.debug("SpecialFeature 저장 시작: {} 건", specialFeatureListEntities.size());
shipDetailRepository.saveAllSpecialFeatureData(specialFeatureListEntities);
// Thrusters 저장
log.debug("Thrusters 저장 시작: {} 건", thrustersListEntities.size());
shipDetailRepository.saveAllThrustersData(thrustersListEntities);
// 2-3. ShipHashRepository (Hash값 데이터)
log.debug("Ship Hash 데이터 저장 시작: {} 건", hashEntities.size());
@ -77,4 +173,26 @@ public class ShipDetailDataWriter extends BaseWriter<ShipDetailUpdate> {
}
/**
* List<ShipDetailUpdate>에서 특정 Entity List들을 추출하여 하나의 List로 병합(Flatten)합니다.
*
* @param items 처리할 ShipDetailUpdate 리스트
* @param entityListGetter ShipDetailUpdate에서 원하는 Entity List를 가져오는 Function (: ShipDetailUpdate::getCrewListEntityList)
* @param <T> Entity의 타입
* @return 평탄화된 Entity 리스트
*/
public static <T> List<T> flattenEntities(
List<ShipDetailUpdate> items,
Function<ShipDetailUpdate, List<T>> entityListGetter) {
return items.stream()
// DTO에서 특정 Entity List<T> 추출합니다.
.map(entityListGetter)
// List가 null인 경우는 필터링하여 NPE를 방지합니다.
.filter(list -> list != null)
// List<List<T>> 형태를 List<T> 형태로 평탄화합니다.
.flatMap(Collection::stream)
.collect(Collectors.toList());
}
}

파일 보기

@ -0,0 +1,66 @@
package com.snp.batch.service;
import com.snp.batch.global.model.BatchLastExecution;
import com.snp.batch.global.repository.BatchLastExecutionRepository;
import jakarta.transaction.Transactional;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.util.*;
@Service
public class BatchDateService {
private static final String API_KEY = "SHIP_UPDATE_API";
private final BatchLastExecutionRepository repository;
public BatchDateService(BatchLastExecutionRepository repository) {
this.repository = repository;
}
/**
* API 호출에 필요한 from/to 날짜 파라미터를 계산하고 반환합니다.
*/
public Map<String, String> getShipUpdateApiDateParams() {
// 1. 마지막 성공 일자 (FROM 날짜) DB에서 조회
// 조회된 값이 없으면 (최초 실행), API 호출 시점의 하루 날짜를 사용합니다.
LocalDate lastDate = repository.findLastSuccessDate(API_KEY)
.orElse(LocalDate.now().minusDays(1));
// 2. 현재 실행 시점의 일자 (TO 날짜) 계산
LocalDate currentDate = LocalDate.now();
// 3. 파라미터 Map 구성
Map<String, String> params = new HashMap<>();
// FROM Parameters (DB 조회 )
params.put("fromYear", String.valueOf(lastDate.getYear()));
params.put("fromMonth", String.valueOf(lastDate.getMonthValue()));
params.put("fromDay", String.valueOf(lastDate.getDayOfMonth()));
// TO Parameters (현재 시점 )
params.put("toYear", String.valueOf(currentDate.getYear()));
params.put("toMonth", String.valueOf(currentDate.getMonthValue()));
params.put("toDay", String.valueOf(currentDate.getDayOfMonth()));
// 고정
params.put("shipsCategory", "0");
return params;
}
/**
* 배치 성공 , 다음 실행을 위해 to 날짜를 DB에 저장 업데이트합니다.
* @param successDate API 호출 성공 사용된 to 날짜
*/
@Transactional // UPDATE 쿼리를 사용하므로 트랜잭션 필요
public void updateLastSuccessDate(LocalDate successDate) {
// 1. UPDATE 시도
int updatedRows = repository.updateLastSuccessDate(API_KEY, successDate);
// 2. 업데이트된 레코드가 없다면 (최초 실행), INSERT 수행
if (updatedRows == 0) {
BatchLastExecution entity = new BatchLastExecution(API_KEY, successDate);
repository.save(entity);
}
}
}