第06章 - 数据源访问与管理
第06章 - 数据源访问与管理
6.1 DataStore API 概述
6.1.1 DataStore 体系结构
DataStore 是 GeoTools 数据访问的核心抽象,提供统一的接口访问各种数据源。
┌─────────────────────────────────────────────────────────────────────┐
│ DataStore 体系 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ DataAccess<FeatureType, Feature> │
│ │ │
│ └── DataStore │
│ │ │
│ ├── FileDataStore │
│ │ ├── ShapefileDataStore │
│ │ ├── PropertyDataStore │
│ │ └── GeoJSONDataStore │
│ │ │
│ ├── JDBCDataStore │
│ │ ├── PostGISDataStore │
│ │ ├── OracleDataStore │
│ │ ├── H2GISDataStore │
│ │ └── SQLServerDataStore │
│ │ │
│ ├── ContentDataStore │
│ │ └── MemoryDataStore │
│ │ │
│ └── DirectoryDataStore │
│ └── (目录中的多个文件) │
│ │
└─────────────────────────────────────────────────────────────────────┘
6.1.2 DataStore 核心接口
/**
* DataStore 接口定义
*/
public interface DataStore extends DataAccess<SimpleFeatureType, SimpleFeature> {
// 获取所有图层名称
String[] getTypeNames() throws IOException;
// 获取图层的要素类型
SimpleFeatureType getSchema(String typeName) throws IOException;
// 更新要素类型
void updateSchema(String typeName, SimpleFeatureType featureType) throws IOException;
// 获取要素源(只读)
SimpleFeatureSource getFeatureSource(String typeName) throws IOException;
// 获取要素读取器
FeatureReader<SimpleFeatureType, SimpleFeature> getFeatureReader(
Query query, Transaction transaction) throws IOException;
// 获取要素写入器
FeatureWriter<SimpleFeatureType, SimpleFeature> getFeatureWriter(
String typeName, Transaction transaction) throws IOException;
// 获取带过滤的要素写入器
FeatureWriter<SimpleFeatureType, SimpleFeature> getFeatureWriter(
String typeName, Filter filter, Transaction transaction) throws IOException;
// 获取追加写入器
FeatureWriter<SimpleFeatureType, SimpleFeature> getFeatureWriterAppend(
String typeName, Transaction transaction) throws IOException;
// 创建要素类型
void createSchema(SimpleFeatureType featureType) throws IOException;
// 删除要素类型
void removeSchema(String typeName) throws IOException;
// 释放资源
void dispose();
// 获取锁定服务
LockingManager getLockingManager();
}
6.2 DataStoreFinder 使用
6.2.1 发现可用的 DataStore
import org.geotools.api.data.DataStore;
import org.geotools.api.data.DataStoreFinder;
import org.geotools.api.data.DataStoreFactorySpi;
// 列出所有可用的 DataStore 工厂
Iterator<DataStoreFactorySpi> factories = DataStoreFinder.getAvailableDataStores();
System.out.println("可用的 DataStore 工厂:");
while (factories.hasNext()) {
DataStoreFactorySpi factory = factories.next();
System.out.println(" " + factory.getDisplayName());
System.out.println(" 描述: " + factory.getDescription());
// 打印参数
Param[] params = factory.getParametersInfo();
for (Param param : params) {
System.out.printf(" - %s (%s): %s%n",
param.getName(),
param.getType().getSimpleName(),
param.getDescription());
}
System.out.println();
}
6.2.2 通过参数获取 DataStore
import java.util.Map;
import java.util.HashMap;
// 方式1:Shapefile
Map<String, Object> shpParams = new HashMap<>();
shpParams.put("url", new File("data/countries.shp").toURI().toURL());
DataStore shpStore = DataStoreFinder.getDataStore(shpParams);
// 方式2:PostGIS
Map<String, Object> pgParams = new HashMap<>();
pgParams.put("dbtype", "postgis");
pgParams.put("host", "localhost");
pgParams.put("port", 5432);
pgParams.put("database", "gisdb");
pgParams.put("schema", "public");
pgParams.put("user", "postgres");
pgParams.put("passwd", "password");
DataStore pgStore = DataStoreFinder.getDataStore(pgParams);
// 方式3:GeoPackage
Map<String, Object> gpkgParams = new HashMap<>();
gpkgParams.put("dbtype", "geopkg");
gpkgParams.put("database", "data/world.gpkg");
DataStore gpkgStore = DataStoreFinder.getDataStore(gpkgParams);
// 方式4:WFS 服务
Map<String, Object> wfsParams = new HashMap<>();
wfsParams.put("WFSDataStoreFactory:GET_CAPABILITIES_URL",
"https://example.com/geoserver/wfs?service=WFS&version=2.0.0&request=GetCapabilities");
DataStore wfsStore = DataStoreFinder.getDataStore(wfsParams);
6.2.3 FileDataStoreFinder
import org.geotools.api.data.FileDataStore;
import org.geotools.api.data.FileDataStoreFinder;
// 简化的文件数据源获取
File shpFile = new File("data/countries.shp");
FileDataStore store = FileDataStoreFinder.getDataStore(shpFile);
if (store != null) {
// 获取唯一的要素源
SimpleFeatureSource source = store.getFeatureSource();
System.out.println("图层名: " + source.getSchema().getTypeName());
System.out.println("要素数: " + source.getCount(Query.ALL));
// 使用完毕释放
store.dispose();
}
// 获取支持的文件扩展名
Set<String> extensions = FileDataStoreFinder.getAvailableFileExtensions();
System.out.println("支持的文件扩展名: " + extensions);
6.3 DataStore 基本操作
6.3.1 读取数据
public class DataStoreReadExample {
public static void main(String[] args) throws Exception {
// 获取 DataStore
File file = new File("data/countries.shp");
FileDataStore store = FileDataStoreFinder.getDataStore(file);
try {
// 获取图层名称
String[] typeNames = store.getTypeNames();
System.out.println("图层数量: " + typeNames.length);
for (String name : typeNames) {
System.out.println(" - " + name);
}
// 获取第一个图层
String typeName = typeNames[0];
SimpleFeatureSource source = store.getFeatureSource(typeName);
// 获取要素类型
SimpleFeatureType schema = source.getSchema();
System.out.println("\n要素类型: " + schema.getTypeName());
System.out.println("坐标系: " + schema.getCoordinateReferenceSystem().getName());
// 获取边界
ReferencedEnvelope bounds = source.getBounds();
System.out.println("边界: " + bounds);
// 获取要素数量
int count = source.getCount(Query.ALL);
System.out.println("要素数量: " + count);
// 读取所有要素
SimpleFeatureCollection features = source.getFeatures();
try (SimpleFeatureIterator iter = features.features()) {
int n = 0;
while (iter.hasNext() && n < 5) {
SimpleFeature feature = iter.next();
System.out.println(feature.getID() + ": " +
feature.getAttribute("NAME"));
n++;
}
}
} finally {
store.dispose();
}
}
}
6.3.2 查询数据
import org.geotools.api.data.Query;
import org.geotools.api.filter.Filter;
import org.geotools.api.filter.FilterFactory;
import org.geotools.factory.CommonFactoryFinder;
public class DataStoreQueryExample {
public static void queryData(DataStore store, String typeName) throws Exception {
FilterFactory ff = CommonFactoryFinder.getFilterFactory();
SimpleFeatureSource source = store.getFeatureSource(typeName);
// 1. 属性过滤
Filter nameFilter = ff.equals(
ff.property("NAME"),
ff.literal("China")
);
Query query1 = new Query(typeName, nameFilter);
SimpleFeatureCollection result1 = source.getFeatures(query1);
System.out.println("名称过滤结果: " + result1.size());
// 2. 空间过滤 - BBOX
Filter bboxFilter = ff.bbox(
ff.property("the_geom"),
73, 18, 135, 54, // 中国大致边界
"EPSG:4326"
);
Query query2 = new Query(typeName, bboxFilter);
SimpleFeatureCollection result2 = source.getFeatures(query2);
System.out.println("BBOX 过滤结果: " + result2.size());
// 3. 属性投影(只获取部分属性)
Query query3 = new Query(typeName);
query3.setPropertyNames("NAME", "POP_EST"); // 只获取这两个属性
query3.setMaxFeatures(10);
SimpleFeatureCollection result3 = source.getFeatures(query3);
System.out.println("\n属性投影结果:");
try (SimpleFeatureIterator iter = result3.features()) {
while (iter.hasNext()) {
SimpleFeature f = iter.next();
System.out.printf(" %s: %s%n",
f.getAttribute("NAME"),
f.getAttribute("POP_EST"));
}
}
// 4. 组合查询
Filter combinedFilter = ff.and(
ff.greater(ff.property("POP_EST"), ff.literal(100000000)),
bboxFilter
);
Query query4 = new Query(typeName, combinedFilter);
query4.setPropertyNames("NAME", "POP_EST", "the_geom");
query4.setSortBy(ff.sort("POP_EST", SortOrder.DESCENDING));
SimpleFeatureCollection result4 = source.getFeatures(query4);
System.out.println("\n组合查询结果:");
try (SimpleFeatureIterator iter = result4.features()) {
while (iter.hasNext()) {
SimpleFeature f = iter.next();
System.out.printf(" %s: %,d%n",
f.getAttribute("NAME"),
((Number) f.getAttribute("POP_EST")).longValue());
}
}
}
}
6.3.3 写入数据
import org.geotools.api.data.Transaction;
import org.geotools.data.DefaultTransaction;
public class DataStoreWriteExample {
public static void writeData(DataStore store, String typeName) throws Exception {
SimpleFeatureSource source = store.getFeatureSource(typeName);
// 检查是否支持写入
if (!(source instanceof SimpleFeatureStore)) {
System.out.println("数据源不支持写入");
return;
}
SimpleFeatureStore featureStore = (SimpleFeatureStore) source;
// 创建事务
Transaction transaction = new DefaultTransaction("write-example");
featureStore.setTransaction(transaction);
try {
// 创建新要素
SimpleFeatureType schema = featureStore.getSchema();
SimpleFeatureBuilder builder = new SimpleFeatureBuilder(schema);
GeometryFactory gf = JTSFactoryFinder.getGeometryFactory();
Point location = gf.createPoint(new Coordinate(116.4, 39.9));
builder.set("the_geom", location);
builder.set("NAME", "Test City");
builder.set("POP_EST", 1000000L);
SimpleFeature newFeature = builder.buildFeature(null);
// 添加到要素集合
DefaultFeatureCollection newFeatures = new DefaultFeatureCollection();
newFeatures.add(newFeature);
// 写入
List<FeatureId> addedIds = featureStore.addFeatures(newFeatures);
System.out.println("添加了 " + addedIds.size() + " 个要素");
// 提交事务
transaction.commit();
System.out.println("事务已提交");
} catch (Exception e) {
// 回滚
transaction.rollback();
System.err.println("事务已回滚: " + e.getMessage());
throw e;
} finally {
transaction.close();
}
}
}
6.4 MemoryDataStore
6.4.1 创建内存数据源
import org.geotools.data.memory.MemoryDataStore;
// 创建要素类型
SimpleFeatureType cityType = DataUtilities.createType(
"City",
"location:Point:srid=4326,name:String,population:Long"
);
// 创建内存数据源
MemoryDataStore memStore = new MemoryDataStore();
// 添加要素类型
memStore.createSchema(cityType);
// 添加要素
SimpleFeatureBuilder builder = new SimpleFeatureBuilder(cityType);
GeometryFactory gf = JTSFactoryFinder.getGeometryFactory();
builder.add(gf.createPoint(new Coordinate(116.4074, 39.9042)));
builder.add("北京");
builder.add(21540000L);
SimpleFeature beijing = builder.buildFeature("city.1");
builder.add(gf.createPoint(new Coordinate(121.4737, 31.2304)));
builder.add("上海");
builder.add(24280000L);
SimpleFeature shanghai = builder.buildFeature("city.2");
memStore.addFeature(beijing);
memStore.addFeature(shanghai);
// 或者批量添加
DefaultFeatureCollection collection = new DefaultFeatureCollection();
collection.add(beijing);
collection.add(shanghai);
memStore.addFeatures(collection);
// 查询
SimpleFeatureSource source = memStore.getFeatureSource("City");
System.out.println("要素数量: " + source.getCount(Query.ALL));
6.4.2 从其他数据源复制到内存
public static MemoryDataStore copyToMemory(DataStore sourceStore, String typeName)
throws Exception {
SimpleFeatureSource source = sourceStore.getFeatureSource(typeName);
SimpleFeatureCollection features = source.getFeatures();
// 创建内存数据源
MemoryDataStore memStore = new MemoryDataStore();
memStore.createSchema(source.getSchema());
// 复制要素
try (SimpleFeatureIterator iter = features.features()) {
while (iter.hasNext()) {
memStore.addFeature(iter.next());
}
}
return memStore;
}
6.5 ContentDataStore 扩展
6.5.1 自定义 DataStore 基础
import org.geotools.data.store.ContentDataStore;
import org.geotools.data.store.ContentEntry;
import org.geotools.data.store.ContentFeatureSource;
/**
* 自定义 DataStore 示例
*/
public class MyDataStore extends ContentDataStore {
private File dataFile;
public MyDataStore(File dataFile) {
this.dataFile = dataFile;
}
@Override
protected List<Name> createTypeNames() throws IOException {
// 返回支持的图层名称
List<Name> names = new ArrayList<>();
names.add(new NameImpl("myLayer"));
return names;
}
@Override
protected ContentFeatureSource createFeatureSource(ContentEntry entry) throws IOException {
return new MyFeatureSource(entry, Query.ALL);
}
@Override
public void dispose() {
// 释放资源
super.dispose();
}
}
/**
* 自定义 FeatureSource
*/
public class MyFeatureSource extends ContentFeatureSource {
public MyFeatureSource(ContentEntry entry, Query query) {
super(entry, query);
}
@Override
protected ReferencedEnvelope getBoundsInternal(Query query) throws IOException {
// 返回边界
return null;
}
@Override
protected int getCountInternal(Query query) throws IOException {
// 返回要素数量
return -1;
}
@Override
protected FeatureReader<SimpleFeatureType, SimpleFeature> getReaderInternal(Query query)
throws IOException {
// 返回要素读取器
return null;
}
@Override
protected SimpleFeatureType buildFeatureType() throws IOException {
// 构建要素类型
return DataUtilities.createType("myLayer", "geom:Point,name:String");
}
}
6.6 DirectoryDataStore
6.6.1 访问目录中的多个文件
import org.geotools.data.directory.DirectoryDataStore;
// 创建目录数据源(访问目录中的所有 Shapefile)
File directory = new File("data/shapefiles");
DirectoryDataStore dirStore = new DirectoryDataStore(
directory,
new ShapefileDataStoreFactory().createDataStore(new HashMap<>())
);
// 列出所有图层
String[] typeNames = dirStore.getTypeNames();
System.out.println("目录中的图层:");
for (String name : typeNames) {
System.out.println(" - " + name);
}
// 访问特定图层
SimpleFeatureSource source = dirStore.getFeatureSource("countries");
System.out.println("要素数量: " + source.getCount(Query.ALL));
dirStore.dispose();
6.7 参数管理
6.7.1 DataStore 参数
import org.geotools.api.data.DataStoreFactorySpi.Param;
// 获取工厂
DataStoreFactorySpi factory = new PostgisNGDataStoreFactory();
// 获取参数信息
Param[] params = factory.getParametersInfo();
System.out.println("PostGIS 参数:");
for (Param param : params) {
System.out.printf(" %s:%n", param.getName());
System.out.printf(" 类型: %s%n", param.getType().getSimpleName());
System.out.printf(" 描述: %s%n", param.getDescription());
System.out.printf(" 必需: %s%n", param.isRequired());
System.out.printf(" 默认: %s%n", param.getDefaultValue());
System.out.println();
}
// 验证参数
Map<String, Object> params = new HashMap<>();
params.put("dbtype", "postgis");
params.put("host", "localhost");
// ... 其他参数
boolean canProcess = factory.canProcess(params);
System.out.println("参数有效: " + canProcess);
6.7.2 连接池配置
// PostGIS 连接池参数
Map<String, Object> params = new HashMap<>();
params.put("dbtype", "postgis");
params.put("host", "localhost");
params.put("port", 5432);
params.put("database", "gisdb");
params.put("user", "postgres");
params.put("passwd", "password");
// 连接池设置
params.put("max connections", 20); // 最大连接数
params.put("min connections", 5); // 最小连接数
params.put("connection timeout", 20); // 连接超时(秒)
params.put("validate connections", true); // 验证连接
params.put("fetch size", 1000); // 批量获取大小
// 其他高级选项
params.put("Expose primary keys", true); // 暴露主键
params.put("encode functions", true); // 编码函数到 SQL
DataStore store = DataStoreFinder.getDataStore(params);
6.8 事务管理
6.8.1 事务基础
import org.geotools.api.data.Transaction;
import org.geotools.data.DefaultTransaction;
// 自动提交事务(默认)
Transaction autoCommit = Transaction.AUTO_COMMIT;
// 手动事务
Transaction transaction = new DefaultTransaction("edit-session");
try {
// 绑定事务到 FeatureStore
SimpleFeatureStore store = (SimpleFeatureStore) dataStore.getFeatureSource("layer");
store.setTransaction(transaction);
// 执行操作
store.addFeatures(newFeatures);
store.modifyFeatures("name", "新值", filter);
store.removeFeatures(deleteFilter);
// 提交
transaction.commit();
} catch (Exception e) {
// 回滚
transaction.rollback();
throw e;
} finally {
// 关闭
transaction.close();
}
6.8.2 事务状态
// 事务状态管理
Transaction transaction = new DefaultTransaction("session");
// 添加授权(用于锁定)
transaction.addAuthorization("lock-123");
// 获取事务状态
Transaction.State state = transaction.getState(dataStore);
// 监听提交/回滚
transaction.addAuthorization("my-auth");
6.8.3 多数据源事务
// 协调多个数据源的事务
Transaction transaction = new DefaultTransaction("multi-store");
try {
// 数据源1
SimpleFeatureStore store1 = (SimpleFeatureStore) dataStore1.getFeatureSource("layer1");
store1.setTransaction(transaction);
store1.addFeatures(features1);
// 数据源2
SimpleFeatureStore store2 = (SimpleFeatureStore) dataStore2.getFeatureSource("layer2");
store2.setTransaction(transaction);
store2.addFeatures(features2);
// 统一提交
transaction.commit();
} catch (Exception e) {
transaction.rollback();
throw e;
} finally {
transaction.close();
}
6.9 数据源锁定
6.9.1 要素锁定
import org.geotools.api.data.FeatureLock;
import org.geotools.api.data.FeatureLocking;
// 获取支持锁定的 FeatureSource
SimpleFeatureSource source = dataStore.getFeatureSource("layer");
if (source instanceof FeatureLocking) {
FeatureLocking<SimpleFeatureType, SimpleFeature> locking =
(FeatureLocking<SimpleFeatureType, SimpleFeature>) source;
// 创建锁
FeatureLock lock = FeatureLockFactory.generate(
"my-lock", // 锁名称
60 * 60 * 1000L // 过期时间(毫秒)
);
// 设置锁
locking.setFeatureLock(lock);
// 锁定要素
Filter filter = ff.equals(ff.property("id"), ff.literal(123));
int locked = locking.lockFeatures(filter);
System.out.println("锁定了 " + locked + " 个要素");
// 在事务中使用锁
Transaction transaction = new DefaultTransaction("locked-edit");
transaction.addAuthorization(lock.getAuthorization());
SimpleFeatureStore store = (SimpleFeatureStore) source;
store.setTransaction(transaction);
try {
// 编辑锁定的要素
store.modifyFeatures("name", "新值", filter);
transaction.commit();
} catch (Exception e) {
transaction.rollback();
} finally {
transaction.close();
}
// 解锁
locking.unLockFeatures(filter);
}
6.10 性能优化
6.10.1 分页查询
// 分页查询
int pageSize = 100;
int pageNumber = 0;
Query query = new Query(typeName);
query.setMaxFeatures(pageSize);
query.setStartIndex(pageNumber * pageSize);
// 获取一页数据
SimpleFeatureCollection page = source.getFeatures(query);
System.out.println("本页数量: " + page.size());
// 遍历所有页
int total = source.getCount(Query.ALL);
int totalPages = (total + pageSize - 1) / pageSize;
for (int i = 0; i < totalPages; i++) {
query.setStartIndex(i * pageSize);
SimpleFeatureCollection pageFc = source.getFeatures(query);
try (SimpleFeatureIterator iter = pageFc.features()) {
while (iter.hasNext()) {
SimpleFeature feature = iter.next();
// 处理要素
}
}
}
6.10.2 属性投影
// 只获取需要的属性(减少数据传输)
Query query = new Query(typeName);
query.setPropertyNames("id", "name", "the_geom"); // 只获取这三个属性
// 不获取几何(如果不需要)
Query query2 = new Query(typeName);
query2.setPropertyNames("id", "name", "population"); // 不包含几何
SimpleFeatureCollection fc = source.getFeatures(query2);
6.10.3 Hints 优化
import org.geotools.util.factory.Hints;
Query query = new Query(typeName);
// 提示优化
Hints hints = new Hints();
hints.put(Hints.FEATURE_2D, Boolean.TRUE); // 强制2D
hints.put(Hints.JTS_COORDINATE_SEQUENCE_FACTORY,
PackedCoordinateSequenceFactory.FLOAT_FACTORY); // 使用紧凑坐标
query.setHints(hints);
6.11 本章小结
本章详细介绍了 GeoTools 的数据源访问机制:
-
DataStore API
- DataStore 体系结构
- 核心接口设计
-
DataStoreFinder
- 发现可用数据源
- 参数配置
-
基本操作
- 读取数据
- 查询过滤
- 写入数据
-
特殊 DataStore
- MemoryDataStore
- DirectoryDataStore
- ContentDataStore 扩展
-
事务和锁定
- 事务管理
- 要素锁定
-
性能优化
- 分页查询
- 属性投影
- Hints 优化
关键要点
- 使用 DataStoreFinder 获取数据源
- 注意资源释放(dispose)
- 写操作使用事务
- 合理使用 Query 优化性能

浙公网安备 33010602011771号