semtax의 개발 일지

JPA를 이용해서 RDB에 공간정보 저장하기 본문

개발/Java

JPA를 이용해서 RDB에 공간정보 저장하기

semtax 2020. 5. 26. 23:41
반응형

개요

이번 포스팅에서는 JPA를 이용해서 MySQL에 위치정보를 저장하는 법에 대해서 다뤄보도록 하겠다.

                           ​

공간 정보(Geometry 타입)?

                           ​

MySQL(사실, 다른 RDB에서도) 에서는, GPS좌표나 다각형과 같은 공간/기하 데이터를 저장할 수 있는 Geometry 타입을 제공한다.

                           ​

MySQL과 같은 경우 아래 목록의 타입들을 제공한다

                           ​

데이터 타입 설명
Point 좌표 공간에서 한 지점의 위치를 표시
[ Ex : Point(10,10) ]
LineString 다수의 점을 연결해주는 선분
[ Ex : LINESTRING(10 10, 20 25, 15 40) ]
Polygon 다수의 선분들이 연결되있는 다각형
[ Ex : POLYGON(10 10, 10 20, 20 20, 20 10, 10 10) ]
Multi-Point 다수개의 점들의 집합
[ Ex : MULTIPOINT(10 10, 30 20) ]
Multi-LineString 다수개의 선분들의 집합
[ Ex : MULTILINESTRING((10 10, 20 25, 15 40), (50 50, 85 105, 120 160)) ]
Multi-Polygon 다수개의 다각형들의 집합
[ Ex : MULTIPOLYGON((10 10, 10 20, 20 20, 20 10, 10 10), (10 10, 10 20, 20 20, 20 10, 10 10)) ]
GeomCollection 모든 공간데이터들의 집합
[ Ex : GEOMCOLLECTION(POINT(10,10), LINESTRING(10 10, 20 25, 15 40)) ]

                           ​                           ​

                           ​

이제 이러한 공간정보를 스프링에서 제공하는 ORM인 JPA(+hibernate)를 이용해서 저장해보는 예제를 작성해보자.

해당 예제에서는 Point(위도/경도) 정보를 저장/불러오는 예제를 다루도록 하겠다.

예제

먼저 스프링 프로젝트를 만들고 아래와 같이 Maven에 의존성을 추가해주자.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.semtax.geometryexample</groupId>
    <artifactId>geometryexample</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.1.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
            <version>2.1.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>test</scope>
            <version>1.4.199</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>2.1.6.RELEASE</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-spatial</artifactId>
            <version>5.3.10.Final</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.8</version>
        </dependency>
        <dependency>
            <groupId>com.github.gavlyukovskiy</groupId>
            <artifactId>p6spy-spring-boot-starter</artifactId>
            <version>1.5.7</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.19</version>
        </dependency>
        <dependency>
            <groupId>ch.vorburger.mariaDB4j</groupId>
            <artifactId>mariaDB4j</artifactId>
            <version>2.2.3</version>
        </dependency>
    </dependencies>

    <build>
        <sourceDirectory>src</sourceDirectory>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.0</version>
            </plugin>
        </plugins>
    </build>

</project>

공간 데이터 저장을 위해 hibernate의 hibernate-spatial 라이브러리를 의존성으로 추가했다.

해당 라이브러리의 버전은 스프링부트의 hibernate 버전에 맞춰주면 된다.

다음으로 아래와 같이 application.properties 를 작성해준다.

spring.datasource.hikari.maximum-pool-size=10

spring.datasource.url=jdbc:mysql://localhost:3306/testdb?useSSL=false&allowPublicKeyRetrieval=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root

spring.jpa.hibernate.use-new-id-generator-mappings=false

spring.jpa.hibernate.ddl-auto=create
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
spring.jpa.properties.hibernate.dialect=org.hibernate.spatial.dialect.mysql.MySQL56InnoDBSpatialDialect
spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext

server.port=45000

먼저 MySQL 8 이상을 쓰는 경우 위와 같이 datasource.url을 작성해야 에러가 발생하지 않는다.

(기본적으로 SSL 통신을 사용함 + UTC 시간 지정이 필요)

그리고, JPA 구현체로 hibernate를 사용하겠다는 설정과, 기타 설정들을 지정해준다.

다음으로 아래와 같이 위치정보가 포함된 Entity 객체를 작성해준다.

package main.java.entity;

import org.springframework.data.geo.Point;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.util.Objects;

@Entity
public class PointEntry {

    @Id
    @GeneratedValue
    private Long id;

    private Point point;

    public PointEntry() {
    }

    public PointEntry(Point point) {
        this.point = point;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Point getPoint() {
        return point;
    }

    public void setPoint(Point point) {
        this.point = point;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        PointEntry that = (PointEntry) o;
        return Objects.equals(id, that.id) &&
                Objects.equals(point, that.point);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, point);
    }
}

그리고 나서, 위에서 선언한 엔티티(개체)에 대한 Repository 를 작성해준다.

package main.java.repositories;


import main.java.entity.PointEntry;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface PointEntryRepository extends JpaRepository<PointEntry, Long> {
}

다음으로, 실제 위치정보를 저장하고 불러오는 컨트롤러들을 작성해준다.

package main.java.controller;


import main.java.entity.PointEntry;
import main.java.repositories.PointEntryRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Point;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.Random;


@RestController
public class TestController {
    @Autowired
    PointEntryRepository pointEntryRepository;

    @GetMapping("/")
    public List<PointEntry> mainPage(){
        return pointEntryRepository.findAll();
    }

    @GetMapping("/insert")
    public String insertPage(){
        Random r = new Random();

        PointEntry pointEntry = new PointEntry(new Point(r.nextDouble(),r.nextDouble()));
        pointEntryRepository.save(pointEntry);
        return "success";
    }
}

위의 컨트롤러에서는, "/insert" 페이지를 통해 랜덤으로 위도/경도 정보를 저장하고 "/" 페이지를 이용해서 지금까지 저장한 모든 위치정보(위도/경도) 들을 출력 해주는 역할을 수행하게 된다.

마지막으로 Main 함수를 아래와 같이 작성해준다.

package main.java;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringGeometryExample {

    public static void main(String[] args) {
        SpringApplication.run(SpringGeometryExample.class, args);
    }
}

이제 실제로 위에서 작성한 예제를 실행해보도록 하자.

실행 결과

먼저 아래와 같이 /insert 페이지를 연속적으로 방문해서 위도/경도 데이터를 넣어주자.

그리고 아래 페이지를 방문하면 위도/경도 목록들을 얻을 수 있다.

출처

  1. http://www.gurubee.net/lecture/2921
  2. https://mkyong.com/spring-boot/spring-boot-spring-data-jpa-mysql-example/
  3. https://stackoverflow.com/questions/40240955/why-i-have-this-error-trying-to-use-hibernate-spatial-into-a-spring-boot-project
  4. https://www.baeldung.com/hibernate-spatial
반응형
Comments