Hibernate Spatial 소개

1. 소개

이 기사에서는 Hibernate, hibernate-spatial의 공간 확장을 살펴 보겠습니다.

버전 5부터 Hibernate Spatial은 지리 데이터 작업을위한 표준 인터페이스를 제공합니다 .

2. Hibernate Spatial에 대한 배경

지리 데이터에는 Point, Line, Polygon 과 같은 엔티티의 표현이 포함됩니다 . 이러한 데이터 유형은 JDBC 사양의 일부가 아니므로 JTS (JTS Topology Suite)는 공간 데이터 유형을 나타내는 표준이되었습니다.

JTS와 별도로 Hibernate 공간은 Geolatte-geom도 지원합니다. 이는 JTS에서 사용할 수없는 일부 기능이있는 최신 라이브러리입니다.

두 라이브러리는 이미 hibernate-spatial 프로젝트에 포함되어 있습니다. 한 라이브러리를 다른 라이브러리보다 사용하는 것은 단순히 데이터 유형을 가져 오는 jar에 대한 질문입니다.

Hibernate 공간은 Oracle, MySQL, PostgreSQLql / PostGIS 및 기타 몇 가지 다른 데이터베이스와 같은 다른 데이터베이스를 지원하지만 데이터베이스 특정 기능에 대한 지원은 균일하지 않습니다.

hibernate가 주어진 데이터베이스에 대한 지원을 제공하는 함수 목록을 확인하려면 최신 Hibernate 문서를 참조하는 것이 좋습니다.

이 기사에서는 MySQL의 전체 기능을 유지하는 인 메모리 Mariadb4j를 사용합니다.

Mariadb4j 및 MySql의 구성은 비슷하며 mysql-connector 라이브러리도이 두 데이터베이스 모두에서 작동합니다.

3 . Maven 종속성

간단한 최대 절전 공간 프로젝트를 설정하는 데 필요한 Maven 종속성을 살펴 보겠습니다.

 org.hibernate hibernate-core 5.2.12.Final   org.hibernate hibernate-spatial 5.2.12.Final   mysql mysql-connector-java 6.0.6   ch.vorburger.mariaDB4j mariaDB4j 2.2.3  

절전 공간적 종속성은 공간 데이터 유형에 대한 지원을 제공하는 것입니다. 최신 버전의 hibernate-core, hibernate-spatial, mysql-connector-java 및 mariaDB4j는 Maven Central에서 구할 수 있습니다.

4. Hibernate Spatial 설정

첫 번째 단계는 만드는 것입니다 hibernate.properties 에서 자원 디렉토리를 :

hibernate.dialect=org.hibernate.spatial.dialect.mysql.MySQL56SpatialDialect // ...

hibernate-spatial에 특정한 유일한 것은 MySQL56SpatialDialect dialect 입니다. 이 방언은 MySQL55Dialect 방언을 확장하고 공간 데이터 유형과 관련된 추가 기능을 제공합니다.

속성 파일로드, SessionFactory 생성 및 Mariadb4j 인스턴스 인스턴스화에 특정한 코드 는 표준 최대 절전 모드 프로젝트에서와 동일합니다.

5 . 지오메트리 유형 이해

Geometry 는 JTS의 모든 공간 유형에 대한 기본 유형입니다. 이는 Point , Polygon 및 기타와 같은 다른 유형이 Geometry 에서 확장 됨을 의미합니다 . 형상 받는 자바 대응을 입력 형상 뿐만 아니라 MySQL을 입력합니다.

유형 의 문자열 표현을 구문 분석하여 Geometry 인스턴스를 얻습니다 . JTS에서 제공 하는 유틸리티 클래스 WKTReader를 사용하여 잘 알려진 텍스트 표현을 Geometry 유형 으로 변환 할 수 있습니다 .

public Geometry wktToGeometry(String wellKnownText) throws ParseException { return new WKTReader().read(wellKnownText); }

이제이 메서드가 작동하는지 살펴 보겠습니다.

@Test public void shouldConvertWktToGeometry() { Geometry geometry = wktToGeometry("POINT (2 5)"); assertEquals("Point", geometry.getGeometryType()); assertTrue(geometry instanceof Point); }

우리가 볼 수 있듯이, 메서드의 반환 형식이 경우에도 읽기 () 방법은 기하학은 실제 인스턴스가의 즉, 포인트 .

6. DB에 포인트 저장

이제 우리는 좋은 A를 어떤 아이디어가 있는지 형상 어떻게 얻을하는 유형이며, 포인트를 에서 문자열을 ,의를 살펴 보자 PointEntity를 :

@Entity public class PointEntity { @Id @GeneratedValue private Long id; private Point point; // standard getters and setters }

엔티티 PointEntity 에는 공간 유형 Point 가 포함되어 있습니다 . 앞서 설명한 것처럼 Point 는 두 개의 좌표로 표시됩니다.

public void insertPoint(String point) { PointEntity entity = new PointEntity(); entity.setPoint((Point) wktToGeometry(point)); session.persist(entity); }

insertPoint () 메서드 는 Point 의 WKT (Well-Known Text) 표현 을 받아 Point 인스턴스 로 변환 하고 DB에 저장합니다.

다시 말해 , 세션 은 최대 절전 모드 공간에만 국한되지 않으며 다른 최대 절전 모드 프로젝트와 유사한 방식으로 생성됩니다.

여기서 Point 의 인스턴스가 생성되면 PointEntity 를 저장하는 프로세스가 일반 엔터티와 비슷하다는 것을 알 수 있습니다.

몇 가지 테스트를 살펴 보겠습니다.

@Test public void shouldInsertAndSelectPoints() { PointEntity entity = new PointEntity(); entity.setPoint((Point) wktToGeometry("POINT (1 1)")); session.persist(entity); PointEntity fromDb = session .find(PointEntity.class, entity.getId()); assertEquals("POINT (1 1)", fromDb.getPoint().toString()); assertTrue(geometry instanceof Point); }

호출 toString () A의 점은 a의 WKT 표현을 반환 포인트 . 이는 Geometry 클래스가 toString () 메서드를 재정의하고 이전에 보았던 WKTReader 의 보완 클래스 인 WKTWriter를 내부적으로 사용 하기 때문 입니다.

이 테스트를 실행하면 hibernate가 PointEntity 테이블을 생성 합니다.

해당 테이블을 살펴 보겠습니다.

desc PointEntity; Field Type Null Key id bigint(20) NO PRI point geometry YES

예상대로 Type of Field PointGEOMETRY 입니다. 이 때문에 SQL 편집기 (예 : MySql 워크 벤치)를 사용하여 데이터를 가져 오는 동안이 GEOMETRY 유형을 사람이 읽을 수있는 텍스트로 변환해야합니다.

select id, astext(point) from PointEntity; id astext(point) 1 POINT(2 4)

그러나 Hibernate는 Geometry 또는 그 하위 클래스에서 toString () 메서드를 호출 할 때 이미 WKT 표현을 반환하므로이 변환에 대해 신경 쓸 필요가 없습니다.

7. 공간 함수 사용

7.1. ST_WITHIN () 예제

이제 공간 데이터 유형과 함께 작동하는 데이터베이스 함수의 사용법을 살펴 보겠습니다.

MySQL의 이러한 함수 중 하나 는 한 Geometry 가 다른 Geometry 내에 있는지 여부를 알려주 는 ST_WITHIN () 입니다 . 여기에서 좋은 예는 주어진 반경 내의 모든 점을 찾는 것입니다.

서클을 만드는 방법부터 살펴 보겠습니다.

public Geometry createCircle(double x, double y, double radius) { GeometricShapeFactory shapeFactory = new GeometricShapeFactory(); shapeFactory.setNumPoints(32); shapeFactory.setCentre(new Coordinate(x, y)); shapeFactory.setSize(radius * 2); return shapeFactory.createCircle(); }

원은 setNumPoints () 메서드로 지정된 유한 한 점 집합으로 표현됩니다 . 반경은 호출하기 전에 두 배가됩니다 에는 setSize () 우리는 두 방향으로, 중심 주위에 원을 그릴 필요로하는 방법.

이제 앞으로 나아가서 주어진 반경 내에서 포인트를 가져 오는 방법을 살펴 보겠습니다.

@Test public void shouldSelectAllPointsWithinRadius() throws ParseException { insertPoint("POINT (1 1)"); insertPoint("POINT (1 2)"); insertPoint("POINT (3 4)"); insertPoint("POINT (5 6)"); Query query = session.createQuery("select p from PointEntity p where within(p.point, :circle) = true", PointEntity.class); query.setParameter("circle", createCircle(0.0, 0.0, 5)); assertThat(query.getResultList().stream() .map(p -> ((PointEntity) p).getPoint().toString())) .containsOnly("POINT (1 1)", "POINT (1 2)"); }

최대 절전 모드는 매핑 내 () 받는 기능을 ST_Within에 () 의 MySQL 기능.

여기서 흥미로운 점은 점 (3, 4)이 정확히 원 위에 있다는 것입니다. 그래도 쿼리는이 지점을 반환하지 않습니다. 때문이다 내 () 함수가 제공된 경우에만 true를 반환 기하학은 또 다른 내에서 완전히 기하학 .

7.2. ST_TOUCHES() Example

Here, we'll present an example that inserts a set of Polygons in the database and select the Polygons that are adjacent to a given Polygon. Let's have a quick look at the PolygonEntity class:

@Entity public class PolygonEntity { @Id @GeneratedValue private Long id; private Polygon polygon; // standard getters and setters }

The only thing different here from the previous PointEntity is that we're using the type Polygon instead of the Point.

Let's now move towards the test:

@Test public void shouldSelectAdjacentPolygons() throws ParseException { insertPolygon("POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0))"); insertPolygon("POLYGON ((3 0, 3 5, 8 5, 8 0, 3 0))"); insertPolygon("POLYGON ((2 2, 3 1, 2 5, 4 3, 3 3, 2 2))"); Query query = session.createQuery("select p from PolygonEntity p where touches(p.polygon, :polygon) = true", PolygonEntity.class); query.setParameter("polygon", wktToGeometry("POLYGON ((5 5, 5 10, 10 10, 10 5, 5 5))")); assertThat(query.getResultList().stream() .map(p -> ((PolygonEntity) p).getPolygon().toString())).containsOnly( "POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0))", "POLYGON ((3 0, 3 5, 8 5, 8 0, 3 0))"); }

The insertPolygon() method is similar to the insertPoint() method that we saw earlier. The source contains the full implementation of this method.

touches () 함수를 사용하여 주어진 Polygon에 인접한 Polygon 을 찾습니다 . 분명히 주어진 Polygon에 닿는 가장자리가 없기 때문에 세 번째 Polygon 은 결과에 반환되지 않습니다 .

8. 결론

이 기사에서 우리는 hibernate-spatial이 저수준 세부 사항을 처리하기 때문에 공간 데이터 유형을 훨씬 더 간단하게 처리하는 것을 보았습니다.

이 기사에서는 Mariadb4j를 사용하지만 구성을 수정하지 않고도 MySql로 대체 할 수 있습니다.

항상 그렇듯이이 기사의 전체 소스 코드는 GitHub에서 찾을 수 있습니다.