Rev 312 | Blame | Compare with Previous | Last modification | View Log | RSS feed
<?xml version="1.0" encoding="UTF-8"?><document xmlns="http://maven.apache.org/XDOC/2.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/XDOC/2.0 http://maven.apache.org/xsd/xdoc-2.0.xsd"><properties><title>Tutorial</title><author>Karel Maesen</author></properties><body><section name="Introduction"><p>This tutorial gives a quick overview of how to getHibernate Spatial 4.x working. We will develop a simpleapplication that stores, and retrieves somesimple data objects. The data objects are "special" inthat they have a property of type Geometry.</p><p>This tutorial assumesthat you are familiar with Hibernate and the basicconcepts of working with geographic data.</p><p>For this tutorial We require a postgis database. For information on how to create a postgis database, you should consult<a href="http://postgis.refractions.net/documentation/manual-1.5/">the postgis documentation</a>. For this tutorial we used Postgresql 8.4 andPostgis 1.5.</p></section><section name="Creating a Spatially-Enabled EventManager"><p>We will create a small application to store and retrieve events we want to attend. (This isthe same use case as in the Hibernate Tutorial).</p><subsection name="Setup"><p>We first need to set up our development environment. We will use the<a href="http://maven.org/">Maven</a>build tool in this tutorial.</p><p>Maven can generate the basic structure of our simple application using the<code>mvnarchetype:generate</code>.For this example, we specify type (the default) and set<code>groupId</code>to <code>org.hibernate.spatial.tutorials</code>,<code>artifactId</code>to<code>event-tutorial</code>and<code>package</code>to <code>event</code>.</p><source>mvn archetype:generate[INFO] Scanning for projects...[INFO] Searching repository for plugin with prefix: 'archetype'.[INFO] ------------------------------------------------------------------------[INFO] Building Maven Default Project[INFO] task-segment: [archetype:generate] (aggregator-style)[INFO] ------------------------------------------------------------------------[INFO] Preparing archetype:generate[INFO] No goals needed for project - skipping[INFO] Setting property: classpath.resource.loader.class =>'org.codehaus.plexus.velocity.ContextClassLoaderResourceLoader'........Define value for groupId: : org.hibernate.spatial.tutorialsDefine value for artifactId: : event-tutorialDefine value for version: 1.0-SNAPSHOT: :Define value for package: : eventConfirm properties configuration:groupId: org.hibernatespatial.tutorialsartifactId: event-tutorialversion: 1.0-SNAPSHOTpackage: eventY: : Y......[INFO] ------------------------------------------------------------------------[INFO] BUILD SUCCESSFUL[INFO] ------------------------------------------------------------------------[INFO] Total time: 36 seconds[INFO] Finished at: Mon Apr 05 17:23:22 CEST 2010[INFO] Final Memory: 15M/158M[INFO] ------------------------------------------------------------------------</source><p>This results in the following directory structure.</p><source><![CDATA[.|-- pom.xml`-- src|-- main| |-- java| | |-- event| | `-- App.java`-- test`-- java`-- event]]></source><p>We now need to edit the pom to add the required dependencies and repositories (see also the <ahref="mavenquick.html">Maven Quick Start</a>.).</p><source><![CDATA[<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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.hibernate.spatial.tutorials</groupId><artifactId>event-tutorial</artifactId><version>1.0-SNAPSHOT</version><packaging>jar</packaging><name>event-tutorial</name><url>http://maven.apache.org</url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><hibernate.version>4.0.0.Final</hibernate.version></properties><build><finalName>${artifactId}</finalName><plugins><plugin><artifactId>maven-compiler-plugin</artifactId><configuration><source>1.6</source><target>1.6</target><encoding>UTF-8</encoding></configuration></plugin></plugins></build><dependencies><!-- Hibernate Spatial --><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-spatial</artifactId><version>4.0-M1</version></dependency><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-entitymanager</artifactId><version>${hibernate.version}</version></dependency><!-- the postgresql driver --><dependency><groupId>postgresql</groupId><artifactId>postgresql</artifactId><version>8.4-701.jdbc3</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>1.5.11</version></dependency></dependencies><!-- add repositories for JTS and Hibernate Spatial and Hibernate (JBoss) --><repositories><repository><id>OSGEO GeoTools repo</id><url>http://download.osgeo.org/webdav/geotools</url></repository><repository><id>Hibernate Spatial repo</id><url>http://www.hibernatespatial.org/repository</url></repository><!-- add JBOSS repository for easy access to Hibernate libraries --><repository><id>JBOSS</id><url>https://repository.jboss.org/nexus/content/repositories/releases/</url></repository></repositories></project>]]></source><p>With the command<code>mvn dependency:list</code>we can see which libraries are required for our minimalHibernate Spatial application.</p><source>$ mvn dependency:list....[INFO] The following files have been resolved:[INFO] antlr:antlr:jar:2.7.7:compile[INFO] com.fasterxml:classmate:jar:0.5.4:compile[INFO] com.vividsolutions:jts:jar:1.12:compile[INFO] commons-collections:commons-collections:jar:3.2.1:compile[INFO] dom4j:dom4j:jar:1.6.1:compile[INFO] javassist:javassist:jar:3.12.1.GA:compile[INFO] log4j:log4j:jar:1.2.14:compile[INFO] org.hibernate:hibernate-core:jar:4.0.0.Final:compile[INFO] org.hibernate:hibernate-entitymanager:jar:4.0.0.Final:compile[INFO] org.hibernate:hibernate-spatial:jar:4.0-M1:compile[INFO] org.hibernate.common:hibernate-commons-annotations:jar:4.0.1.Final:compile[INFO] org.hibernate.javax.persistence:hibernate-jpa-2.0-api:jar:1.0.1.Final:compile[INFO] org.jboss:jandex:jar:1.0.3.Final:compile[INFO] org.jboss.logging:jboss-logging:jar:3.1.0.CR2:compile[INFO] org.jboss.spec.javax.transaction:jboss-transaction-api_1.1_spec:jar:1.0.0.Final:compile[INFO] org.postgis:postgis-jdbc:jar:1.5.3:compile[INFO] org.slf4j:slf4j-api:jar:1.5.11:compile[INFO] org.slf4j:slf4j-log4j12:jar:1.5.11:compile[INFO] postgresql:postgresql:jar:8.4-701.jdbc3:compile[INFO] xerces:xercesImpl:jar:2.4.0:compile[INFO] xml-apis:xml-apis:jar:1.0.b2:compile</source><p>Hibernate Spatial works with a wide range of versions of these libraries, so don't be too concernedif you seeeither more recent or slightly older versions.</p></subsection><subsection name="The Event Class"><p>Our persistent class is the<code>Event</code>class. Since this classcontains a geometry-valued property (a property of type Geometry), its instancesare geographic objects, or features.</p><source><![CDATA[package event;import com.vividsolutions.jts.geom.Point;import org.hibernate.annotations.GenericGenerator;import org.hibernate.annotations.Type;import javax.persistence.*;import java.util.Date;/*** @author Karel Maesen, Geovise BVBA* creation-date: 6/18/12*/@Entitypublic class Event {@Id@GeneratedValue(generator="increment")@GenericGenerator(name="increment", strategy = "increment")private Long id;private String title;private Date date;@Type(type="org.hibernate.spatial.GeometryType")private Point location;public Event() {}public Long getId() {return id;}private void setId(Long id) {this.id = id;}public Date getDate() {return date;}public void setDate(Date date) {this.date = date;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public Point getLocation() {return this.location;}public void setLocation(Point location) {this.location = location;}}]]></source><p>We put this file in the<code>./src/main/java/event</code>directory of the development directory.</p><p>Note the @Type annotation. This informs Hibernate that the location attribute is of type Geometry.The @Type annotation is Hibernate specific, and the only non-JPA annotation that is required.In future versions of Hibernate (version 5 and later) it will no longer be necessary to explicitlydeclare the Type of Geometry-valued attributes.this </p></subsection><subsection name="The Hibernate/JPA Configuration"><p>We proceed with the hibernate configuration file. The only differencew.r.t. the usual Hibernate/JPA configurations is the dialectproperty. Hibernate Spatial extends the Hibernate <code>Dialect</code>s so that the spatialfunctions of the database are available within HQL and JPQL. So instead of usingthe (in our case) PostgreSQLDialect, we use Hibernate Spatial'sextension of that dialect which is the PostGISDialect.Our<code>persistence.xml</code>looks like this:<source><![CDATA[<persistence xmlns="http://java.sun.com/xml/ns/persistence"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"version="2.0"><persistence-unit name="org.hibernate.events.jpa" transaction-type="RESOURCE_LOCAL"><properties><property name="hibernate.dialect" value="org.hibernate.spatial.dialect.postgis.PostgisDialect"/><property name="hibernate.connection.driver_class" value="org.postgresql.Driver"/><property name="hibernate.connection.url" value="jdbc:postgresql://localhost:5432:hstutorial"/><property name="hibernate.connection.username" value="hstutorial"/><property name="hibernate.connection.password" value="hstutorial"/><property name="hibernate.connection.pool_size" value="5"/><property name="hibernate.show_sql" value="true"/><property name="hibernate.format_sql" value="true"/><property name="hibernate.max_fetch_depth" value="5"/><property name="hibernate.hbm2ddl.auto" value="update"/></properties></persistence-unit></persistence>]]></source>As is usual when building with maven, we store this file in the<code>./src/main/resources/META-INF</code>directory.</p><p>Note that this configuration file means that Hibernate will connect to the "events"database on localhost, with username "hstutorial" and password "hstutorial".You may need to change these values to reflect your set-up.</p><p>Also notice that the "hbm2dll.auto" property is set to 'update'. This will create thenecessary table(s) when the application is run for the first time.</p></subsection></section><section name="The JPAUtil helper"><p>The<code>JPAUtil</code>class creates the <code>EntityManagerFactory</code> for the application, and provides a methodto create <code>EntityManager</code> instances.</p><source><![CDATA[package util;import javax.persistence.EntityManager;import javax.persistence.EntityManagerFactory;import javax.persistence.Persistence;public class JPAUtil {private static final EntityManagerFactory emFactory;static {try {emFactory = Persistence.createEntityManagerFactory("org.hibernate.events.jpa");}catch(Throwable ex){System.err.println("Cannot create EntityManagerFactory.");throw new ExceptionInInitializerError(ex);}}public static EntityManager createEntityManager() {return emFactory.createEntityManager();}public static void close(){emFactory.close();}}]]></source></section><section name="The EventManager"><p>We are now ready to write a first version of the main application class<code>EventManager</code>.</p><source><![CDATA[package event;import com.vividsolutions.jts.geom.Geometry;import com.vividsolutions.jts.geom.Point;import com.vividsolutions.jts.io.ParseException;import com.vividsolutions.jts.io.WKTReader;import util.JPAUtil;import javax.persistence.EntityManager;import javax.persistence.Query;import java.util.Date;import java.util.List;/*** @author Karel Maesen, Geovise BVBA* creation-date: 6/19/12*/public class EventManager {public static void main(String[] args) {EventManager mgr = new EventManager();if (args[0].equals("store")) {mgr.createAndStoreEvent("My Event", new Date(), assemble(args));}JPAUtil.close();}private void createAndStoreEvent(String title, Date theDate, String wktPoint) {Geometry geom = wktToGeometry(wktPoint);if (!geom.getGeometryType().equals("Point")) {throw new RuntimeException("Geometry must be a point. Got a " + geom.getGeometryType());}EntityManager em = JPAUtil.createEntityManager();em.getTransaction().begin();Event theEvent = new Event();theEvent.setTitle(title);theEvent.setDate(theDate);theEvent.setLocation((Point) geom);em.persist(theEvent);em.getTransaction().commit();em.close();}private Geometry wktToGeometry(String wktPoint) {WKTReader fromText = new WKTReader();Geometry geom = null;try {geom = fromText.read(wktPoint);} catch (ParseException e) {throw new RuntimeException("Not a WKT string:" + wktPoint);}return geom;}/*** Utility method to assemble all arguments save the first into a String*/private static String assemble(String[] args) {StringBuilder builder = new StringBuilder();for (int i = 1; i < args.length; i++) {builder.append(args[i]).append(" ");}return builder.toString();}}]]></source><p>The<code>EventManger</code>stores a point that represents the location for the event. The point is given as a String in the Well-Known Text (WKT) format. See<a href="http://www.vividsolutions.com/jts/javadoc/com/vividsolutions/jts/io/WKTReader.html">the JTSWTKReader JavaDoc</a> for more information about the WTK format.(The <code>assemble()</code> method is necessary because the Maven exec pluginparses string with spaces as a list of command-line arguments.)</p><p>The development directory now looks like this.<source><![CDATA[.|-- pom.xml`-- src|-- main| |-- java| | |-- event| | | |-- Event.java| | | `-- EventManager.java| | `-- util| | `-- JPAUtil.java| `-- resources| `-- META-INF| `-- persistence.xml|`-- test`-- java`-- event]]></source></p><p>We can now build this program using maven:<source><![CDATA[ $ mvn compile ]]></source>We can now execute the program using maven as follows:<source><![CDATA[ mvn exec:java -Dexec.mainClass="event.EventManager" -Dexec.args="store POINT(10 5)"]]></source></p><p>This will create the events table, add a new event with the date set to today, the title to "My Title",and the point to coordinates (10,15).</p><p>We can check the content of the events table using the astext() function thatconverts the geometries to their WKT representation.</p><source><![CDATA[ events=# select title, astext(location) from events;title | astext----------+--------------My Event | POINT(10 15)(1 row) ]]></source></section><section name="Spatial Queries"><p>We will now modify the<code>EventManager</code>by adding an action to list all events within a certain area.This will show how to use Hibernate Spatial for spatial querying.</p><p>Here is the addition to the<code>main</code>method in the<code>EventManager</code>thatimplements the "find" action.<source><![CDATA[...if (args[0].equals("store")) {mgr.createAndStoreEvent("My Event", new Date(), assemble(args));} else if (args[0].equals("find")) {List events = mgr.find(args[1]);for (int i = 0; i < events.size(); i++) {Event event = (Event) events.get(i);System.out.println("Event: " + event.getTitle() +", Time: " + event.getDate() +", Location: " + event.getLocation());}}...]]></source>We also need to implement the<code>find(String filter)</code>method.</p><p>The<code>find</code>method takes a WKT string that represents a polygon, andsearches the events table for all events that are located within this polygon.</p><p>Here is the code.<source><![CDATA[...private List find(String wktFilter) {Geometry filter = wktToGeometry(wktFilter);EntityManager em = JPAUtil.createEntityManager();em.getTransaction().begin();Query query = em.createQuery("select e from Event e where st_within(e.location, :filter) = true", Event.class);query.setParameter("filter", filter);return query.getResultList();}...]]></source></p><p>The<code>st_within</code> function is registered in the Dialect <code>PostgisDialect</code>method. It takes two parameters: the first is the name of the Geometry-valued property on which the filteris applied; the second is the filter geometry.</p><p>If now run:<source><![CDATA[ $ mvn exec:java -Dexec.mainClass="event.EventManager" -Dexec.args="find POLYGON((1\ 1,20\ 1,20\20,1\ 20,1\ 1))" ]]></source>We get<source><![CDATA[...Hibernate:selectevent0_.id as id0_,event0_.date as date0_,event0_.location as location0_,event0_.title as title0_fromEvent event0_wherest_within(event0_.location, ?)=trueEvent: My Event, Time: 2012-06-19 15:20:06.049, Location: POINT (10 5)...]]></source>The first lines show the SQL generated by Hibernate (with a little help of Hibernate Spatial).It is shown because the<code>show_sql</code>property is set in the configuration file.The last line is the output generated by the<code>find</code>action.</p></section><section name="But I use Oracle/MySQL/SQLServer/... so what do I do?"><p>If you need to run Hibernate Spatial on a different database, you need to change the JDBC driver (obviously) andset the appropriate spatial dialect. Currently, your choices are:</p><ol><li>Oracle 10g/11g: <code>org.hibernate.spatial.dialect.oracle.OracleSpatial10gDialect</code></li><li>MySQL 5+: <code>org.hibernate.spatial.dialect.mysql.MySQLSpatialDialect</code> or <code> org.hibernate.spatial.dialect.mysql.MySQLSpatialInnoDBDialect</code></li><li>Microsoft SQL Server 2008 : <code>org.hibernate.spatial.dialect.sqlserver.SqlServer2008SpatialDialect</code></li><li>H2 + GeoDB: <code>org.hibernate.spatial.dialect.h2geodb.GeoDBDialect</code></li></ol></section></body></document>