Add Hibernate Spatial User Guide chapter

This commit is contained in:
Vlad Mihalcea 2016-02-25 10:48:45 +02:00
parent f076b5823c
commit 19e035c788
28 changed files with 436 additions and 15 deletions

5
.gitignore vendored
View File

@ -44,6 +44,5 @@ ObjectStore
/.nb-gradle/
# Additional databases used in local envs
databases/mysql/matrix.gradle
databases/mysql/resources/hibernate.properties
databases/mysql/
databases/postgis/

View File

@ -63,6 +63,7 @@ dependencies {
testCompile( project(':hibernate-core') )
testCompile( project(':hibernate-entitymanager') )
testCompile( project(':hibernate-ehcache') )
testCompile( project(':hibernate-spatial') )
testCompile( project(':hibernate-testing') )
testCompile( project(path: ':hibernate-entitymanager', configuration: 'tests') )

View File

@ -19,9 +19,10 @@ include::chapters/fetching/Fetching.adoc[]
include::chapters/batch/Batching.adoc[]
include::chapters/caching/Caching.adoc[]
include::chapters/events/Events.adoc[]
include::chapters/query-hql/HQL.adoc[]
include::chapters/query-criteria/Criteria.adoc[]
include::chapters/query-native/Native.adoc[]
include::chapters/query/hql/HQL.adoc[]
include::chapters/query/criteria/Criteria.adoc[]
include::chapters/query/native/Native.adoc[]
include::chapters/query/spatial/Spatial.adoc[]
include::chapters/multitenancy/Multi_Tenancy.adoc[]
include::chapters/osgi/OSGi.adoc[]
include::chapters/envers/Envers.adoc[]

View File

@ -7,7 +7,7 @@ This appendix covers the legacy Hibernate `org.hibernate.Criteria` API, which sh
New development should focus on the JPA javax.persistence.criteria.CriteriaQuery API.
Eventually, Hibernate-specific criteria features will be ported as extensions to the JPA `javax.persistence.criteria.CriteriaQuery`.
For details on the JPA APIs, see <<chapters/query-criteria/Criteria.adoc#criteria, Criteria>>.
For details on the JPA APIs, see <<chapters/query/criteria/Criteria.adoc#criteria, Criteria>>.
====
Hibernate features an intuitive, extensible criteria query API.

View File

@ -277,4 +277,4 @@ include::{sourcedir}/BatchTest.java[tags=batch-bulk-hql-insert-example]
----
====
This section is only a brief overview of HQL. For more information, see <<chapters/query-hql/HQL.adoc#hql,HQL>>.
This section is only a brief overview of HQL. For more information, see <<chapters/query/hql/HQL.adoc#hql,HQL>>.

View File

@ -1,6 +1,6 @@
[[criteria]]
== Criteria
:sourcedir: ../../../../../test/java/org/hibernate/userguide/criteria
:sourcedir: ../../../../../../test/java/org/hibernate/userguide/criteria
Criteria queries offer a type-safe alternative to HQL, JPQL and native SQL queries.

View File

@ -1,7 +1,7 @@
[[hql]]
== HQL and JPQL
:modeldir: ../../../../../main/java/org/hibernate/userguide/model
:sourcedir: ../../../../../test/java/org/hibernate/userguide/hql
:modeldir: ../../../../../../main/java/org/hibernate/userguide/model
:sourcedir: ../../../../../../test/java/org/hibernate/userguide/hql
:extrasdir: extras
The Hibernate Query Language (HQL) and Java Persistence Query Language (JPQL) are both object model focused query languages similar in nature to SQL.
@ -9,7 +9,7 @@ JPQL is a heavily-inspired-by subset of HQL.
A JPQL query is always a valid HQL query, the reverse is not true however.
Both HQL and JPQL are non-type-safe ways to perform query operations.
Criteria queries offer a type-safe approach to querying. See <<chapters/query-criteria/Criteria.adoc#criteria,Criteria>> for more information.
Criteria queries offer a type-safe approach to querying. See <<chapters/query/criteria/Criteria.adoc#criteria,Criteria>> for more information.
[[query-api]]
=== Query API

View File

@ -1,7 +1,7 @@
[[sql]]
== Native SQL Queries
:modeldir: ../../../../../main/java/org/hibernate/userguide/model
:sourcedir: ../../../../../test/java/org/hibernate/userguide/sql
:modeldir: ../../../../../../main/java/org/hibernate/userguide/model
:sourcedir: ../../../../../../test/java/org/hibernate/userguide/sql
:extrasdir: extras
You may also express queries in the native SQL dialect of your database.

View File

@ -0,0 +1,269 @@
[[spatial]]
== Spatial
:sourcedir: ../../../../../../test/java/org/hibernate/userguide/spatial
:extrasdir: extras
[[spatial-overview]]
=== Overview
Hibernate Spatial was originally developed as a generic extension to Hibernate for handling geographic data.
Since 5.0, Hibernate Spatial is now part of Hibernate ORM project,
and it allows you to deal with geographic data in a standardized way.
Hibernate Spatial provides a standardized, cross-database interface to geographic data storage and query functions.
It supports most of the functions described by the OGC Simple Feature Specification, and the supported databases are: Oracle 10g/11g, PostgreSql/PostGIS, MySQL, Microsoft SQL Server and H2/GeoDB,
yet, not all databases support all the functions defined by Hibernate Spatial.
Hibernate Spatial uses the Java Topology Suite (JTS) as its geometry model.
JTS is an implementation of the OpenGIS Simple Features Implementation Specification for SQLv. 1.1 (SFS).
This specification is implemented in most RDBMS with spatial data support.
It is also a direct precursor to SQL/MM Part 3: Spatial (ISO/IEC 13249-3).
The SFS specification defines a set of functions on geometries.
Hibernate Spatial makes a subset of these functions available in HQL and in the Criteria Query API.
[[spatial-configuration]]
=== Configuration
Hibernate Spatial requires some configuration prior to start using it.
[[spatial-configuration-dependency]]
==== Dependency
You need to include the `hibernate-spatial` dependency in your build environment.
For Maven, you need to add the following dependency:
[[spatial-configuration-maven-example]]
.Maven dependency
====
[source,xml]
----
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-spatial</artifactId>
<version>${hibernate.version}</version>
</dependency>
----
====
[[spatial-configuration-dialect]]
==== Dialects
Hibernate Spatial extends the Hibernate ORM dialects so that the spatial functions of the database are made available within HQL and JPQL.
So, for instance, instead of using the `PostgreSQL82Dialect`, we use the Hibernate Spatial extension of that dialect which is the `PostgisDialect`.
[[spatial-configuration-dialect-example]]
.Specifying a spatial dialect
====
[source,xml]
----
<property
name="hibernate.dialect"
value="org.hibernate.spatial.dialect.postgis.PostgisDialect"
/>
----
====
Not all databases support all the functions defined by Hibernate Spatial.
The table below provides an overview of the functions provided by each database.
:yes: icon:check[role="green"]
:no: icon:times[role="red"]
[[spatial-configuration-dialect-features]]
.Hibernate Spatial dialect function support
[cols=",,,,,," |options="header",]
|================================
|Function | Description | PostgresSQL | Oracle 10g/11g | MySQL | SQLServer | GeoDB (H2)
|Basic functions on Geometry | | | | | |
|`int dimension(Geometry)` | SFS §2.1.1.1 | {yes} | {yes} | {yes} | {yes} | {yes}
|`String geometrytype(Geometry)` | SFS §2.1.1.1 | {yes} | {yes} | {yes} | {yes} | {yes}
|`int srid(Geometry)` | SFS §2.1.1.1 | {yes} | {yes} | {yes} | {yes} | {yes}
|`Geometry envelope(Geometry)` | SFS §2.1.1.1 | {yes} | {yes} | {yes} | {yes} | {yes}
|`String astext(Geometry)` | SFS §2.1.1.1 | {yes} | {yes} | {yes} | {yes} | {yes}
|`byte[] asbinary(Geometry)` | SFS §2.1.1.1 | {yes} | {yes} | {yes} | {yes} | {yes}
|`boolean isempty(Geometry)` | SFS §2.1.1.1 | {yes} | {yes} | {yes} | {yes} | {yes}
|`boolean issimple(Geometry)` | SFS §2.1.1.1 | {yes} | {yes} | {yes} | {yes} | {yes}
|`Geometry boundary(Geometry)` | SFS §2.1.1.1 | {yes} | {yes} | {no} | {yes} | {yes}
|Functions for testing Spatial Relations between geometric objects | | | | | |
|`boolean equals(Geometry, Geometry)` | SFS §2.1.1.2 | {yes} | {yes} | {yes} | {yes} | {yes}
|`boolean disjoint(Geometry, Geometry)` | SFS §2.1.1.2 | {yes} | {yes} | {yes} | {yes} | {yes}
|`boolean intersects(Geometry, Geometry)` | SFS §2.1.1.2 | {yes} | {yes} | {yes} | {yes} | {yes}
|`boolean touches(Geometry, Geometry)` | SFS §2.1.1.2 | {yes} | {yes} | {yes} | {yes} | {yes}
|`boolean crosses(Geometry, Geometry)` | SFS §2.1.1.2 | {yes} | {yes} | {yes} | {yes} | {yes}
|`boolean within(Geometry, Geometry)` | SFS §2.1.1.2 | {yes} | {yes} | {yes} | {yes} | {yes}
|`boolean contains(Geometry, Geometry)` | SFS §2.1.1.2 | {yes} | {yes} | {yes} | {yes} | {yes}
|`boolean overlaps(Geometry, Geometry)` | SFS §2.1.1.2 | {yes} | {yes} | {yes} | {yes} | {yes}
|`boolean relate(Geometry, Geometry, String)` | SFS §2.1.1.2 | {yes} | {yes} | {no} | {yes} | {yes}
|Functions that support Spatial Analysis | | | | | |
|`double distance(Geometry, Geometry)` | SFS §2.1.1.3 | {yes} | {yes} | {no} | {yes} | {yes}
|`Geometry buffer(Geometry, double)` | SFS §2.1.1.3 | {yes} | {yes} | {no} | {yes} | {yes}
|`Geometry convexhull(Geometry)` | SFS §2.1.1.3 | {yes} | {yes} | {no} | {yes} | {yes}
|`Geometry intersection(Geometry, Geometry)` | SFS §2.1.1.3 | {yes} | {yes} | {no} | {yes} | {yes}
|`Geometry geomunion(Geometry, Geometry)` | SFS §2.1.1.3 (renamed from union) | {yes} | {yes} | {no} | {yes} | {yes}
|`Geometry difference(Geometry, Geometry)` | SFS §2.1.1.3 | {yes} | {yes} | {no} | {yes} | {yes}
|`Geometry symdifference(Geometry, Geometry)` | SFS §2.1.1.3 | {yes} | {yes} | {no} | {yes} | {yes}
|Common non-SFS functions | | | | | |
|`boolean dwithin(Geometry, Geometry, double)` | Returns true if the geometries are within the specified distance of one another | {yes} | {yes} | {no} | {no} | {yes}
|`Geometry transform(Geometry, int)` | Returns a new geometry with its coordinates transformed to the SRID referenced by the integer parameter | {yes} | {yes} | {no} | {no} | {no}
|Spatial aggregate Functions | | | | | |
|`Geometry extent(Geometry)` | Returns a bounding box that bounds the set of returned geometries | {yes} | {yes} | {no} | {no} | {no}
|================================
[[spatial-configuration-dialect-postgis]]
Postgis::
For Postgis from versions 1.3 and later, the best dialect to use is `org.hibernate.spatial.dialect.postgis.PostgisDialect`.
+
This translates the HQL spatial functions to the Postgis SQL/MM-compliant functions.
For older, pre v1.3 versions of Postgis, which are not SQL/MM compliant, the dialect `org.hibernate.spatial.dialect.postgis.PostgisNoSQLMM` is provided.
+
This dialect depends on the JDBC extensions in postgis.jar (see the http://postgis.net/docs/postgis_installation.html#id336398[Postgis documentation]).
[IMPORTANT]
====
Beware of classpath problems in a Java EE containers where the JDBC drivers live in a different classpath than the Postgis JDBC extensions and/or Hibernate Spatial.
For JBoss, some users found https://gist.github.com/bjornharrtell/3054462[this post] helpful.
====
[[spatial-configuration-dialect-mysql]]
MySQL::
There are several dialects for MySQL:
`MySQLSpatialDialect`:::
a spatially-extended version of Hibernate `MySQLDialect`
`MySQLSpatialInnoDBDialect`:::
a spatially-extended version of Hibernate `MySQLInnoDBDialect`
`MySQLSpatial56Dialect`:::
a spatially-extended version of Hibernate `MySQL5DBDialect`.
`MySQLSpatial5InnoDBDialect`:::
the same as `MySQLSpatial56Dialect`, but with support for the InnoDB storage engine.
[NOTE]
====
MySQL versions before 5.6.1 had only limited support for spatial operators.
Most operators only took account of the minimum bounding rectangles (MBR) of the geometries, and not the geometries themselves.
This changed in version 5.6.1 were MySQL introduced `ST_*` spatial operators.
The dialects `MySQLSpatial56Dialect` and `MySQLSpatial5InnoDBDialect` use these newer, more precise operators.
These dialects may therefore produce results that differ from that of the other spatial dialects.
For more information see this page in the MySQL reference guide (esp. the section https://dev.mysql.com/doc/refman/5.7/en/spatial-relation-functions.html[Functions That Test Spatial Relations Between Geometry Objects])
====
[[spatial-configuration-dialect-oracle]]
Oracle10g/11g::
There is currently only one Oracle spatial dialect: `OracleSpatial10gDialect` which extends the Hibernate dialect `Oracle10gDialect`.
This dialect has been tested on both Oracle 10g and Oracle 11g with the `SDO_GEOMETRY` spatial database type.
+
This dialect is the only dialect that can be configured using these Hibernate properties:
+
`hibernate.spatial.connection_finder`:::
the fully-qualified classname for the Connection finder for this Dialect (see below).
.The `ConnectionFinder` interface
[NOTE]
====
The `SDOGeometryType` requires access to an `OracleConnection` object when converting a geometry to SDO_GEOMETRY.
In some environments, however, the `OracleConnection` is not available (e.g. because a Java EE container or connection pool proxy wraps the connection object in its own `Connection` implementation).
A `ConnectionFinder` knows how to retrieve the `OracleConnection` from the wrapper or proxy Connection object that is passed into prepared statements.
The default implementation will, when the passed object is not already an `OracleConnection`, attempt to retrieve the `OracleConnection` by recursive reflection.
It will search for methods that return `Connection` objects, execute these methods and check the result.
If the result is of type `OracleConnection` the object is returned, otherwise it recurses on it.
In may cases this strategy will suffice.
If not, you can provide your own implementation of this interface on the class path, and configure it in the `hibernate.spatial.connection_finder` property.
Note that implementations must be thread-safe and have a default no-args constructor.
====
`hibernate.spatial.ogc_strict`::: true to use the OGC-compliant functions on SDO_GEOMETRY (see below)
.OGC Compliance Setting
[NOTE]
====
The Oracle Spatial dialect can be configured to run in either OGC strict or non-strict mode.
In OGC strict mode, the Open Geospatial compliant functions of Oracle Spatial are used in spatial operations (they exists in Oracle 10g, but are not documented). In non-strict mode the usual Oracle Spatial functions are used directly, and mimic the OGC semantics.The default is OGC strict mode.
You can change this to non-strict mode by setting the hibernate.spatial.ogc_strict property to false.
Note that changing from strict to non-strict mode changes the semantics of the spatial operation.
We have attempted to implement the OGC semantics as well we could using the standard Oracle Spatial operators, but this was not possible in all cases.
On the plus side, non-strict mode should be faster in most cases.
====
SQL Server::
The dialect `SqlServer2008Dialect` supports the `GEOMETRY` type in SQL Server 2008 and later.
[NOTE]
====
The `GEOGRAPHY` type is not currently supported.
====
GeoDB (H2)::
The `GeoDBDialect` supports the GeoDB a spatial extension of the H2 in-memory database.
[NOTE]
====
The dialect has been tested with GeoDB version 0.7
====
[[spatial-types]]
=== Types
Hibernate Spatial comes with the following types:
jts_geometry::
Handled by `org.hibernate.spatial.JTSGeometryType` it maps a database geometry column type to a `com.vividsolutions.jts.geom.Geometry` entity property type.
geolatte_geometry::
Handled by `org.hibernate.spatial.GeolatteGeometryType`, it maps a database geometry column type to a `org.geolatte.geom.Geometry` entity property type.
The following entity uses the `jts_geometry` to map the PostgreSQL geometry type to a `com.vividsolutions.jts.geom.Point`.
[[spatial-types-mapping-example]]
.Type mapping
====
[source, JAVA, indent=0]
----
include::{sourcedir}/SpatialTest.java[tags=spatial-types-mapping-example]
----
====
When creating such entity:
[[spatial-types-point-creation-example]]
.Creating a Point
====
[source, JAVA, indent=0]
----
include::{sourcedir}/SpatialTest.java[tags=spatial-types-point-creation-example]
----
====
Hibernate generates the following SQL statement:
[[spatial-types-point-creation-example]]
.Creating a Point
====
[source, SQL, indent=0]
----
include::{extrasdir}/spatial-types-point-creation-example.sql[]
----
====
Hibernate Spatial defines many query functions that are available both in HQL and JPQL queries.
[[spatial-types-query-example]]
.Querying the geometry
====
[source, SQL, indent=0]
----
include::{sourcedir}/SpatialTest.java[tags=spatial-types-query-example]
----
====
This JPQL query generates the following SQL statement:
[[spatial-types-sql-query-example]]
.Underlying SQL query
====
[source, SQL, indent=0]
----
include::{extrasdir}/spatial-types-sql-query-example.sql[]
----
====

View File

@ -0,0 +1,4 @@
INSERT INTO
Event (location, name, id)
VALUES
('POINT (10 5)', 'Hibernate ORM presentation', 1)

View File

@ -0,0 +1,6 @@
select
e.id as id1_0_,
e.location as location2_0_,
e.name as name3_0_
from Event e
where st_within(e.location, 'POLYGON ((1 1, 20 1, 20 20, 1 20, 1 1))') = true

View File

@ -13,6 +13,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.persistence.AttributeConverter;
import javax.persistence.Entity;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Id;
@ -155,6 +156,17 @@ public class BootstrapTest {
}
{
AttributeConverter myAttributeConverter = new AttributeConverter() {
@Override
public Object convertToDatabaseColumn(Object attribute) {
return null;
}
@Override
public Object convertToEntityAttribute(Object dbData) {
return null;
}
} ;
//tag::bootstrap-native-metadata-builder-example[]
ServiceRegistry standardRegistry =
new StandardServiceRegistryBuilder().build();
@ -170,6 +182,9 @@ public class BootstrapTest {
// specify the schema name to use for tables, etc when none is explicitly specified
metadataBuilder.applyImplicitSchemaName( "my_default_schema" );
// specify a custom Attribute Converter
metadataBuilder.applyAttributeConverter( myAttributeConverter );
Metadata metadata = metadataBuilder.build();
//end::bootstrap-native-metadata-builder-example[]
}

View File

@ -0,0 +1,126 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.userguide.spatial;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.hibernate.annotations.Type;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.spatial.dialect.postgis.PostgisDialect;
import org.hibernate.testing.RequiresDialect;
import org.junit.Test;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.io.ParseException;
import com.vividsolutions.jts.io.WKTReader;
import static org.junit.Assert.assertEquals;
import static org.hibernate.userguide.util.TransactionUtil.doInJPA;
/**
* @author Vlad Mihalcea
*/
@RequiresDialect(PostgisDialect.class)
public class SpatialTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Event.class,
};
}
@Test
public void test() {
Long addressId = doInJPA( this::entityManagerFactory, entityManager -> {
try {
//tag::spatial-types-point-creation-example[]
Event event = new Event();
event.setId( 1L);
event.setName( "Hibernate ORM presentation");
event.setLocation( (Point) new WKTReader().read( "POINT(10 5)"));
entityManager.persist( event );
//end::spatial-types-point-creation-example[]
return event.getId();
} catch (ParseException e) {
throw new RuntimeException(e);
}
});
doInJPA( this::entityManagerFactory, entityManager -> {
Event event = entityManager.find( Event.class, addressId);
Coordinate coordinate = event.getLocation().getCoordinate();
assertEquals( 10.0d, coordinate.getOrdinate( Coordinate.X), 0.1);
assertEquals( 5.0d, coordinate.getOrdinate( Coordinate.Y), 0.1);
});
doInJPA( this::entityManagerFactory, entityManager -> {
try {
//tag::spatial-types-query-example[]
Event event = entityManager.createQuery(
"select e " +
"from Event e " +
"where within(e.location, :filter) = true", Event.class)
.setParameter("filter", new WKTReader().read( "POLYGON((1 1,20 1,20 20,1 20,1 1))"))
.getSingleResult();
//end::spatial-types-query-example[]
Coordinate coordinate = event.getLocation().getCoordinate();
assertEquals( 10.0d, coordinate.getOrdinate( Coordinate.X), 0.1);
assertEquals( 5.0d, coordinate.getOrdinate( Coordinate.Y), 0.1);
}
catch (ParseException e) {
throw new RuntimeException(e);
}
});
}
//tag::spatial-types-mapping-example[]
@Entity(name = "Event")
public static class Event {
@Id
private Long id;
private String name;
@Type(type = "jts_geometry")
private Point location;
//Getters and setters are omitted for brevity
//end::spatial-types-mapping-example[]
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Point getLocation() {
return location;
}
public void setLocation(Point location) {
this.location = location;
}
//tag::spatial-types-mapping-example[]
}
//end::spatial-types-mapping-example[]
}

View File

@ -25,7 +25,7 @@ log4j.logger.org.hibernate.reflection=info
log4j.logger.org.hibernate.SQL=debug
### log JDBC bind parameters ###
log4j.logger.org.hibernate.type=info
log4j.logger.org.hibernate.type.descriptor.sql=trace
### log schema export/update ###
log4j.logger.org.hibernate.tool.hbm2ddl=info