• 티스토리 홈
  • 프로필사진
    알쓸개잡
  • 방명록
  • 공지사항
  • 태그
  • 블로그 관리
  • 글 작성
알쓸개잡
  • 프로필사진
    알쓸개잡
    • 분류 전체보기 (92)
      • 스프링부트 (52)
      • AWS (5)
      • 쿠버네티스 (7)
      • 자바 (19)
      • 인프라 (0)
      • ETC (8)
  • 방문자 수
    • 전체:
    • 오늘:
    • 어제:
  • 최근 댓글
      등록된 댓글이 없습니다.
    • 최근 공지
        등록된 공지가 없습니다.
      • 반응형
      # Home
      # 공지사항
      #
      # 태그
      # 검색결과
      # 방명록
      • Spring Data JPA 에서 공간 정보(Spatial Data) 사용하기
        2023년 11월 01일
        • 알쓸개잡
        • 작성자
        • 2023.11.01.:33

        Spatial Data Type은 공간 데이터를 저장하고 처리하기 위해 데이터베이스에서 제공하는 데이터 유형이다. 

        이 데이터 유형은 지리적인 위치 정보나 공간 상의 객체를 나타내는 데 사용된다.

        일반적으로 데이터베이스 시스템에서는 Spatial Data Type을 지원하기 위한 확장 기능이 제공된다.

        Spring Data JPA + mariadb 간단한 샘플 코드를 통해서 Spatial Data Type을 사용하는 방법에 대해서 기록한다.

         

        사전조건

        • 로컬 환경에 mariadb 데이터 베이스가 실행되고 있다고 가정하겠다.
        • mariadb 접속 정보 설정이 되어 있다고 가정하겠다.

        SpringBoot 3.1 이상 버전의 경우 springboot docker compose를 사용하여 mariadb 설치 및 설정 없이 간단하게 테스트 환경을 구성할 수 있다.

        springboot docker compose 사용에 대해서는 다음 포스팅을 참고하기 바란다.

        2023.10.22 - [스프링부트] - spring boot 3.1 docker compose support

         

         

        테이블 생성

         

        CREATE TABLE `geometry_test` (
          `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
          `geo_point` geometry DEFAULT NULL,
          `geo_linestring` geometry DEFAULT NULL,
          `geo_polygon` geometry DEFAULT NULL,
          PRIMARY KEY (`id`)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

        point, linestring, polygon Spatial Data를 저장하는 테이블을 생성한다.

        geometry 타입 대신에 각각 point, linestring, polygon으로 구체화된 타입으로 지정할 수도 있다.

         

        Dependency

        Spring Data JPA와 공간 데이터 처리를 위해서는 다음과 같은 추가 디펜던시가 필요하다.

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
            <version>3.1.0</version>
        </dependency>
        <dependency>
        	<groupId>org.hibernate.orm</groupId>
        	<artifactId>hibernate-spatial</artifactId>
        	<version>6.2.2.Final</version>
        </dependency>

        parent  디펜던시로 spring-boot-starter-parent 가 지정되어 있다면 <version>은 생략 가능하다.

         

        Entity and DTO 정의

        Entity 정의

        package com.example.jpa.entity;
        
        import jakarta.persistence.*;
        import lombok.*;
        import org.locationtech.jts.geom.Geometry;
        
        @Entity
        @Table(name = "geometry_test")
        @NoArgsConstructor(access = AccessLevel.PROTECTED)
        @Getter
        @ToString(callSuper = true)
        public class GeoMetryTest{
        	@Id
        	@GeneratedValue(strategy = GenerationType.IDENTITY)
        	@Column(name = "id")
        	private Long gid;
        
        	@Column(name = "geo_point", nullable = false)
        	private Geometry point;
        
        	@Column(name = "geo_linestring", nullable = false)
        	private Geometry lineString;
        
        	@Column(name = "geo_polygon", nullable = false)
        	private Geometry polygon;
        
        	@Builder
        	public GeoMetryTest(Long gid, Geometry point, Geometry lineString, Geometry polygon) {
        		this.gid = gid;
        		this.point = point;
        		this.lineString = lineString;
        		this.polygon = polygon;
        	}
        }

        point, lineString, polygon 모두 Geometry 타입으로 선언되었지만, 각각 Point, LineString, Polygon 타입으로 선언 가능하다.

         

        DTO 정의

        package com.example.jpa.dto;
        
        import com.example.jpa.entity.GeoMetryTest;
        import lombok.*;
        import org.geolatte.geom.Point;
        import org.locationtech.jts.geom.Geometry;
        import org.locationtech.jts.geom.LineString;
        import org.locationtech.jts.geom.Polygon;
        
        @Getter
        @Setter
        @ToString
        public class GeoMetryTestDto {
        	private Long gid;
        	private Geometry point;
        	private Geometry lineString;
        	private Geometry polygon;
        
        	@Builder
        	public GeoMetryTestDto(Long gid, Geometry point, Geometry lineString, Geometry polygon) {
        		this.gid = gid;
        		this.point = point;
        		this.lineString = lineString;
        		this.polygon = polygon;
        	}
        
        	public GeoMetryTest entity() {
        		return GeoMetryTest.builder()
        			.gid(gid)
        			.point(point)
        			.lineString(lineString)
        			.polygon(polygon)
        			.build();
        	}
        }

        point, lineString, polygon 모두 Geometry 타입으로 선언되었지만, 각각 Point, LineString, Polygon 타입으로 선언 가능하다.

         

        Repository 생성

        package com.example.jpa.repository;
        
        import com.example.jpa.dto.GeoMetryTestDto;
        import com.example.jpa.entity.GeoMetryTest;
        import org.springframework.data.jpa.repository.JpaRepository;
        import org.springframework.stereotype.Repository;
        
        import java.util.Optional;
        
        @Repository
        public interface GeoMetryTestRepository extends JpaRepository<GeoMetryTest, Long>{
        	Optional<GeoMetryTest> findByGid(Long gid);
        }

         

        테스트

        geometry_test 테이블에 샘플 데이터를 생성한다.

        INSERT INTO geometry_test (geo_point, geo_linestring, geo_polygon) values(ST_GEOMFROMTEXT('Point(1 1)'), ST_GEOMFROMTEXT('LineString(1 1, 2 2, 3 3)'), ST_GEOMFROMTEXT('POLYGON((1 1,1 5,4 9,6 9,9 3,7 2,1 1))'));

         

        테스트 코드

        package com.example.jpa.repository;
        
        import com.example.jpa.dto.GeoMetryTestDto;
        import com.example.jpa.entity.GeoMetryTest;
        import org.assertj.core.api.Assertions;
        import org.junit.jupiter.api.DisplayName;
        import org.junit.jupiter.api.Test;
        import org.locationtech.jts.geom.*;
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
        import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
        import org.springframework.test.annotation.Commit;
        
        import java.util.List;
        import java.util.Optional;
        
        import static org.junit.jupiter.api.Assertions.*;
        
        @DataJpaTest
        @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
        class GeoMetryTestRepositoryTest {
        	@Autowired
        	GeoMetryTestRepository geoMetryTestRepository;
        
        	@Test
        	@DisplayName("geometry 조회 테스트")
        	void select_geometry_test() {
        		Optional<GeoMetryTest> byGid = geoMetryTestRepository.findByGid(1L);
        		Assertions.assertThat(byGid).isPresent();
        		GeoMetryTest geoMetryTest = byGid.get();
        		Geometry lineString = geoMetryTest.getLineString();
        		Geometry point = geoMetryTest.getPoint();
        		Geometry polygon = geoMetryTest.getPolygon();
        
        		Assertions.assertThat(point).isInstanceOf(Point.class);
        		Assertions.assertThat(lineString).isInstanceOf(LineString.class);
        		Assertions.assertThat(polygon).isInstanceOf(Polygon.class);
        	}
        
        	@Test
        	@DisplayName("geometry 저장 테스트")
        	@Commit
        	void save_geometry_test() {
        		GeometryFactory geometryFactory = new GeometryFactory();
        		Point point = geometryFactory.createPoint(new Coordinate(2.0, 2.0));
        		List<Coordinate> coordinates = List.of(
        			new Coordinate(1.0, 1.0),
        			new Coordinate(2.0, 2.0),
        			new Coordinate(3.0, 3.0)
        		);
        		LineString lineString = geometryFactory.createLineString(coordinates.toArray(new Coordinate[0]));
        		List<Coordinate> coordinatesPolygon = List.of(
        			new Coordinate(1.0, 1.0),
        			new Coordinate(1.0, 5.0),
        			new Coordinate(4.0, 9.0),
        			new Coordinate(6.0, 9.0),
        			new Coordinate(9.0, 3.0),
        			new Coordinate(7.0, 2.0),
        			new Coordinate(1.0, 1.0)
        		);
        		Polygon polygon = geometryFactory.createPolygon(coordinatesPolygon.toArray(new Coordinate[0]));
        		GeoMetryTestDto geoMetryTestDto = GeoMetryTestDto.builder()
        			.point(point)
        			.lineString(lineString)
        			.polygon(polygon)
        			.build();
        
        		GeoMetryTest entity = geoMetryTestDto.entity();
        		GeoMetryTest save = geoMetryTestRepository.save(entity);
        		Assertions.assertThat(save).isNotNull();
        	}
        }

        select_geometry_test 테스트 코드 실행

        select_geometry_test 를 실행하여 디버깅한 결과는 다음과 같다.

        GeoMetry 타입의 lineString, point, polygon 변수의 인스턴스는 각각 LineString, Point, Polygon으로 저장된 데이터 유형에 맞게 적절한 객체 타입으로 변환됨을 알 수 있다.

         

        save_geometry_test 테스트 코드 실행

        Point, LineString, Polygon 인스턴스를 생성하여 저장해보는 테스트다.

        실제 DB에 INSERT 되었는지 확인하기 위해서 @Commit 어노테이션을 지정하였다.

        테스트 코드를 실행 후 geometry_test 테이블은 다음과 같다.


        참고링크

        https://mariadb.com/kb/en/geographic-geometric-features/

         

        Geographic & Geometric Features

         

        mariadb.com

         

        저작자표시 비영리 변경금지 (새창열림)

        '스프링부트' 카테고리의 다른 글

        Spring REST 서비스 예외 처리 방법  (0) 2023.11.09
        Spring Boot Embedded Tomcat 서버 설정  (0) 2023.11.03
        Spring REST 컨트롤러에서 HTTP 헤더 읽기  (0) 2023.10.29
        Spring에서 특정 HTTP 응답 상태 반환하기  (0) 2023.10.28
        REST PUT vs PATCH  (0) 2023.10.22
        다음글
        다음 글이 없습니다.
        이전글
        이전 글이 없습니다.
        댓글
      조회된 결과가 없습니다.
      스킨 업데이트 안내
      현재 이용하고 계신 스킨의 버전보다 더 높은 최신 버전이 감지 되었습니다. 최신버전 스킨 파일을 다운로드 받을 수 있는 페이지로 이동하시겠습니까?
      ("아니오" 를 선택할 시 30일 동안 최신 버전이 감지되어도 모달 창이 표시되지 않습니다.)
      목차
      표시할 목차가 없습니다.
        • 안녕하세요
        • 감사해요
        • 잘있어요

        티스토리툴바