Inleiding tot Hibernate Spatial

1. Inleiding

In dit artikel zullen we de ruimtelijke uitbreiding van Hibernate, hibernate-spatial, bekijken.

Beginnend met versie 5, Hibernate Spatial biedt een standaardinterface voor het werken met geografische gegevens.

2. Achtergrondinformatie over Hibernate Spatial

Geografische gegevens omvatten weergave van entiteiten zoals een Punt, lijn, veelhoek. Dergelijke gegevenstypen maken geen deel uit van de JDBC-specificatie, vandaar dat de JTS (JTS Topology Suite) een standaard is geworden voor het weergeven van ruimtelijke gegevenstypen.

Behalve JTS ondersteunt Hibernate spatial ook Geolatte-geom - een recente bibliotheek met enkele functies die niet beschikbaar zijn in JTS.

Beide bibliotheken zijn al opgenomen in het hibernate-spatial project. Het gebruik van de ene bibliotheek boven de andere is gewoon een kwestie van uit welke pot we gegevenstypen importeren.

Hoewel Hibernate spatial verschillende databases ondersteunt, zoals Oracle, MySQL, PostgreSQLql / PostGIS en een paar andere, is de ondersteuning voor de database-specifieke functies niet uniform.

Het is beter om de nieuwste Hibernate-documentatie te raadplegen om de lijst met functies te controleren waarvoor hibernate ondersteuning biedt voor een bepaalde database.

In dit artikel gebruiken we een Mariadb4j in het geheugen, die de volledige functionaliteit van MySQL behoudt.

De configuratie voor Mariadb4j en MySql is vergelijkbaar, zelfs de mysql-connectorbibliotheek werkt voor beide databases.

3. Afhankelijkheden van Maven

Laten we eens kijken naar de Maven-afhankelijkheden die nodig zijn voor het opzetten van een eenvoudig slaapstand-ruimtelijk project:

 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 

De overwinteren-ruimtelijk afhankelijkheid is degene die ondersteuning zal bieden voor de ruimtelijke gegevenstypen. De nieuwste versies van hibernate-core, hibernate-spatial, mysql-connector-java en mariaDB4j kunnen worden verkregen bij Maven Central.

4. Hibernate Spatial configureren

De eerste stap is het maken van een slaapstand.eigenschappen in de middelen directory:

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

Het enige dat specifiek is voor hibernate-spatial is de MySQL56SpatialDialect dialect. Dit dialect breidt het MySQL55Dialect dialect en biedt aanvullende functionaliteit met betrekking tot de ruimtelijke gegevenstypen.

De code die specifiek is voor het laden van het eigenschappenbestand, waardoor een SessionFactory, en het instantiëren van een Mariadb4j-instantie, is hetzelfde als in een standaard slaapstandproject.

5. Inzicht in de Geometrie Type

Geometrie is het basistype voor alle ruimtelijke typen in JTS. Dit betekent dat andere typen zoals Punt, Veelhoek, en anderen strekken zich uit van Geometrie. De Geometrie type in java komt overeen met de GEOMETRIE typ ook MySql in.

Door een Draad weergave van het type, we krijgen een exemplaar van Geometrie. Een hulpprogramma-klasse WKTReader geleverd door JTS kan worden gebruikt om elke bekende tekstweergave te converteren naar een Geometrie type:

openbare Geometry wktToGeometry (String wellKnownText) gooit ParseException {return new WKTReader (). read (wellKnownText); }

Laten we deze methode nu in actie zien:

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

Zoals we kunnen zien, zelfs als het retourtype van de methode is lezen() methode is Geometrie, de feitelijke instantie is die van een Punt.

6. Opslaan van een punt in DB

Nu we een goed idee hebben van wat een Geometrie type is en hoe u een Punt uit een Draad, laten we eens kijken naar de PointEntity:

@Entity openbare klasse PointEntity {@Id @GeneratedValue privé Lange id; privé punt punt; // standaard getters en setters}

Merk op dat de entiteit PointEntity bevat een ruimtelijk type Punt. Zoals eerder aangetoond, is een Punt wordt vertegenwoordigd door twee coördinaten:

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

De methode insertPoint () accepteert een bekende tekst (WKT) weergave van een Punt, converteert het naar een Punt instantie en wordt opgeslagen in de database.

Ter herinnering: de sessie is niet specifiek voor hibernate-spatial en is gemaakt op een manier die vergelijkbaar is met een ander hibernate-project.

We kunnen hier opmerken dat we eenmaal een exemplaar hebben van Punt gemaakt, het proces van opslag PointEntity is vergelijkbaar met elke reguliere entiteit.

Laten we eens kijken naar enkele tests:

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

Roeping toString () op een Punt geeft de WKT-weergave terug van a Punt. Dit komt doordat de Geometrie klasse overschrijft de toString () methode en intern gebruikt WKTWriter, een gratis les voor WKTReader die we eerder zagen.

Zodra we deze test hebben uitgevoerd, wordt de slaapstand aangemaakt PointEntity tafel voor ons.

Laten we die tabel eens bekijken:

desc PointEntity; Veldtype Null Sleutel-id bigint (20) NEE PRI-puntgeometrie JA

Zoals verwacht, de Type van VeldPunt is GEOMETRIE. Daarom moeten we bij het ophalen van de gegevens met behulp van onze SQL-editor (zoals MySql-werkbank) dit GEOMETRY-type converteren naar voor mensen leesbare tekst:

selecteer id, astext (point) van PointEntity; id astext (punt) 1 PUNT (2 4)

Omdat de slaapstand echter al WKT-weergave retourneert wanneer we bellen toString () methode op Geometrie of een van zijn subklassen, hoeven we ons geen zorgen te maken over deze conversie.

7. Ruimtelijke functies gebruiken

7.1. ST_WITHIN () Voorbeeld

We gaan nu kijken naar het gebruik van databasefuncties die werken met ruimtelijke gegevenstypen.

Een van die functies in MySQL is ST_WITHIN () dat vertelt of een Geometrie is binnen een ander. Een goed voorbeeld hiervan zou zijn om alle punten binnen een bepaalde straal te achterhalen.

Laten we beginnen met te kijken hoe u een cirkel kunt maken:

openbare Geometry createCircle (dubbele x, dubbele y, dubbele straal) {GeometricShapeFactory shapeFactory = nieuwe GeometricShapeFactory (); shapeFactory.setNumPoints (32); shapeFactory.setCentre (nieuwe coördinaat (x, y)); shapeFactory.setSize (straal * 2); return shapeFactory.createCircle (); }

Een cirkel wordt vertegenwoordigd door een eindige reeks punten gespecificeerd door de setNumPoints () methode. De straal wordt verdubbeld voordat de setSize () methode omdat we de cirkel rond het midden moeten tekenen, in beide richtingen.

Laten we nu verder gaan en kijken hoe we de punten binnen een bepaalde straal kunnen ophalen:

@Test openbare leegte shouldSelectAllPointsWithinRadius () gooit ParseException {insertPoint ("POINT (1 1)"); insertPoint ("PUNT (1 2)"); insertPoint ("PUNT (3 4)"); insertPoint ("PUNT (5 6)"); Query query = session.createQuery ("selecteer p van PointEntity p waar binnen (p.point,: circle) = true", 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) "); }

Hibernate brengt zijn binnen() functie naar de ST_WITHIN () functie van MySql.

Een interessante observatie hier is dat het punt (3, 4) precies op de cirkel valt. Toch retourneert de query dit punt niet. Dit is zo omdat de binnen() functie retourneert alleen waar als het gegeven Geometrie is volledig in een ander Geometrie.

7.2. ST_TOUCHES () Voorbeeld

Hier zullen we een voorbeeld presenteren waarin een set van Veelhoeks in de database en selecteer het Veelhoeks die grenzen aan een gegeven Veelhoek. Laten we het PolygonEntity klasse:

@Entity openbare klasse PolygonEntity {@Id @GeneratedValue privé Lange id; privé veelhoek veelhoek; // standaard getters en setters}

Het enige dat hier anders is dan het vorige PointEntity is dat we het type gebruiken Veelhoek in plaats van de Punt.

Laten we nu naar de test gaan:

@Test openbare void shouldSelectAd aangrenzendePolygons () gooit 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))"); Queryquery = session.createQuery ("selecteer p van PolygonEntity p waar raakt (p.polygon,: polygon) = true", PolygonEntity.class); query.setParameter ("polygoon", 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)) "); }

De insertPolygon () methode is vergelijkbaar met de insertPoint () methode die we eerder zagen. De bron bevat de volledige implementatie van deze methode.

We gebruiken de raakt () functie om het Veelhoeks naast een gegeven Veelhoek. Het is duidelijk dat de derde Veelhoek wordt niet geretourneerd in het resultaat omdat er geen rand is die het gegeven raakt Veelhoek.

8. Conclusie

In dit artikel hebben we gezien dat hibernate-spatial het omgaan met ruimtelijke datatypes een stuk eenvoudiger maakt omdat het zorgt voor de details op laag niveau.

Hoewel dit artikel Mariadb4j gebruikt, kunnen we het vervangen door MySql zonder enige configuratie te wijzigen.

Zoals altijd is de volledige broncode voor dit artikel te vinden op GitHub.