Company Detail 수집 프로세스 추가

This commit is contained in:
hyojin kim 2026-01-16 14:15:00 +09:00
부모 64a3a55e78
커밋 43057d74fb
13개의 변경된 파일454개의 추가작업 그리고 85개의 파일을 삭제

파일 보기

@ -13,7 +13,7 @@ public class JsonChangeDetector {
// 해시 비교에서 제외할 필드 목록 (DataSetVersion )
// 목록은 모든 JSON 계층에 걸쳐 적용됩니다.
private static final java.util.Set<String> EXCLUDE_KEYS =
java.util.Set.of("DataSetVersion", "APSStatus", "LastUpdateDate", "LastUpdateDateTime");
java.util.Set.of("DataSetVersion", "APSStatus", "LastUpdateDateTime");
// =========================================================================
// LIST_SORT_KEYS: 정적 초기화 블록을 사용한 Map 정의
@ -47,6 +47,7 @@ public class JsonChangeDetector {
map.put("DarkActivityConfirmed", "Lrno,Mmsi,Dark_Time,Dark_Status");
map.put("CompanyComplianceDetails", "OwCode");
map.put("CompanyVesselRelationships", "LRNO");
map.put("CompanyDetailsComplexWithCodesAndParent", "OWCODE,LastChangeDate");
LIST_SORT_KEYS = Collections.unmodifiableMap(map);
}

파일 보기

@ -117,7 +117,7 @@ public class ShipDetailUpdateJobConfig extends BaseMultiStepJobConfig<ShipDetail
@Override
protected int getChunkSize() {
return 50; // API에서 100개씩 가져오므로 chunk도 100으로 설정
return 5; // API에서 100개씩 가져오므로 chunk도 100으로 설정
}
@Bean(name = "ShipDetailUpdateJob")

파일 보기

@ -0,0 +1,82 @@
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 CompanyDetailDto {
@JsonProperty("DataSetVersion")
private DataSetVersionDto dataSetVersion;
@JsonProperty("CompanyStatus")
private String companyStatus;
@JsonProperty("CountryName")
private String countryName;
@JsonProperty("Emailaddress")
private String emailaddress;
@JsonProperty("FoundedDate")
private String foundedDate;
@JsonProperty("FullAddress")
private String fullAddress;
@JsonProperty("FullName")
private String fullName;
@JsonProperty("LastChangeDate")
private String lastChangeDate;
@JsonProperty("LocationCode")
private String locationCode;
@JsonProperty("NationalityofControl")
private String nationalityofControl;
@JsonProperty("NationalityofControlCode")
private String nationalityofControlCode;
@JsonProperty("NationalityofRegistration")
private String nationalityofRegistration;
@JsonProperty("NationalityofRegistrationCode")
private String nationalityofRegistrationCode;
@JsonProperty("OWCODE")
private String owcode;
@JsonProperty("ParentCompany")
private String parentCompany;
@JsonProperty("RoomFloorBuilding1")
private String roomFloorBuilding1;
@JsonProperty("RoomFloorBuilding2")
private String roomFloorBuilding2;
@JsonProperty("RoomFloorBuilding3")
private String roomFloorBuilding3;
@JsonProperty("ShortCompanyName")
private String shortCompanyName;
@JsonProperty("Street")
private String street;
@JsonProperty("StreetNumber")
private String streetNumber;
@JsonProperty("Telephone")
private String telephone;
@JsonProperty("TownName")
private String townName;
@JsonProperty("Website")
private String website;
@JsonProperty("Facsimile")
private String facsimile;
@JsonProperty("Telex")
private String telex;
@JsonProperty("CareOfCode")
private String careOfCode;
@JsonProperty("POBox")
private String poBox;
@JsonProperty("PrePostcode")
private String prePostcode;
@JsonProperty("PostPostcode")
private String postPostcode;
@Getter @Setter @ToString @NoArgsConstructor
public static class DataSetVersionDto {
@JsonProperty("DataSetVersion")
private String dataSetVersion;
}
}

파일 보기

@ -294,7 +294,22 @@ public class ShipDetailDto {
@JsonProperty("BunkersDescriptiveNarrative")
private String bunkersdescriptivenarrative;
// 마지막 수정 일자
@JsonProperty("LastUpdateDate")
private String lastUpdateDate;
// 회사 코드
@JsonProperty("DocumentOfComplianceDOCCompanyCode")
private String documentOfComplianceDOCCompanyCode;
@JsonProperty("GroupBeneficialOwnerCompanyCode")
private String groupBeneficialOwnerCompanyCode;
@JsonProperty("OperatorCompanyCode")
private String operatorCompanyCode;
@JsonProperty("ShipManagerCompanyCode")
private String shipManagerCompanyCode;
@JsonProperty("TechnicalManagerCode")
private String technicalManagerCode;
@JsonProperty("RegisteredOwnerCode")
private String registeredOwnerCode;
/**
* 소유주 이력 List
@ -471,4 +486,7 @@ public class ShipDetailDto {
@JsonProperty("DarkActivityConfirmed")
private List<DarkActivityConfirmedDto> darkActivityConfirmed;
@JsonProperty("CompanyDetailsComplexWithCodesAndParent")
private List<CompanyDetailDto> companyDetailDtoList;
}

파일 보기

@ -46,4 +46,5 @@ public class ShipDetailUpdate {
private final List<DarkActivityConfirmedEntity> darkActivityConfirmedEntityList;
private final List<CompanyComplianceEntity> companyComplianceEntityList;
private final List<CompanyVesselRelationshipEntity> companyVesselRelationshipEntityList;
private final List<CompanyDetailEntity> companyDetailEntityList;
}

파일 보기

@ -0,0 +1,46 @@
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 CompanyDetailEntity extends BaseEntity {
private String dataSetVersion;
private String owcode;
private String shortcompanyname;
private String countryname;
private String townname;
private String telephone;
private String telex;
private String emailaddress;
private String website;
private String fullname;
private String careofcode;
private String roomfloorbuilding1;
private String roomfloorbuilding2;
private String roomfloorbuilding3;
private String pobox;
private String streetnumber;
private String street;
private String prepostcode;
private String postpostcode;
private String nationalityofregistration;
private String nationalityofcontrol;
private String locationcode;
private String nationalityofregistrationcode;
private String nationalityofcontrolcode;
private String lastchangedate;
private String parentcompany;
private String companystatus;
private String fulladdress;
private String facsimile;
private String foundeddate;
}

파일 보기

@ -269,4 +269,13 @@ public class ShipDetailEntity extends BaseEntity {
private String auxiliarygeneratorsdescriptivenarrative;
private String bunkersdescriptivenarrative;
// 마지막 수정 일자
private String lastUpdateDate;
// 회사 코드
private String documentOfComplianceDOCCompanyCode;
private String groupBeneficialOwnerCompanyCode;
private String operatorCompanyCode;
private String shipManagerCompanyCode;
private String technicalManagerCode;
private String registeredOwnerCode;
}

파일 보기

@ -62,6 +62,7 @@ public class ShipDetailDataProcessor extends BaseProcessor<ShipDetailComparisonD
List<DarkActivityConfirmedEntity> darkActivityConfirmedEntityList = makeDarkActivityConfirmedEntityList(comparisonData.getStructuredDto().getDarkActivityConfirmed());
List<CompanyComplianceEntity> companyComplianceEntityList = makeCompanyComplianceEntityList(comparisonData.getStructuredDto().getCompanyComplianceDetails());
List<CompanyVesselRelationshipEntity> companyVesselRelationshipEntityList = makeCompanyVesselRelationshipEntityList(comparisonData.getStructuredDto().getCompanyVesselRelationships());
List<CompanyDetailEntity> companyDetailEntityList = makeCompanyDetailEntity(comparisonData.getStructuredDto().getCompanyDetailDtoList());
// 3. 최종 업데이트 DTO 생성 (Writer에 전달)
return ShipDetailUpdate.builder()
@ -94,6 +95,7 @@ public class ShipDetailDataProcessor extends BaseProcessor<ShipDetailComparisonD
.darkActivityConfirmedEntityList(darkActivityConfirmedEntityList)
.companyComplianceEntityList(companyComplianceEntityList)
.companyVesselRelationshipEntityList(companyVesselRelationshipEntityList)
.companyDetailEntityList(companyDetailEntityList)
.shipHashEntity(makeShipHashEntity(imo, currentHash))
.build();
}
@ -200,6 +202,15 @@ public class ShipDetailDataProcessor extends BaseProcessor<ShipDetailComparisonD
.auxiliaryenginesnarrative(safeGetString(dto.getAuxiliaryenginesnarrative()))
.auxiliarygeneratorsdescriptivenarrative(safeGetString(dto.getAuxiliarygeneratorsdescriptivenarrative()))
.bunkersdescriptivenarrative(safeGetString(dto.getBunkersdescriptivenarrative()))
// 마지막 수정 일자
.lastUpdateDate(safeGetString(dto.getLastUpdateDate()))
// 회사 코드
.documentOfComplianceDOCCompanyCode(safeGetString(dto.getDocumentOfComplianceDOCCompanyCode()))
.groupBeneficialOwnerCompanyCode(safeGetString(dto.getGroupBeneficialOwnerCompanyCode()))
.operatorCompanyCode(safeGetString(dto.getOperatorCompanyCode()))
.shipManagerCompanyCode(safeGetString(dto.getShipManagerCompanyCode()))
.technicalManagerCode(safeGetString(dto.getTechnicalManagerCode()))
.registeredOwnerCode(safeGetString(dto.getRegisteredOwnerCode()))
.build();
}
@ -834,6 +845,54 @@ public class ShipDetailDataProcessor extends BaseProcessor<ShipDetailComparisonD
return entityList;
}
private List<CompanyDetailEntity> makeCompanyDetailEntity(List<CompanyDetailDto> dtoList) {
List<CompanyDetailEntity> companyDetailEntityList = new ArrayList<>();
if (dtoList == null || dtoList.isEmpty()) {
return companyDetailEntityList;
}
for (CompanyDetailDto dto : dtoList) {
String datasetVersion = (dto.getDataSetVersion() != null) ? dto.getDataSetVersion().getDataSetVersion() : null;
CompanyDetailEntity entity = CompanyDetailEntity.builder()
.dataSetVersion(safeGetString(datasetVersion))
.owcode(safeGetString(dto.getOwcode()))
.shortcompanyname(safeGetString(dto.getShortCompanyName()))
.countryname(safeGetString(dto.getCountryName()))
.townname(safeGetString(dto.getTownName()))
.telephone(safeGetString(dto.getTelephone()))
.telex(safeGetString(dto.getTelex()))
.emailaddress(safeGetString(dto.getEmailaddress()))
.website(safeGetString(dto.getWebsite()))
.fullname(safeGetString(dto.getFullName()))
.careofcode(safeGetString(dto.getCareOfCode()))
.roomfloorbuilding1(safeGetString(dto.getRoomFloorBuilding1()))
.roomfloorbuilding2(safeGetString(dto.getRoomFloorBuilding2()))
.roomfloorbuilding3(safeGetString(dto.getRoomFloorBuilding3()))
.pobox(safeGetString(dto.getPoBox()))
.streetnumber(safeGetString(dto.getStreetNumber()))
.street(safeGetString(dto.getStreet()))
.prepostcode(safeGetString(dto.getPrePostcode()))
.postpostcode(safeGetString(dto.getPostPostcode()))
.nationalityofregistration(safeGetString(dto.getNationalityofRegistration()))
.nationalityofcontrol(safeGetString(dto.getNationalityofControl()))
.locationcode(safeGetString(dto.getLocationCode()))
.nationalityofregistrationcode(safeGetString(dto.getNationalityofRegistrationCode()))
.nationalityofcontrolcode(safeGetString(dto.getNationalityofControlCode()))
.lastchangedate(safeGetString(dto.getLastChangeDate()))
.parentcompany(safeGetString(dto.getParentCompany()))
.companystatus(safeGetString(dto.getCompanyStatus()))
.fulladdress(safeGetString(dto.getFullAddress()))
.facsimile(safeGetString(dto.getFacsimile()))
.foundeddate(safeGetString(dto.getFoundedDate()))
.build();
companyDetailEntityList.add(entity);
}
return companyDetailEntityList;
}
/**
* 해시값을 비교하여 변경 여부를 판단합니다.
*/

파일 보기

@ -29,7 +29,7 @@ public class ShipDetailUpdateDataReader extends BaseApiReader<ShipDetailComparis
// DB 해시값을 저장할
private Map<String, String> dbMasterHashes;
private int currentBatchIndex = 0;
private final int batchSize = 50;
private final int batchSize = 5;
public ShipDetailUpdateDataReader(WebClient webClient, JdbcTemplate jdbcTemplate, ObjectMapper objectMapper,BatchDateService batchDateService, BatchApiLogService batchApiLogService, String maritimeApiUrl) {
super(webClient);
this.jdbcTemplate = jdbcTemplate;

파일 보기

@ -63,6 +63,8 @@ public interface ShipDetailRepository {
void saveAllCompanyVesselRelationshipData(List<CompanyVesselRelationshipEntity> entities);
void saveAllCompanyDetailData(List<CompanyDetailEntity> entities);
void delete(String id);
}

파일 보기

@ -61,7 +61,9 @@ public class ShipDetailRepositoryImpl extends BaseJdbcRepository<ShipDetailEntit
cargocapacitiesnarrative, geardescriptivenarrative, holdsdescriptivenarrative, hatchesdescriptivenarrative,
lanesdoorsrampsnarrative, specialisttankernarrative, tanksdescriptivenarrative,
primemoverdescriptivenarrative, primemoverdescriptiveoverviewnarrative,
auxiliaryenginesnarrative, auxiliarygeneratorsdescriptivenarrative, bunkersdescriptivenarrative
auxiliaryenginesnarrative, auxiliarygeneratorsdescriptivenarrative, bunkersdescriptivenarrative.
lastupdatedate,
documentofcompliancedoccompanycode, groupbeneficialownercompanycode, operatorcompanycode, shipmanagercompanycode, technicalmanagercode, registeredownercode
) VALUES (
?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
@ -70,7 +72,9 @@ public class ShipDetailRepositoryImpl extends BaseJdbcRepository<ShipDetailEntit
?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
?, ?, ?, ?, ?
?, ?, ?, ?, ?,
?,
?, ?, ?, ?, ?, ?
)
ON CONFLICT (ihslrorimoshipno)
DO UPDATE SET
@ -147,7 +151,14 @@ public class ShipDetailRepositoryImpl extends BaseJdbcRepository<ShipDetailEntit
primemoverdescriptiveoverviewnarrative = EXCLUDED.primemoverdescriptiveoverviewnarrative,
auxiliaryenginesnarrative = EXCLUDED.auxiliaryenginesnarrative,
auxiliarygeneratorsdescriptivenarrative = EXCLUDED.auxiliarygeneratorsdescriptivenarrative,
bunkersdescriptivenarrative = EXCLUDED.bunkersdescriptivenarrative
bunkersdescriptivenarrative = EXCLUDED.bunkersdescriptivenarrative,
lastupdatedate = EXCLUDED.lastupdatedate,
documentofcompliancedoccompanycode = EXCLUDED.documentofcompliancedoccompanycode,
groupbeneficialownercompanycode = EXCLUDED.groupbeneficialownercompanycode,
operatorcompanycode = EXCLUDED.operatorcompanycode,
shipmanagercompanycode = EXCLUDED.shipmanagercompanycode,
technicalmanagercode = EXCLUDED.technicalmanagercode,
registeredownercode = EXCLUDED.registeredownercode
""";
}
@ -229,6 +240,13 @@ public class ShipDetailRepositoryImpl extends BaseJdbcRepository<ShipDetailEntit
auxiliaryenginesnarrative = ?,
auxiliarygeneratorsdescriptivenarrative = ?,
bunkersdescriptivenarrative = ?,
lastupdatedate = ?,
documentofcompliancedoccompanycode = ?,
groupbeneficialownercompanycode = ?,
operatorcompanycode = ?,
shipmanagercompanycode = ?,
technicalmanagercode = ?,
registeredownercode = ?,
batch_flag = 'N'::character varying
WHERE ihslrorimoshipno = ?
""";
@ -341,6 +359,16 @@ public class ShipDetailRepositoryImpl extends BaseJdbcRepository<ShipDetailEntit
ps.setString(idx++, entity.getAuxiliarygeneratorsdescriptivenarrative());
ps.setString(idx++, entity.getBunkersdescriptivenarrative());
// 마지막 수정 일자
ps.setString(idx++, entity.getLastUpdateDate());
// 회사 코드
ps.setString(idx++, entity.getDocumentOfComplianceDOCCompanyCode());
ps.setString(idx++, entity.getGroupBeneficialOwnerCompanyCode());
ps.setString(idx++, entity.getOperatorCompanyCode());
ps.setString(idx++, entity.getShipManagerCompanyCode());
ps.setString(idx++, entity.getTechnicalManagerCode());
ps.setString(idx++, entity.getRegisteredOwnerCode());
// 감사 필드
// ps.setTimestamp(idx++, entity.getCreatedAt() != null ?
// Timestamp.valueOf(entity.getCreatedAt()) : Timestamp.valueOf(now()));
@ -502,6 +530,15 @@ public class ShipDetailRepositoryImpl extends BaseJdbcRepository<ShipDetailEntit
ps.setString(idx++, entity.getAuxiliarygeneratorsdescriptivenarrative());
ps.setString(idx++, entity.getBunkersdescriptivenarrative());
ps.setString(idx++, entity.getLastUpdateDate());
// 회사 코드
ps.setString(idx++, entity.getDocumentOfComplianceDOCCompanyCode());
ps.setString(idx++, entity.getGroupBeneficialOwnerCompanyCode());
ps.setString(idx++, entity.getOperatorCompanyCode());
ps.setString(idx++, entity.getShipManagerCompanyCode());
ps.setString(idx++, entity.getTechnicalManagerCode());
ps.setString(idx++, entity.getRegisteredOwnerCode());
ps.setString(idx++, entity.getIhslrorimoshipno());
}
@ -1139,6 +1176,64 @@ public class ShipDetailRepositoryImpl extends BaseJdbcRepository<ShipDetailEntit
log.info("{} 배치 삽입 완료: {} 건", entityName, entities.size());
}
@Override
public void saveAllCompanyDetailData(List<CompanyDetailEntity> entities) {
String entityName = "CompanyDetailEntity";
String sql = ShipDetailSql.getCompanyDetailSql();
if (entities == null || entities.isEmpty()) {
return;
}
log.debug("{} 배치 삽입 시작: {} 건", entityName, entities.size());
jdbcTemplate.batchUpdate(sql, entities, entities.size(),
(ps, entity) -> {
try {
setCompanyDetailInsertParameters(ps, entity);
} catch (Exception e) {
log.error("배치 삽입 파라미터 설정 실패 - " + entityName, e);
throw new RuntimeException(e);
}
});
log.info("{} 배치 삽입 완료: {} 건", entityName, entities.size());
}
private void setCompanyDetailInsertParameters(PreparedStatement ps, CompanyDetailEntity entity) throws Exception {
int idx = 1;
ps.setString(idx++, entity.getDataSetVersion());
ps.setString(idx++, entity.getOwcode());
ps.setString(idx++, entity.getShortcompanyname());
ps.setString(idx++, entity.getCountryname());
ps.setString(idx++, entity.getTownname());
ps.setString(idx++, entity.getTelephone());
ps.setString(idx++, entity.getTelex());
ps.setString(idx++, entity.getEmailaddress());
ps.setString(idx++, entity.getWebsite());
ps.setString(idx++, entity.getFullname());
ps.setString(idx++, entity.getCareofcode());
ps.setString(idx++, entity.getRoomfloorbuilding1());
ps.setString(idx++, entity.getRoomfloorbuilding2());
ps.setString(idx++, entity.getRoomfloorbuilding3());
ps.setString(idx++, entity.getPobox());
ps.setString(idx++, entity.getStreetnumber());
ps.setString(idx++, entity.getStreet());
ps.setString(idx++, entity.getPrepostcode());
ps.setString(idx++, entity.getPostpostcode());
ps.setString(idx++, entity.getNationalityofregistration());
ps.setString(idx++, entity.getNationalityofcontrol());
ps.setString(idx++, entity.getLocationcode());
ps.setString(idx++, entity.getNationalityofregistrationcode());
ps.setString(idx++, entity.getNationalityofcontrolcode());
ps.setString(idx++, entity.getLastchangedate());
ps.setString(idx++, entity.getParentcompany());
ps.setString(idx++, entity.getCompanystatus());
ps.setString(idx++, entity.getFulladdress());
ps.setString(idx++, entity.getFacsimile());
ps.setString(idx++, entity.getFoundeddate());
}
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);

파일 보기

@ -614,4 +614,55 @@ public class ShipDetailSql {
""";
}
public static String getCompanyDetailSql() {
return """
INSERT INTO snp_data.tb_company_detail (
datasetversion, owcode, shortcompanyname, countryname, townname,
telephone, telex, emailaddress, website, fullname,
careofcode, roomfloorbuilding1, roomfloorbuilding2, roomfloorbuilding3, pobox,
streetnumber, street, prepostcode, postpostcode, nationalityofregistration,
nationalityofcontrol, locationcode, nationalityofregistrationcode, nationalityofcontrolcode, lastchangedate,
parentcompany, companystatus, fulladdress, facsimile, foundeddate
) VALUES (
?, ?, ?, ?, ?,
?, ?, ?, ?, ?,
?, ?, ?, ?, ?,
?, ?, ?, ?, ?,
?, ?, ?, ?, ?,
?, ?, ?, ?, ?
)
ON CONFLICT (owcode) DO UPDATE SET
datasetversion = EXCLUDED.datasetversion,
shortcompanyname = EXCLUDED.shortcompanyname,
countryname = EXCLUDED.countryname,
townname = EXCLUDED.townname,
telephone = EXCLUDED.telephone,
telex = EXCLUDED.telex,
emailaddress = EXCLUDED.emailaddress,
website = EXCLUDED.website,
fullname = EXCLUDED.fullname,
careofcode = EXCLUDED.careofcode,
roomfloorbuilding1 = EXCLUDED.roomfloorbuilding1,
roomfloorbuilding2 = EXCLUDED.roomfloorbuilding2,
roomfloorbuilding3 = EXCLUDED.roomfloorbuilding3,
pobox = EXCLUDED.pobox,
streetnumber = EXCLUDED.streetnumber,
street = EXCLUDED.street,
prepostcode = EXCLUDED.prepostcode,
postpostcode = EXCLUDED.postpostcode,
nationalityofregistration = EXCLUDED.nationalityofregistration,
nationalityofcontrol = EXCLUDED.nationalityofcontrol,
locationcode = EXCLUDED.locationcode,
nationalityofregistrationcode = EXCLUDED.nationalityofregistrationcode,
nationalityofcontrolcode = EXCLUDED.nationalityofcontrolcode,
lastchangedate = EXCLUDED.lastchangedate,
parentcompany = EXCLUDED.parentcompany,
companystatus = EXCLUDED.companystatus,
fulladdress = EXCLUDED.fulladdress,
facsimile = EXCLUDED.facsimile,
foundeddate = EXCLUDED.foundeddate,
batch_flag = 'N';
""";
}
}

파일 보기

@ -68,6 +68,7 @@ public class ShipDetailDataWriter extends BaseWriter<ShipDetailUpdate> {
List<DarkActivityConfirmedEntity> darkActivityConfirmedEntities = flattenEntities(items, ShipDetailUpdate::getDarkActivityConfirmedEntityList);
List<CompanyComplianceEntity> companyComplianceEntities = flattenEntities(items, ShipDetailUpdate::getCompanyComplianceEntityList);
List<CompanyVesselRelationshipEntity> companyVesselRelationshipEntities = flattenEntities(items, ShipDetailUpdate::getCompanyVesselRelationshipEntityList);
List<CompanyDetailEntity> companyDetailEntities = flattenEntities(items, ShipDetailUpdate::getCompanyDetailEntityList);
// 1-3. List<ShipHashEntity> (Hash값 데이터 처리용)
List<ShipHashEntity> hashEntities = items.stream()
@ -180,6 +181,10 @@ public class ShipDetailDataWriter extends BaseWriter<ShipDetailUpdate> {
log.debug("CompanyVesselRelationship 저장 시작: {} 건", companyVesselRelationshipEntities.size());
shipDetailRepository.saveAllCompanyVesselRelationshipData(companyVesselRelationshipEntities);
// CompanyVesselRelationship 저장
log.debug("Company Detail 저장 시작: {} 건", companyDetailEntities.size());
shipDetailRepository.saveAllCompanyDetailData(companyDetailEntities);
// 2-3. ShipHashRepository (Hash값 데이터)
log.debug("Ship Hash 데이터 저장 시작: {} 건", hashEntities.size());
shipHashRepository.saveAllData(hashEntities);