hibernate 支持 postgis函数
今天上午一位朋友问到了,关于hibernate中是否支持postgis函数的问题,我就这个问题,随便聊几句。
要想使用hibernate的空间数据操作,就要提到一个概念 java Topology Suite (字面上理解就是 空间拓扑的意思,简称 JTS,
注意:过需要声明一点,本文中的JTS与进行java事务处理的JTS、JTA没有联系).
HIBERNATE中对空间数据作了支持( Hibernate Spatial), Hibernate Spatial是对处理空间数据的一个Hibernate扩展 ,
Hibernate Spatial 使用标准的方式处理地理信息数据 ,并且提供了一个可以跨数据库的处理的接口函数,
Hibernate Spatial 中包含了多种 OGC 简单的处理函数. 支持的数据库为: Oracle 10g/11g, Postgresql/Postgis, and MySQL.
要想使用 Hibernate Spatial 就要引入JTS, JTS 从根本上而言其实并不是很复杂,它主要是完成了java对几何对象、空间拓扑得核心操作算法。
下面通过简单配置来说明一下如何使用(我们使用的数据库是postgis):
数据库脚本:
sql:
CREATE TABLE events
(
id bigint NOT NULL,
event_date timestamp without time zone,
title character varying(255),
"location" geometry,
CONSTRAINT events_pkey PRIMARY KEY (id)
)
1,引入 jts-1.8.jar, hibernate3.jar 等包 ,同时还要应用 hibernate-spatial-postgis-1.0-20070920.111959-1.jar 和
hibernate-spatial-1.0-20070920.111959-1.jar 包(如果不是postgre sql就要引用相应的数据库包)。
2,创建一个持久化类(po对象)
如下:
import
java.util.Date;
import com.vividsolutions.jts.geom.Point;
public class Event {
private Long id;
private String title;
private Date date;
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;
}
}
注意:上面的po对象中的location属性的类型。这个类型是空间数据类型。
import com.vividsolutions.jts.geom.Point;
public class Event {
private Long id;
private String title;
private Date date;
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;
}
}
3,创建相应的Mapping 文件
< hibernate - mapping >
< class name = " Event " table = " EVENTS " >
< id name = " id " column = " EVENT_ID " >
< generator class = " native " />
</ id >
< property name = " date " type = " timestamp "
column = " EVENT_DATE " />
< property name = " title " />
< property name = " location "
type = " org.hibernatespatial.GeometryUserType "
column = " location " />
</ class >
</ hibernate - mapping >
4,配置hibernate 配置文件
<?
xml version='1.0' encoding='utf-8'
?>
<! DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd" >
< hibernate-configuration >
< session-factory >
<!-- Database connection settings -->
< property name ="connection.driver_class" > org.postgresql.Driver </ property >
< property name ="connection.url" > jdbc:postgresql://localhost:5432/test </ property >
< property name ="connection.username" > postgres </ property >
< property name ="connection.password" > test </ property >
<!-- JDBC connection pool (use the built-in) -->
< property name ="connection.pool_size" > 1 </ property >
<!-- SPATIAL SQL dialect -->
< property name ="dialect" > org.hibernatespatial.postgis.PostgisDialect </ property >
<!-- Enable Hibernate's automatic session context management -->
< property name ="current_session_context_class" > thread </ property >
<!-- Disable the second-level cache -->
< property name ="cache.provider_class" > org.hibernate.cache.NoCacheProvider </ property >
<!-- Echo all executed SQL to stdout -->
< property name ="show_sql" > true </ property >
<!-- Drop and re-create the database schema on startup -->
< property name ="hbm2ddl.auto" > create </ property >
< mapping resource ="Event.hbm.xml" />
</ session-factory >
</ hibernate-configuration >
注意:在上面的配置文件中, <property name="dialect">org.hibernatespatial.postgis.PostgisDialect</property><! DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd" >
< hibernate-configuration >
< session-factory >
<!-- Database connection settings -->
< property name ="connection.driver_class" > org.postgresql.Driver </ property >
< property name ="connection.url" > jdbc:postgresql://localhost:5432/test </ property >
< property name ="connection.username" > postgres </ property >
< property name ="connection.password" > test </ property >
<!-- JDBC connection pool (use the built-in) -->
< property name ="connection.pool_size" > 1 </ property >
<!-- SPATIAL SQL dialect -->
< property name ="dialect" > org.hibernatespatial.postgis.PostgisDialect </ property >
<!-- Enable Hibernate's automatic session context management -->
< property name ="current_session_context_class" > thread </ property >
<!-- Disable the second-level cache -->
< property name ="cache.provider_class" > org.hibernate.cache.NoCacheProvider </ property >
<!-- Echo all executed SQL to stdout -->
< property name ="show_sql" > true </ property >
<!-- Drop and re-create the database schema on startup -->
< property name ="hbm2ddl.auto" > create </ property >
< mapping resource ="Event.hbm.xml" />
</ session-factory >
</ hibernate-configuration >
这句配置起到了重要的作用,这里声明了 hibernate 的方言。该方言声明了对postgis的一些支持。
并且大家也应该知道,在使用hibernate时候,如果需要自定义函数,要需要自定义方言。
5,第一个主类
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernatespatial.criterion.SpatialRestrictions;
import org.postgis.Geometry;
import org.postgis.LineString;
import org.postgis.MultiLineString;
import org.postgis.MultiPolygon;
import org.postgis.PGgeometry;
import org.postgis.Polygon;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.io.ParseException;
import com.vividsolutions.jts.io.WKTReader;
import com.vividsolutions.jts.geom. * ;
import java.util.Date;
import java.util.List;
import util.HibernateUtil;
public class EventManager {
public int SRID = 4326;
public static void main(String[] args) {
EventManager mgr = new EventManager();
String testPiont = "2,3";
mgr.createAndStoreEvent("My Event", new Date(), testPiont);
HibernateUtil.getSessionFactory().close();
}
private void createAndStoreEvent(String title, Date theDate, String wktPoint) {
com.vividsolutions.jts.geom.Geometry geom = null;
try {
geom = pointFromText(wktPoint);
} catch (Exception e) {
throw new RuntimeException("Not a WKT string:" + wktPoint);
}
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Event theEvent = new Event();
theEvent.setTitle(title);
theEvent.setDate(theDate);
theEvent.setLocation((Point) geom);
session.save(theEvent);
session.flush();
session.getTransaction().commit();
List l= find("POLYGON((1 1,20 1,20 20, 1 20, 1 1))");
System.out.println(l.size());
List l1= find1("POLYGON((1 1,20 1,20 20, 1 20, 1 1))");
System.out.println(l1.size());
}
private List find(String wktFilter){
WKTReader fromText = new WKTReader();
com.vividsolutions.jts.geom.Geometry filter = null;
try{
filter = fromText.read(wktFilter);
filter.setSRID(SRID);
} catch(ParseException e){
throw new RuntimeException("Not a WKT String:" + wktFilter);
}
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
System.out.println("Filter is : " + filter);
Criteria testCriteria = session.createCriteria(Event.class);
testCriteria.add(SpatialRestrictions.within("location", filter, filter));
List results = testCriteria.list();
session.getTransaction().commit();
return results;
}
private List find1(String wktFilter){
WKTReader fromText = new WKTReader();
com.vividsolutions.jts.geom.Geometry filter = null;
try{
filter = fromText.read(wktFilter);
filter.setSRID(SRID);
} catch(ParseException e){
throw new RuntimeException("Not a WKT String:" + wktFilter);
}
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Query query = session.createQuery("select within(location,'srid=4326;POLYGON ((1 1, 20 1, 20 20, 1 20, 1 1))') from Event ");
List list = query.list();
return list;
}
public Point pointFromText(String txt) throws Exception {
String tmp = txt;
if (!tmp.startsWith("POINT")) {
tmp = tmp.replace(",", " ");
tmp = "POINT(" + tmp + ")";
}
try {
WKTReader fromText = new WKTReader();
Geometry geom = fromText.read(tmp);
Point pt = (Point) fromText.read(tmp);
pt.setSRID(SRID);
return pt;
} catch (Exception e) {
return null;
}
}
}
通过上面的方法,我们可以测试出来,hibernate已经可以支持空间数据类型的数据操作了,可以实现空间数据入库到空间数据对象影射到java对象中(插入和查询方法),但是有一个问题,就是支持的空间数据操作方法太少了。例如 contains,disjoint,within等十几个方法。这些方法都是简单的空间数据操作方法,要想实现复杂的空间数据这还远远不够的。相对于这方面来说ibatis 就很方便的使用了。
这方面的资料比较少,大家都交流 ,有什么好的经验大家分享一下。