Skip to content

Commit 7544bb3

Browse files
authored
Merge pull request #215 from FunD-StockProject/fix/stock-master-info
Fix 종목 마스터 데이터 업데이트 오류 수정10
2 parents 608cb98 + 5bbac83 commit 7544bb3

File tree

2 files changed

+58
-106
lines changed

2 files changed

+58
-106
lines changed

scripts/db/check_and_remove_sequences.sql

Lines changed: 0 additions & 56 deletions
This file was deleted.

src/main/java/com/fund/stockProject/stock/service/StockImportService.java

Lines changed: 58 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
import lombok.extern.slf4j.Slf4j;
1616
import org.springframework.dao.DataIntegrityViolationException;
1717
import org.springframework.stereotype.Service;
18-
import org.springframework.transaction.annotation.Transactional;
18+
import org.springframework.transaction.PlatformTransactionManager;
19+
import org.springframework.transaction.support.TransactionTemplate;
1920

2021
import java.io.File;
2122
import java.io.IOException;
@@ -31,9 +32,14 @@ public class StockImportService {
3132
private final ObjectMapper objectMapper;
3233
private final ExperimentRepository experimentRepository;
3334
private final PreferenceRepository preferenceRepository;
35+
private final PlatformTransactionManager transactionManager;
3436

3537
@PersistenceContext
3638
private EntityManager entityManager;
39+
40+
private TransactionTemplate getTransactionTemplate() {
41+
return new TransactionTemplate(transactionManager);
42+
}
3743

3844
/**
3945
* JSON 파일에서 종목 데이터를 읽어서 DB에 저장하고, 종목 마스터에 없는 종목은 isValid=false로 설정합니다.
@@ -389,66 +395,68 @@ private static class UpsertResult {
389395
/**
390396
* 작은 트랜잭션으로 배치를 처리합니다.
391397
* 각 배치는 독립적인 트랜잭션으로 처리되어 락 경합을 최소화합니다.
398+
* TransactionTemplate을 사용하여 명시적으로 트랜잭션을 관리합니다.
392399
*/
393-
@Transactional
394400
private UpsertResult processBatchInTransaction(List<Stock> batch) {
395-
UpsertResult result = new UpsertResult();
396-
397-
for (Stock stock : batch) {
398-
try {
399-
// symbol로 기존 종목 확인
400-
Optional<Stock> existingOpt = stockRepository.findBySymbol(stock.getSymbol());
401-
402-
if (existingOpt.isPresent()) {
403-
// 기존 종목 업데이트
404-
Stock existing = existingOpt.get();
405-
existing.updateSymbolNameIfNull(stock.getSymbolName());
406-
existing.setValid(true);
407-
if (stock.getDomesticSector() != null) {
408-
existing.setDomesticSector(stock.getDomesticSector());
409-
}
410-
if (stock.getOverseasSector() != null) {
411-
existing.setOverseasSector(stock.getOverseasSector());
401+
return getTransactionTemplate().execute(status -> {
402+
UpsertResult result = new UpsertResult();
403+
404+
for (Stock stock : batch) {
405+
try {
406+
// symbol로 기존 종목 확인
407+
Optional<Stock> existingOpt = stockRepository.findBySymbol(stock.getSymbol());
408+
409+
if (existingOpt.isPresent()) {
410+
// 기존 종목 업데이트
411+
Stock existing = existingOpt.get();
412+
existing.updateSymbolNameIfNull(stock.getSymbolName());
413+
existing.setValid(true);
414+
if (stock.getDomesticSector() != null) {
415+
existing.setDomesticSector(stock.getDomesticSector());
416+
}
417+
if (stock.getOverseasSector() != null) {
418+
existing.setOverseasSector(stock.getOverseasSector());
419+
}
420+
stockRepository.save(existing);
421+
result.updated++;
422+
} else {
423+
// 새 종목 삽입 (IDENTITY 전략 사용)
424+
stockRepository.save(stock);
425+
result.inserted++;
412426
}
413-
stockRepository.save(existing);
414-
result.updated++;
415-
} else {
416-
// 새 종목 삽입 (IDENTITY 전략 사용)
417-
stockRepository.save(stock);
418-
result.inserted++;
419-
}
420-
} catch (DataIntegrityViolationException e) {
421-
// 중복 키 오류 발생 시 기존 종목으로 처리
422-
entityManager.clear();
423-
if (handleDuplicateKeyError(stock, e)) {
424-
result.updated++;
425-
} else {
426-
result.errors++;
427-
log.warn("Failed to handle duplicate key error for stock: {}", stock.getSymbol());
428-
}
429-
} catch (Exception e) {
430-
String errorMessage = getRootCauseMessage(e);
431-
if (errorMessage != null && (errorMessage.contains("Duplicate entry") ||
432-
errorMessage.contains("Lock wait timeout") ||
433-
errorMessage.contains("could not read a hi value"))) {
427+
} catch (DataIntegrityViolationException e) {
428+
// 중복 키 오류 발생 시 기존 종목으로 처리
434429
entityManager.clear();
435430
if (handleDuplicateKeyError(stock, e)) {
436431
result.updated++;
437432
} else {
438433
result.errors++;
439-
log.warn("Failed to handle error for stock: {} - {}", stock.getSymbol(), errorMessage);
434+
log.warn("Failed to handle duplicate key error for stock: {}", stock.getSymbol());
435+
}
436+
} catch (Exception e) {
437+
String errorMessage = getRootCauseMessage(e);
438+
if (errorMessage != null && (errorMessage.contains("Duplicate entry") ||
439+
errorMessage.contains("Lock wait timeout") ||
440+
errorMessage.contains("could not read a hi value"))) {
441+
entityManager.clear();
442+
if (handleDuplicateKeyError(stock, e)) {
443+
result.updated++;
444+
} else {
445+
result.errors++;
446+
log.warn("Failed to handle error for stock: {} - {}", stock.getSymbol(), errorMessage);
447+
}
448+
} else {
449+
result.errors++;
450+
log.warn("Failed to save stock: {} - {}", stock.getSymbol(), errorMessage);
440451
}
441-
} else {
442-
result.errors++;
443-
log.warn("Failed to save stock: {} - {}", stock.getSymbol(), errorMessage);
444452
}
445453
}
446-
}
447-
448-
// 트랜잭션 내에서 flush하여 ID 생성 확정
449-
entityManager.flush();
450-
451-
return result;
454+
455+
// 트랜잭션 내에서 flush하여 ID 생성 확정
456+
entityManager.flush();
457+
458+
return result;
459+
});
452460
}
453461

454462

0 commit comments

Comments
 (0)