第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 的数据源访问机制:

  1. DataStore API

    • DataStore 体系结构
    • 核心接口设计
  2. DataStoreFinder

    • 发现可用数据源
    • 参数配置
  3. 基本操作

    • 读取数据
    • 查询过滤
    • 写入数据
  4. 特殊 DataStore

    • MemoryDataStore
    • DirectoryDataStore
    • ContentDataStore 扩展
  5. 事务和锁定

    • 事务管理
    • 要素锁定
  6. 性能优化

    • 分页查询
    • 属性投影
    • Hints 优化

关键要点

  • 使用 DataStoreFinder 获取数据源
  • 注意资源释放(dispose)
  • 写操作使用事务
  • 合理使用 Query 优化性能

← 上一章:要素模型与数据结构 | 返回目录 | 下一章:Shapefile读写详解 →

posted @ 2025-12-29 11:40  我才是银古  阅读(8)  评论(0)    收藏  举报