Introduktion til dvale-rumlig

1. Introduktion

I denne artikel ser vi på den geografiske udvidelse af dvale, dvale-rumlig.

Startende med version 5, Hibernate Spatial giver en standardgrænseflade til at arbejde med geografiske data.

2. Baggrund for dvaletilstand

Geografiske data inkluderer repræsentation af enheder som en Punkt, linje, polygon. Sådanne datatyper er ikke en del af JDBC-specifikationen, og derfor er JTS (JTS Topology Suite) blevet en standard til at repræsentere geografiske datatyper.

Bortset fra JTS understøtter dvaletilstand også Geolatte-geom - et nyligt bibliotek, der har nogle funktioner, der ikke er tilgængelige i JTS.

Begge biblioteker er allerede inkluderet i det dvale-rumlige projekt. Brug af et bibliotek frem for et andet er simpelthen et spørgsmål om, fra hvilken krukke vi importerer datatyper.

Selvom dvale-rumlig understøtter forskellige databaser som Oracle, MySQL, PostgreSQLql / PostGIS og et par andre, er understøttelsen af ​​de databasespecifikke funktioner ikke ensartet.

Det er bedre at henvise til den nyeste dvale-dokumentation for at kontrollere listen over funktioner, som dvale giver support til en given database.

I denne artikel bruger vi Mariadb4j i hukommelsen - som opretholder den fulde funktionalitet af MySQL.

Konfigurationen til Mariadb4j og MySql er ens, selv mysql-connector-biblioteket fungerer for begge disse databaser.

3. Maven afhængigheder

Lad os se på de Maven-afhængigheder, der kræves for at oprette et simpelt dvale-rumligt projekt:

 org. dvale-dvale-core 5.2.12.Final org. dvale dvale-rumlig 5.2.12.Final mysql mysql-connector-java 6.0.6 ch.vorburger.mariaDB4j mariaDB4j 2.2.3 

Det dvale-rumlig afhængighed er den, der yder støtte til de geografiske datatyper. De nyeste versioner af hibernate-core, hibernate-spatial, mysql-connector-java og mariaDB4j kan fås fra Maven Central.

4. Konfiguration af dvaletilstand

Det første trin er at oprette en dvale.egenskaber i ressourcer vejviser:

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

Det eneste der er specifikt for dvale-rumlig er MySQL56SpatialDialect dialekt. Denne dialekt udvider MySQL55Dialect dialekt og giver yderligere funktionalitet relateret til de geografiske datatyper.

Den kode, der er specifik for indlæsning af ejendomsfilen og opretter en SessionFactoryog instantere en Mariadb4j-forekomst er det samme som i et standard dvale-projekt.

5. Forståelse af Geometri Type

Geometri er basistypen for alle de rumlige typer i JTS. Dette betyder, at andre typer som Punkt, Polygon, og andre strækker sig fra Geometri. Det Geometri skriv i java svarer til GEOMETRI skriv også MySql.

Ved at analysere en Snor repræsentation af typen, får vi en forekomst af Geometri. En hjælpeklasse WKTReader leveret af JTS kan bruges til at konvertere enhver velkendt tekstrepræsentation til en Geometri type:

offentlig geometri wktToGeometry (String wellKnownText) kaster ParseException {returner ny WKTReader (). læs (wellKnownText); }

Lad os nu se denne metode i aktion:

@Test offentlig ugyldighed skalConvertWktToGeometry () {Geometry geometry = wktToGeometry ("POINT (2 5)"); assertEquals ("Point", geometry.getGeometryType ()); assertTrue (geometri eksempel på punkt); }

Som vi kan se, selv om metodens returtype er Læs() metode er Geometri, den egentlige forekomst er den af ​​en Punkt.

6. Lagring af et punkt i DB

Nu hvor vi har en god idé om, hvad en Geometri typen er, og hvordan man får en Punkt ud af en Snor, lad os se på PointEntity:

@Entity offentlig klasse PointEntity {@Id @GeneratedValue privat Lang id; privat punkt punkt; // standard getters og setter}

Bemærk, at enheden PointEntity indeholder en rumlig type Punkt. Som vist tidligere, a Punkt er repræsenteret af to koordinater:

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

Metoden insertPoint () accepterer en velkendt tekst (WKT) repræsentation af en Punkt, konverterer det til en Punkt eksempel og gemmer i DB.

Som en påmindelse er den session er ikke specifik for dvale-rumlig og er skabt på en måde, der ligner et andet dvale-projekt.

Vi kan her bemærke, at når vi først har en forekomst af Punkt oprettet, processen med lagring PointEntity svarer til enhver almindelig enhed.

Lad os se på nogle tests:

@Test offentlig ugyldighed shouldInsertAndSelectPoints () {PointEntity entity = new PointEntity (); entity.setPoint ((Point) wktToGeometry ("POINT (1 1)")); session.persist (enhed); PointEntity fromDb = session. Find (PointEntity.class, entity.getId ()); assertEquals ("POINT (1 1)", fromDb.getPoint (). toString ()); assertTrue (geometri eksempel på punkt); }

Ringer toString () på en Punkt returnerer WKT-repræsentationen af ​​en Punkt. Dette skyldes, at Geometri klasse tilsidesætter toString () metode og interne anvendelser WKTWriter, en gratis klasse til WKTReader som vi så tidligere.

Når vi har kørt denne test, oprettes dvaletilstand PointEntity bord til os.

Lad os se på tabellen:

desc PointEntity; Feltype Nul Nøgle-id bigint (20) NO PRI-punktgeometri JA

Som forventet Type af MarkPunkt er GEOMETRI. På grund af dette er vi nødt til at konvertere denne GEOMETRY-type til menneskelig læsbar tekst, mens vi henter data ved hjælp af vores SQL-editor (som MySql-arbejdsbænk):

vælg id, astext (point) fra PointEntity; id astext (point) 1 POINT (2 4)

Som i dvale returnerer imidlertid WKT-repræsentation allerede, når vi ringer toString () metode til Geometri eller nogen af ​​dens underklasser behøver vi ikke bekymre os om denne konvertering.

7. Brug af rumlige funktioner

7.1. ST_WITHIN () Eksempel

Vi ser nu på brugen af ​​databasefunktioner, der fungerer med geografiske datatyper.

En af disse funktioner i MySQL er ST_WITHIN () der fortæller om en Geometri er inden for en anden. Et godt eksempel her ville være at finde ud af alle punkter inden for en given radius.

Lad os starte med at se på, hvordan man opretter en cirkel:

offentlig geometri createCircle (dobbelt x, dobbelt y, dobbelt radius) {GeometricShapeFactory shapeFactory = ny GeometricShapeFactory (); shapeFactory.setNumPoints (32); shapeFactory.setCentre (ny koordinat (x, y)); shapeFactory.setSize (radius * 2); return formFabrik.createCircle (); }

En cirkel er repræsenteret af et endeligt sæt punkter specificeret af setNumPoints () metode. Det radius fordobles, inden han ringer til setSize () metode, da vi har brug for at tegne cirklen rundt om midten i begge retninger.

Lad os nu gå videre og se, hvordan man henter punkterne inden for en given radius:

@Test offentlig ugyldighed skalSelectAllPointsWithinRadius () kaster ParseException {insertPoint ("POINT (1 1)"); insertPoint ("POINT (1 2)"); insertPoint ("POINT (3 4)"); insertPoint ("POINT (5 6)"); Query query = session.createQuery ("vælg p fra PointEntity p hvor indenfor (p.point,: cirkel) = sand", PointEntity.class); query.setParameter ("cirkel", createCircle (0,0, 0,0, 5)); assertThat (query.getResultList (). stream () .map (p -> ((PointEntity) p) .getPoint (). toString ())) .containsOnly ("POINT (1 1)", "POINT (1 2) "); }

Dvaletilstand kortlægger sin inden for() funktion til ST_WITHIN () funktion af MySql.

En interessant observation her er, at punktet (3, 4) falder nøjagtigt på cirklen. Forespørgslen returnerer stadig ikke dette punkt. Dette er fordi det inden for() funktion returnerer kun sand, hvis den givne Geometri er helt inden i en anden Geometri.

7.2. ST_TOUCHES () Eksempel

Her præsenterer vi et eksempel, der indsætter et sæt af Polygons i databasen, og vælg Polygons der støder op til et givet Polygon. Lad os se hurtigt på PolygonEnhed klasse:

@Entity offentlig klasse PolygonEntity {@Id @GeneratedValue privat Lang id; privat polygon polygon; // standard getters og setter}

Det eneste der adskiller sig her fra det foregående PointEntity er, at vi bruger typen Polygon i stedet for Punkt.

Lad os nu gå mod testen:

@Test offentligt ugyldigt skalSelectAdjacentPolygons () kaster 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))"); Forespørgsel = session.createQuery ("vælg p fra PolygonEntity p hvor berører (p.polygon,: polygon) = sand", 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 ())). indeholder Kun ("POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0)) "," POLYGON ((3 0, 3 5, 8 5, 8 0, 3 0)) "); }

Det insertPolygon () metoden ligner insertPoint () metode, som vi så tidligere. Kilden indeholder den fulde implementering af denne metode.

Vi bruger rører () funktion til at finde Polygons støder op til et givet Polygon. Det er klart, den tredje Polygon returneres ikke i resultatet, da der ikke er kant, der rører ved det givne Polygon.

8. Konklusion

I denne artikel har vi set, at dvale-rumlige gør det lettere at håndtere geografiske datatyper, da det tager sig af detaljerne på lavt niveau.

Selvom denne artikel bruger Mariadb4j, kan vi erstatte den med MySql uden at ændre nogen konfiguration.

Som altid kan den fulde kildekode til denne artikel findes på GitHub.