SpringとPostGISとSpring JPAを連携させる方法です。
初めに各種ライブラリの説明を行い、後にソースコードを配置します。実装にのみ興味がある方は後半に飛んでください。
PostGIS
PostGISは空間データを取り扱うためのPostgreSQLにおけるデータフォーマットです
ライセンス
PostGIS自体はGPLですが、単に使用するだけの場合はGPL汚染されません。PostGIS自体を改変する場合にGPLとする必要があると明記されています
postgis jdbc driver
2.5.0より前はこれに従うらしい
けど、それ以降は以下の記載があり、以下に移管されている様子。
As of June 2015, PostGIS JDBC development has been split from PostGIS core and is now on github.
基本的にpostgis jdbc独自のPGeometryは汎用性が低いため、jts準拠のクラス群を用いた方が扱いやすい印象です。
Spring Data JPA
Spring Data JPAはHiberneteをデフォルトでラップしたORM(Object Rerational Mapping)です。
Hibernate Spatial
Hibernate Spatialは PostgreSql/PostGIS, MySQL, Microsoft SQL 等各種DBで定義されている空間データをGenericにORMできるライブラリです。
普通に調べると以下のhttpなホームページが先に出てきますが、こちらは相当古くHibernate4.0程度までしか記載されていません。上記のホームページをご覧ください。ちなみに、現状の最新spring bootである2.7.3に対応するHibernateは5.6.10です。
ライセンス
ただHibernate SpatialはLGPLです。
コード
POM
Kotlinベースなので、Javaの方には必要ない記載が多いですが、以下となります。Spring系列以外に追加するのはpostgresqlのjdbcとhibernate spatialです。
${hibernate.version}はspring-boot-dependencies-2.7.3.pomで定義してくれているおかげで特に何もせずに関連づいたversionを参照できます。この場合5.6.10Final.
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>demo</description>
<properties>
<java.version>17</java.version>
<kotlin.version>1.6.21</kotlin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-spatial</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<configuration>
<args>
<arg>-Xjsr305=strict</arg>
</args>
<compilerPlugins>
<plugin>spring</plugin>
<plugin>jpa</plugin>
</compilerPlugins>
</configuration>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-allopen</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-noarg</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
application.properties
Geometry等を読み込めるようにpostgisをjpa databaseとして指定します。
spring.datasource.driver-class-name=org.postgresql.Driver spring.datasource.url=jdbc:postgresql://localhost:5432/geo spring.datasource.username=postgres spring.datasource.password=postgres spring.jpa.database-platform=org.hibernate.spatial.dialect.postgis.PostgisDialect
Database 定義
postgisに適応したPostgreSQLの使用を始めるのはDockerが用意されているので、それを利用するのが手っ取り早いです。
DB初期化スクリプトとして以下を使用することにします。
CREATE DATABASE geo;
\c geo;
create extension postgis;
create table mygeo(
id serial primary key ,
name varchar(32),
point geometry(POINT, 4326),
linestring geometry(LINESTRING, 4326),
polygon geometry(POLYGON, 4326)
);
Entity
通常のSpring Data Jpaと同様です。Hibernate Spatialではorg.locationtech.jts.geomのライブラリを使用してマッピング可能です。
package com.example.demo
import org.locationtech.jts.geom.LineString
import org.locationtech.jts.geom.Point
import org.locationtech.jts.geom.Polygon
import javax.persistence.*
@Entity
@Table(name = "mygeo")
data class GeoEntity(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private var id: Int = 0,
@Column(name = "name")
private val name:String?,
@Column(name = "point")
private val point: Point?,
@Column(name = "linestring")
private val linestring: LineString?,
@Column(name = "polygon")
private val polygon: Polygon?
)
Repository
interface PostgisRepository : JpaRepository<GeoEntity, Int>{
}
Controller
package com.example.demo
import org.locationtech.jts.geom.Coordinate
import org.locationtech.jts.geom.GeometryFactory
import org.locationtech.jts.geom.LineString
import org.locationtech.jts.geom.LinearRing
import org.locationtech.jts.geom.Point
import org.locationtech.jts.geom.Polygon
import org.locationtech.jts.geom.PrecisionModel
import org.locationtech.jts.geom.impl.CoordinateArraySequence
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController
@RestController
class MyController(
@Autowired val repository: PostgisRepository
) {
val factory = GeometryFactory(PrecisionModel(), 4326)
@GetMapping("/")
fun test(): String{
//POINT
val pointSequence = CoordinateArraySequence(arrayOf(Coordinate(1.0, 1.0)))
val point = Point(pointSequence, factory)
//LINESTRING
val lineSequence = CoordinateArraySequence(arrayOf(Coordinate(1.0, 1.0), Coordinate(2.0, 2.0)))
val lineString = LineString(lineSequence, factory)
//POLYGON
val polygonSequence = CoordinateArraySequence(arrayOf(Coordinate(1.0, 1.0), Coordinate(2.0, 2.0), Coordinate(1.0, 2.0), Coordinate(1.0, 1.0)))
val linearRing = LinearRing(polygonSequence, factory)
val polygon = Polygon(linearRing, arrayOf(), factory)
repository.save(GeoEntity(name = "test", point = point, linestring = lineString, polygon = polygon))
val data = repository.findAll()
data.forEach{ println(it) }
return "test"
}
}
