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"
}
}