Skip to content

Commit

Permalink
Merge pull request #132 from woowacourse-teams/be/refactor/#122-data-…
Browse files Browse the repository at this point in the history
…batch-insert

[BE-REFACTOR] Batch Insert를 통한 성능 개선 #122
  • Loading branch information
unifolio0 authored Aug 6, 2024
2 parents c695807 + b8a7a1f commit 9260b5e
Showing 1 changed file with 166 additions and 94 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@
import com.pokerogue.external.pokemon.dto.DataUrl;
import com.pokerogue.external.pokemon.dto.DataUrls;
import com.pokerogue.external.pokemon.dto.ability.AbilityResponse;
import com.pokerogue.external.pokemon.dto.pokemon.AbilityDataUrl;
import com.pokerogue.external.pokemon.dto.pokemon.PokemonDetail;
import com.pokerogue.external.pokemon.dto.pokemon.PokemonSaveResponse;
import com.pokerogue.external.pokemon.dto.pokemon.TypeDataUrl;
import com.pokerogue.external.pokemon.dto.pokemon.species.PokemonNameAndDexNumber;
import com.pokerogue.external.pokemon.dto.pokemon.species.PokemonSpeciesResponse;
import com.pokerogue.external.pokemon.dto.type.TypeMatchingResponse;
Expand All @@ -29,13 +27,16 @@
import com.pokerogue.helper.type.domain.PokemonTypeMatching;
import com.pokerogue.helper.type.repository.PokemonTypeMatchingRepository;
import com.pokerogue.helper.type.repository.PokemonTypeRepository;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.HttpClientErrorException;

import java.util.List;

@Service
@AllArgsConstructor
public class DataSettingService {
Expand All @@ -61,9 +62,9 @@ public class DataSettingService {
public void setData() {
deleteAll();

savePokemonTypes();
savePokemonAbilities();
savePokemons();
Map<String, PokemonType> pokemonTypeCacheByName = savePokemonTypes();
Map<String, PokemonAbility> pokemonAbilityCacheByName = savePokemonAbilities();
savePokemons(pokemonTypeCacheByName, pokemonAbilityCacheByName);
}

private void deleteAll() {
Expand All @@ -75,13 +76,17 @@ private void deleteAll() {
pokemonRepository.deleteAllInBatch();
}

private void savePokemonTypes() {
private Map<String, PokemonType> savePokemonTypes() {
DataUrls typeDataUrls = getTypeDataUrls();
List<TypeResponse> typeResponses = getTypeResponses(typeDataUrls);
List<PokemonType> pokemonTypes = getPokemonTypes(typeResponses);

pokemonTypeRepository.saveAll(pokemonTypes);
saveAllPokemonTypeMatching(typeDataUrls);
Map<String, PokemonType> pokemonTypeCacheByName = pokemonTypeRepository.saveAll(pokemonTypes).stream()
.collect(Collectors.toMap(PokemonType::getName, Function.identity()));

saveAllPokemonTypeMatching(typeDataUrls, pokemonTypeCacheByName);

return pokemonTypeCacheByName;
}

private DataUrls getTypeDataUrls() {
Expand All @@ -98,76 +103,98 @@ private List<TypeResponse> getTypeResponses(DataUrls dataUrls) {

private List<PokemonType> getPokemonTypes(List<TypeResponse> typeResponses) {
return typeResponses.stream()
.map(typeResponse -> dtoParser.getPokemonType(typeResponse, s3Service.getTypeImageFromS3(typeResponse.name())))
.toList();
.map(typeResponse -> dtoParser.getPokemonType(
typeResponse,
s3Service.getTypeImageFromS3(typeResponse.name()))
).toList();
}

private void saveAllPokemonTypeMatching(DataUrls typeDataUrls) {
private void saveAllPokemonTypeMatching(DataUrls typeDataUrls, Map<String, PokemonType> pokemonTypeCacheByName) {
List<PokemonTypeMatching> pokemonTypeMatchings = new ArrayList<>();
for (DataUrl dataUrl : typeDataUrls.results()) {
PokemonType fromPokemonType = pokemonTypeRepository.findByName(dataUrl.name())
.orElseThrow(() -> new GlobalCustomException(ErrorMessage.POKEMON_TYPE_NOT_FOUND));
if (!pokemonTypeCacheByName.containsKey(dataUrl.name())) {
throw new GlobalCustomException(ErrorMessage.POKEMON_TYPE_NOT_FOUND);
}
PokemonType fromPokemonType = pokemonTypeCacheByName.get(dataUrl.name());
TypeMatchingResponse typeMatchingResponse = pokeClient.getTypeMatchingResponse(dataUrl.getUrlId());

savePokemonTypeMatching(typeMatchingResponse, fromPokemonType);
savePokemonTypeMatching(typeMatchingResponse, fromPokemonType, pokemonTypeMatchings, pokemonTypeCacheByName);
}
}

private void savePokemonTypeMatching(TypeMatchingResponse typeMatchingResponse, PokemonType fromPokemonType) {
List<PokemonType> allPokemonTypes = pokemonTypeRepository.findAll();

saveDoubleDamageTypeMatching(typeMatchingResponse, fromPokemonType, allPokemonTypes);
saveHalfDamageTypeMatching(typeMatchingResponse, fromPokemonType, allPokemonTypes);
saveNoDamageTypeMatching(typeMatchingResponse, fromPokemonType, allPokemonTypes);
saveBasicDamageTypeMatching(fromPokemonType, allPokemonTypes);
}

private void saveDoubleDamageTypeMatching(TypeMatchingResponse typeMatchingResponse, PokemonType fromPokemonType, List<PokemonType> allPokemonTypes) {
for (DataUrl type : typeMatchingResponse.getDoubleDamageTo()) {
PokemonType toPokemonType = pokemonTypeRepository.findByName(type.name())
.orElseThrow(() -> new GlobalCustomException(ErrorMessage.POKEMON_TYPE_NOT_FOUND));
PokemonTypeMatching pokemonTypeMatching = new PokemonTypeMatching(fromPokemonType, toPokemonType, DOUBLE_DAMAGE);

pokemonTypeMatchingRepository.save(pokemonTypeMatching);
allPokemonTypes.remove(toPokemonType);
}
pokemonTypeMatchingRepository.saveAll(pokemonTypeMatchings);
}

private void saveHalfDamageTypeMatching(TypeMatchingResponse typeMatchingResponse, PokemonType fromPokemonType, List<PokemonType> allPokemonTypes) {
for (DataUrl type : typeMatchingResponse.getHalfDamageTo()) {
PokemonType toPokemonType = pokemonTypeRepository.findByName(type.name())
.orElseThrow(() -> new GlobalCustomException(ErrorMessage.POKEMON_TYPE_NOT_FOUND));
PokemonTypeMatching pokemonTypeMatching = new PokemonTypeMatching(fromPokemonType, toPokemonType, HALF_DAMAGE);

pokemonTypeMatchingRepository.save(pokemonTypeMatching);
allPokemonTypes.remove(toPokemonType);
}
private void savePokemonTypeMatching(
TypeMatchingResponse typeMatchingResponse,
PokemonType fromPokemonType,
List<PokemonTypeMatching> pokemonTypeMatchings,
Map<String, PokemonType> pokemonTypeCacheByName
) {
List<PokemonType> allPokemonTypes = new ArrayList<>(pokemonTypeCacheByName.values());

saveNotBasicDamageTypeMatching(
typeMatchingResponse.getDoubleDamageTo(),
fromPokemonType,
DOUBLE_DAMAGE,
allPokemonTypes,
pokemonTypeMatchings,
pokemonTypeCacheByName
);
saveNotBasicDamageTypeMatching(
typeMatchingResponse.getHalfDamageTo(),
fromPokemonType,
HALF_DAMAGE,
allPokemonTypes,
pokemonTypeMatchings,
pokemonTypeCacheByName
);
saveNotBasicDamageTypeMatching(
typeMatchingResponse.getNoDamageTo(),
fromPokemonType,
NO_DAMAGE,
allPokemonTypes,
pokemonTypeMatchings,
pokemonTypeCacheByName
);
saveBasicDamageTypeMatching(fromPokemonType, allPokemonTypes, pokemonTypeMatchings);
}

private void saveNoDamageTypeMatching(TypeMatchingResponse typeMatchingResponse, PokemonType fromPokemonType, List<PokemonType> allPokemonTypes) {
for (DataUrl type : typeMatchingResponse.getNoDamageTo()) {
PokemonType toPokemonType = pokemonTypeRepository.findByName(type.name())
.orElseThrow(() -> new GlobalCustomException(ErrorMessage.POKEMON_TYPE_NOT_FOUND));
PokemonTypeMatching pokemonTypeMatching = new PokemonTypeMatching(fromPokemonType, toPokemonType, NO_DAMAGE);

pokemonTypeMatchingRepository.save(pokemonTypeMatching);
private void saveNotBasicDamageTypeMatching(
List<DataUrl> dataUrls,
PokemonType fromPokemonType,
double multiply,
List<PokemonType> allPokemonTypes,
List<PokemonTypeMatching> pokemonTypeMatchings,
Map<String, PokemonType> pokemonTypeCacheByName
) {
for (DataUrl type : dataUrls) {
PokemonType toPokemonType = pokemonTypeCacheByName.get(type.name());
PokemonTypeMatching pokemonTypeMatching = new PokemonTypeMatching(fromPokemonType, toPokemonType, multiply);

pokemonTypeMatchings.add(pokemonTypeMatching);
allPokemonTypes.remove(toPokemonType);
}
}

private void saveBasicDamageTypeMatching(PokemonType fromPokemonType, List<PokemonType> allPokemonTypes) {
private void saveBasicDamageTypeMatching(
PokemonType fromPokemonType,
List<PokemonType> allPokemonTypes,
List<PokemonTypeMatching> pokemonTypeMatchings
) {
for (PokemonType toPokemonType : allPokemonTypes) {
PokemonTypeMatching pokemonTypeMatching = new PokemonTypeMatching(fromPokemonType, toPokemonType, BASIC_DAMAGE);

pokemonTypeMatchingRepository.save(pokemonTypeMatching);
pokemonTypeMatchings.add(pokemonTypeMatching);
}
}

private void savePokemonAbilities() {
private Map<String, PokemonAbility> savePokemonAbilities() {
DataUrls abilityDataUrls = getAbilityDataUrls();
List<AbilityResponse> abilityResponses = getAbilityResponses(abilityDataUrls);
List<PokemonAbility> pokemonAbilities = getPokemonAbilities(abilityResponses);

pokemonAbilityRepository.saveAll(pokemonAbilities);
return pokemonAbilityRepository.saveAll(pokemonAbilities).stream()
.collect(Collectors.toMap(PokemonAbility::getName, Function.identity()));
}

private DataUrls getAbilityDataUrls() {
Expand All @@ -188,72 +215,117 @@ private List<PokemonAbility> getPokemonAbilities(List<AbilityResponse> abilityRe
.toList();
}

private void savePokemons() {
private void savePokemons(
Map<String, PokemonType> pokemonTypeCacheByName,
Map<String, PokemonAbility> pokemonAbilityCacheByName
) {
CountResponse pokemonCountResponse = pokeClient.getPokemonResponsesCount();
List<Pokemon> pokemons = new ArrayList<>();
List<PokemonSaveResponse> pokemonSaveResponses = new ArrayList<>();
for (int offset = 0; offset < pokemonCountResponse.count(); offset += PACKET_SIZE) {
savePokemonsByOffset(offset);
savePokemonsByOffset(offset, pokemons, pokemonSaveResponses);
}
Map<String, Pokemon> pokemonCacheByName = pokemonRepository.saveAll(pokemons).stream()
.collect(Collectors.toMap(Pokemon::getName, Function.identity()));

savePokemonTypeMapping(pokemonSaveResponses, pokemonCacheByName, pokemonTypeCacheByName);
savePokemonAbilityMapping(pokemonSaveResponses, pokemonCacheByName, pokemonAbilityCacheByName);
}

private void savePokemonsByOffset(int offset) {
private void savePokemonsByOffset(
int offset,
List<Pokemon> pokemons,
List<PokemonSaveResponse> pokemonSaveResponses
) {
DataUrls pokemonDataUrls = pokeClient.getPokemonResponses(offset, PACKET_SIZE);
for (DataUrl dataUrl : pokemonDataUrls.results()) {
String id = dataUrl.getUrlId();
PokemonSaveResponse pokemonSaveResponse = pokeClient.getPokemonSaveResponse(id);
pokemonSaveResponses.add(pokemonSaveResponse);

savePokemon(pokemonSaveResponse, id);
savePokemon(pokemonSaveResponse, id, pokemons);
}
}

private void savePokemon(PokemonSaveResponse pokemonSaveResponse, String id) {
private void savePokemon(
PokemonSaveResponse pokemonSaveResponse,
String id,
List<Pokemon> pokemons
) {
PokemonDetail pokemonDetail = dtoParser.getPokemonDetails(pokemonSaveResponse);
DataUrl species = pokemonDetail.species();
PokemonNameAndDexNumber pokemonNameAndDexNumber = getPokemonNameAndDexNumber(getPokemonSpeciesResponse(species));
PokemonNameAndDexNumber pokemonNameAndDexNumber = getPokemonNameAndDexNumber(species);
String image;
try {
image = s3Service.postImageToS3(pokeClient.getImage(id));
} catch (HttpClientErrorException e) {
image = "이미지가 없습니다ㅠㅠ";
}
Pokemon pokemon = new Pokemon(pokemonNameAndDexNumber.pokedexNumber(), pokemonDetail.name(),
pokemonNameAndDexNumber.koName(), pokemonDetail.weight(), pokemonDetail.height(), pokemonDetail.hp(),
pokemonDetail.speed(), pokemonDetail.attack(), pokemonDetail.defense(), pokemonDetail.specialAttack(),
pokemonDetail.specialDefense(), pokemonDetail.totalStats(), image);

Pokemon savedPokemon = pokemonRepository.save(pokemon);
savePokemonTypeMapping(pokemonSaveResponse, savedPokemon);
savePokemonAbilityMapping(pokemonSaveResponse, savedPokemon);
Pokemon pokemon = new Pokemon(
pokemonNameAndDexNumber.pokedexNumber(),
pokemonDetail.name(),
pokemonNameAndDexNumber.koName(),
pokemonDetail.weight(),
pokemonDetail.height(),
pokemonDetail.hp(),
pokemonDetail.speed(),
pokemonDetail.attack(),
pokemonDetail.defense(),
pokemonDetail.specialAttack(),
pokemonDetail.specialDefense(),
pokemonDetail.totalStats(),
image);

pokemons.add(pokemon);
}

private PokemonSpeciesResponse getPokemonSpeciesResponse(DataUrl species) {
return pokeClient.getPokemonSpeciesResponse(species.getUrlId());
}

private PokemonNameAndDexNumber getPokemonNameAndDexNumber(PokemonSpeciesResponse pokemonSpeciesResponse) {
private PokemonNameAndDexNumber getPokemonNameAndDexNumber(DataUrl species) {
PokemonSpeciesResponse pokemonSpeciesResponse = pokeClient.getPokemonSpeciesResponse(species.getUrlId());
return dtoParser.getPokemonNameAndDexNumber(pokemonSpeciesResponse);
}

private void savePokemonTypeMapping(PokemonSaveResponse pokemonSaveResponse, Pokemon savedPokemon) {
List<TypeDataUrl> types = pokemonSaveResponse.types();
for (TypeDataUrl typeDataUrl : types) {
String name = typeDataUrl.getName();
PokemonType pokemonType = pokemonTypeRepository.findByName(name)
.orElseThrow(() -> new GlobalCustomException(ErrorMessage.POKEMON_TYPE_NOT_FOUND));
PokemonTypeMapping pokemonTypeMapping = new PokemonTypeMapping(savedPokemon, pokemonType);

pokemonTypeMappingRepository.save(pokemonTypeMapping);
}
private void savePokemonTypeMapping(
List<PokemonSaveResponse> pokemonSaveResponses,
Map<String, Pokemon> pokemonCacheByName,
Map<String, PokemonType> pokemonTypeCacheByName
) {
List<PokemonTypeMapping> pokemonTypeMappings = pokemonSaveResponses.stream()
.flatMap(pokemonSaveResponse -> pokemonSaveResponse.types().stream()
.map(typeDataUrl -> {
if (!pokemonCacheByName.containsKey(pokemonSaveResponse.name())) {
throw new GlobalCustomException(ErrorMessage.POKEMON_NOT_FOUND);
}
if (!pokemonTypeCacheByName.containsKey(typeDataUrl.getName())) {
throw new GlobalCustomException(ErrorMessage.POKEMON_TYPE_NOT_FOUND);
}
return new PokemonTypeMapping(
pokemonCacheByName.get(pokemonSaveResponse.name()),
pokemonTypeCacheByName.get(typeDataUrl.getName())
);
})
).toList();
pokemonTypeMappingRepository.saveAll(pokemonTypeMappings);
}

private void savePokemonAbilityMapping(PokemonSaveResponse pokemonSaveResponse, Pokemon savedPokemon) {
List<AbilityDataUrl> abilities = pokemonSaveResponse.abilities();
for (AbilityDataUrl abilityDataUrl : abilities) {
String name = abilityDataUrl.getName();
PokemonAbility pokemonAbility = pokemonAbilityRepository.findByName(name)
.orElseThrow(() -> new GlobalCustomException(ErrorMessage.POKEMON_ABILITY_NOT_FOUND));
PokemonAbilityMapping pokemonAbilityMapping = new PokemonAbilityMapping(savedPokemon, pokemonAbility);

pokemonAbilityMappingRepository.save(pokemonAbilityMapping);
}
private void savePokemonAbilityMapping(
List<PokemonSaveResponse> pokemonSaveResponses,
Map<String, Pokemon> pokemonCacheByName,
Map<String, PokemonAbility> pokemonAbilityCacheByName
) {
List<PokemonAbilityMapping> pokemonAbilityMappings = pokemonSaveResponses.stream()
.flatMap(pokemonSaveResponse -> pokemonSaveResponse.abilities().stream()
.map(abilityDataUrl -> {
if (!pokemonCacheByName.containsKey(pokemonSaveResponse.name())) {
throw new GlobalCustomException(ErrorMessage.POKEMON_NOT_FOUND);
}
if (!pokemonAbilityCacheByName.containsKey(abilityDataUrl.getName())) {
throw new GlobalCustomException(ErrorMessage.POKEMON_ABILITY_NOT_FOUND);
}
return new PokemonAbilityMapping(
pokemonCacheByName.get(pokemonSaveResponse.name()),
pokemonAbilityCacheByName.get(abilityDataUrl.getName()));
})
).toList();
pokemonAbilityMappingRepository.saveAll(pokemonAbilityMappings);
}
}

0 comments on commit 9260b5e

Please sign in to comment.