이웃동네 List를 Column으로 가지는 동네 Entity 생성하기

구동현·2024년 5월 14일

이전 게시물에서는 땅땅땅 프로젝트를 위해 대한민국 행정구역을 DB화 하는 과정에 대해 알아보았습니다. DB화 된 행정구역을 바탕으로, 이번 게시물에서는 동네 Entity를 생성하고 동네 Entity의 Column으로 이웃 동네 리스트를 가지는 방법에 대해 알아보겠습니다.
이전 게시물 보러가기
깃허브 보러가기


Entity와 Repository 생성

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "towns")
public class Town {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String name;

    @Column(columnDefinition = "TEXT")
    private String neighborIdList;

    public Town(String name, String idList) {
        this.name = name;
        this.neighborIdList = idList;
    }

}

Town 엔티티의 neighborIdList은 string값으로 있습니다. 이유는 List<Long>타입을 Column에 넣기 위해 objectMapper로 String으로 바꿔줄 계획입니다.


Service 생성

@RequiredArgsConstructor
@Service
public class TownService {

    private final static Double DEFAULT_X = 111.35;
    private final static Double DEFAULT_Y = 88.80;
    private final static Double DEFAULT_DISTANCE = 5.0;
    private final TownRepository townRepository;
    private final AdminDistrictRepository adminDistrictRepository;
    private final ObjectMapper objectMapper;

    @Transactional
    public void createTown() throws JsonProcessingException {
        List<AdminDistrict> adminDistricts = adminDistrictRepository.findAll();

        for (AdminDistrict adminDistrict : adminDistricts) {
            List<Long> idList = new ArrayList<>();
            String name = getTownName(adminDistrict);

            for (AdminDistrict comparison : adminDistricts) {
                double x = Math.pow((adminDistrict.getX() - comparison.getX()) * DEFAULT_X, 2.0);
                double y = Math.pow((adminDistrict.getY() - comparison.getY()) * DEFAULT_Y, 2.0);

                double distance = Math.sqrt(x + y);

                if (distance < DEFAULT_DISTANCE) {
                    idList.add(comparison.getId());
                }
            }

            String neighborIdList = objectMapper.writeValueAsString(idList);
            Town town = new Town(name, neighborIdList);
            townRepository.save(town);
        }
    }

    public String getTownName(AdminDistrict adminDistrict) {
        StringBuilder name = new StringBuilder(adminDistrict.getState());

        if (!adminDistrict.getCounty().isEmpty()) {
            name.append(" ").append(adminDistrict.getCounty());
        }

        if (!adminDistrict.getCity().isEmpty()) {
            name.append(" ").append(adminDistrict.getCity());
        }

        if (!adminDistrict.getDistrict().isEmpty()) {
            name.append(" ").append(adminDistrict.getDistrict());
        }

        if (!adminDistrict.getVillage().isEmpty()) {
            name.append(" ").append(adminDistrict.getVillage());
        }

        return String.valueOf(name);
    }

}

createTown

메소드는 생각보다 간단합니다.

List<AdminDistrict> adminDistricts = adminDistrictRepository.findAll를 통해 모든 행정구역 entity를 가져오고 하나씩 순회합니다.

한 엔티티를 가지고 나머지 엔티티를 하나씩 비교해가며 두점사이의 거리가 5km 이하일때만, neighborList에 넣어줍니다.

순회가 끝나면 objectMapper.writeValueAsString을 통해 String 값으로 변경한 다음 Entity로 생성해서 레포지토리에 넣어줍니다.

getTownName

StringBuilder를 통해 경기도 + 하남시 + 덕풍북로 이렇게 column값들을 더해주며 Town의 Name Column을 완성합니다.


Test

    @Test
    @Rollback(false)
    @DisplayName("town DB에 저장")
    void saveTownDB() throws JsonProcessingException {
        //given, when, then
        townService.createTown();
    }

이전 게시물과 마찬가지로, 테스트 코드로 실행해주면 됩니다.
다만 이전 게시물의 테스트코드를 실행시키고 지금 테스트 코드를 실행시켜주어야 합니다.

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@SpringBootTest
class CityDbDdangApplicationTests {

    @Autowired
    TownService townService;

    @Autowired
    AdminDistrictService adminDistrictService;

    @Test
    @Order(1)
    @Rollback(false)
    @DisplayName("townList 엑셀 파일 DB에 저장")
    void saveTownListDB() {
        //given, when,then
        adminDistrictService.createAdminDistrict();
    }

    @Test
    @Order(2)
    @Rollback(false)
    @DisplayName("town DB에 저장")
    void saveTownDB() throws JsonProcessingException {
        //given, when, then
        townService.createTown();
    }

}

이 문제는 TestMethodOrder 어노테이션으로 해결할 수 있었습니다.

테스트코드가 정상적으로 돌아갑니다.

DB에도 값이 잘 들어가네요.


이렇게 완성한 Entity를 통해 사용자의 town.neighbor_id_list를 objectMapper로 읽어와서
List로 변환한 다음에, 그 리스트에 해당하는 경매글을 조회하여야 할까요? 답은 아닙니다. objectMapper를 매번 생성하면 리소스 낭비가 심하기 때문에, Column의 type을 String에서 json으로 변경해주겠습니다.

터미널 명령어를 해도 좋고, 인텔리제이에서 하셔도 좋습니다.
저는 편의성을 위해 인텔리제이에서 하겠습니다.

1. tables>towns>modify table

2. column> data type 변경

변경 전,

변경 후,

이제는 town엔티티의 이웃 동네 list를 조회할때,
objectMapper를 사용하지 않아도 됩니다.

profile
개발합시다

0개의 댓글