本文简要介绍如何使用 Spring JDBC 进行基本的空间查询。
数据的准备
将空间数据存入 Oracle Spatial 的方法有很多种,最基本的一种方法就是直接用 SQL 语句插入数据,但这种方法效率太低,而且极易出错。我个人较为喜欢也最常使用的一种方法是借助现成的 GIS 软件导入数据。下面简要介绍如何使用 ArcCatalog 将矢量数据导入 Oracle Spatial。
连接数据库
-
在 ArcCatalog 的目录树中,找到数据库连接,展开后有一个添加数据库连接,双击之后就会打开如下的窗口:
- 按照图中的示范填写相关属性,实例这一项的格式是
主机:端口/数据库实例名
。点击确定,然后就会连接到 Oracle。注意,ArcCatalog 只能连接32位的 Oracle 客户端,我安装的是32位的 Oracle 11g 客户端。
导入数据
-
成功连接到 Oracle 之后在数据库连接下就会看到一个新建的连接,右键,导入→要素类(单个),打开如下的窗口。
- 介绍几个关键字段的含义:
- 输入要素:就是要导入的矢量数据,这里选择的是 .shp 文件(示例数据将会在文末给出)。注意文件名中不能包含中文。
- 输出要素类:数据将会导入到这个字段指定的表中。
- 配置关键字:选择 SDO_GEOMETRY,这是 Oracle Spatial 提供的空间数据类型。
- 点击确定,右键连接,选择刷新,导入成功的话就会看到一个新生成的表,表名就是刚才在输出要素类中给的值。
用 Java API 进行基本的空间查询
实现 Domain 类,封装数据
前面已经提过,Oracle Spatial 用 SDO_GEOMETRY 类型来表示空间数据,Java API 中对应的类是 oracle.spatial.geometry.JGeometry。根据表中的字段,设计如下的 JavaBean 用来封装数据:
// 封装铁路信息的类
public class RailWay {
private int objectId;
private JGeometry shape;
private double fNode;
private double tNode;
private double lPoly;
private double rPoly;
private int gbCode;
private String name;
private String pinYin;
private double length_m;
public int getObjectId() {
return objectId;
}
public void setObjectId(int objectId) {
this.objectId = objectId;
}
public JGeometry getShape() {
return shape;
}
public void setShape(JGeometry shape) {
this.shape = shape;
}
public double getfNode() {
return fNode;
}
public void setfNode(double fNode) {
this.fNode = fNode;
}
public double gettNode() {
return tNode;
}
public void settNode(double tNode) {
this.tNode = tNode;
}
public double getlPoly() {
return lPoly;
}
public void setlPoly(double lPoly) {
this.lPoly = lPoly;
}
public double getrPoly() {
return rPoly;
}
public void setrPoly(double rPoly) {
this.rPoly = rPoly;
}
public int getGbCode() {
return gbCode;
}
public void setGbCode(int gbCode) {
this.gbCode = gbCode;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPinYin() {
return pinYin;
}
public void setPinYin(String pinYin) {
this.pinYin = pinYin;
}
public double getLength_m() {
return length_m;
}
public void setLength_m(double length_m) {
this.length_m = length_m;
}
@Override
public String toString() {
return this.name + ",编号" + this.gbCode + ",长度为" + this.length_m + "m";
}
}
实现 DAO 类,提供 CRUD 接口
这里只举例如何进行查询。
// DAO 类,提供 CRUD 接口
public class RailWayDao {
private static NamedParameterJdbcTemplate namedTempl;
// 建立数据连接
static {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("oracle.jdbc.OracleDriver");
dataSource.setUrl("jdbc:oracle:thin:@localhost:1521:orcl");
dataSource.setUsername("");
dataSource.setPassword("");
namedTempl = new NamedParameterJdbcTemplate(dataSource);
}
// 查询指定名字的铁路,返回结果只有一个
public RailWay getRailWay(String name) {
String sql = "select * from railwayorcl where name = :name";
Map<String,String> param = new HashMap<>();
param.put("name", name);
// 需要自己实现 RowMapper
return this.namedTempl.queryForObject(sql, param, (rs,rowNum) -> {
RailWay r = new RailWay();
r.setObjectId(rs.getInt("objectId"));
r.setShape(JGeometry.load((STRUCT)rs.getObject("shape")));
r.setfNode(rs.getDouble("fNode_"));
r.settNode(rs.getDouble("tNode_"));
r.setlPoly(rs.getDouble("lPoly_"));
r.setrPoly(rs.getDouble("rPoly_"));
r.setGbCode(rs.getInt("gbCode"));
r.setName(rs.getString("name"));
r.setPinYin(rs.getString("pinYin"));
r.setLength_m(rs.getDouble("length_m"));
return r;
});
}
//查询指定名字的铁路,查询结果不止一个
public List<RailWay> getRailWays(String name) {
String sql = "select * from railwayorcl where name = :name";
Map<String,String> param = new HashMap<>();
param.put("name", name);
//需要自己实现 RowMapper
return this.namedTempl.query(sql, param, (rs,rowNum) -> {
RailWay r = new RailWay();
r.setObjectId(rs.getInt("objectId"));
r.setShape(JGeometry.load((STRUCT)rs.getObject("shape")));
r.setfNode(rs.getDouble("fNode_"));
r.settNode(rs.getDouble("tNode_"));
r.setlPoly(rs.getDouble("lPoly_"));
r.setrPoly(rs.getDouble("rPoly_"));
r.setGbCode(rs.getInt("gbCode"));
r.setName(rs.getString("name"));
r.setPinYin(rs.getString("pinYin"));
r.setLength_m(rs.getDouble("length_m"));
return r;
});
}
// 查询指定名字的铁路的结点坐标
public double[] getCoords(String name) {
double[] coords = new double[0];
RailWay r = getRailWay(name);
coords = r.getShape().getOrdinatesArray();
return coords;
}
}
需要注意的是,从数据库查询到的空间数据是 oracle.sql.STRUCT 类型的,而在前面设计的 RailWay 类中,空间数据是 JGeometry 类型的,所以这里需要手动进行转换。因此,必须自己实现一个 RowMapper :
public RailWay getRailWay(String name) {
String sql = "select * from railwayorcl where name = :name";
Map<String,String> param = new HashMap<>();
param.put("name", name);
// RowMapper 接口只有一个 mapRow 方法,所以使用了 lambda 表达式
return this.namedTempl.queryForObject(sql, param, (rs,rowNum) -> {
RailWay r = new RailWay();
r.setObjectId(rs.getInt("objectId"));
//将 STRUCT 转换成 JGeometry
r.setShape(JGeometry.load((STRUCT)rs.getObject("shape")));
r.setfNode(rs.getDouble("fNode_"));
r.settNode(rs.getDouble("tNode_"));
r.setlPoly(rs.getDouble("lPoly_"));
r.setrPoly(rs.getDouble("rPoly_"));
r.setGbCode(rs.getInt("gbCode"));
r.setName(rs.getString("name"));
r.setPinYin(rs.getString("pinYin"));
r.setLength_m(rs.getDouble("length_m"));
return r;
});
}
示例数据 密码:9b1u
Oracle Spatial Java API 的各种 jar 包在 %ORACLE_HOME%\md\jlib 目录下。