GDAL优化
This commit is contained in:
@ -29,7 +29,7 @@ public class GdalController {
|
||||
public void importDataStreamGzip(@Parameter(description = "矢量文件路径", required = true) @RequestParam("path") String path, HttpServletResponse response) throws IOException {
|
||||
|
||||
// 解析矢量文件、得到JSON数据
|
||||
String jsonData = GdalUtil.readVectorToGeoJson(path);
|
||||
String jsonData = GdalUtil.readVectorToSpecifiedGeoJson(path);
|
||||
|
||||
// 设置响应头
|
||||
String filename = URLEncoder.encode("data.gz", StandardCharsets.UTF_8);
|
||||
|
||||
@ -77,7 +77,7 @@ public class IconLibraryController {
|
||||
// 校验图标库文件是否已存在
|
||||
if (iconFile.exists()) {
|
||||
if (iconFile.isDirectory()) {
|
||||
return ApiResponse.failure("同名目录已存在,无法创建文件:" + iconPath);
|
||||
return ApiResponse.failure("同名目录已存在、无法创建文件:" + iconPath);
|
||||
}
|
||||
return ApiResponse.failure("图标库文件已存在:" + iconPath);
|
||||
}
|
||||
@ -391,7 +391,7 @@ public class IconLibraryController {
|
||||
|
||||
private String getIconLibrary() {
|
||||
LambdaQueryWrapper<IconLibrary> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(IconLibrary::getIsEnable, 1); // 1=启用,0=未启用
|
||||
queryWrapper.eq(IconLibrary::getIsEnable, 1); // 1=启用、0=未启用
|
||||
IconLibrary library = iconLibraryService.getOne(queryWrapper);
|
||||
return library == null ? null : library.getPath();
|
||||
}
|
||||
@ -404,7 +404,7 @@ public class IconLibraryController {
|
||||
iconLibraryService.updateById(library);
|
||||
}
|
||||
|
||||
// 检查路径是否已存在(存在则启用,不存在则新增)
|
||||
// 检查路径是否已存在(存在则启用、不存在则新增)
|
||||
LambdaQueryWrapper<IconLibrary> pathWrapper = new LambdaQueryWrapper<>();
|
||||
pathWrapper.eq(IconLibrary::getPath, iconPath);
|
||||
IconLibrary existLibrary = iconLibraryService.getOne(pathWrapper);
|
||||
|
||||
@ -77,7 +77,7 @@ public class MilitaryLibraryController {
|
||||
// 校验军标库文件是否已存在
|
||||
if (militaryFile.exists()) {
|
||||
if (militaryFile.isDirectory()) {
|
||||
return ApiResponse.failure("同名目录已存在,无法创建文件:" + militaryPath);
|
||||
return ApiResponse.failure("同名目录已存在、无法创建文件:" + militaryPath);
|
||||
}
|
||||
return ApiResponse.failure("军标库文件已存在:" + militaryPath);
|
||||
}
|
||||
@ -390,7 +390,7 @@ public class MilitaryLibraryController {
|
||||
|
||||
private String getMilitaryLibrary() {
|
||||
LambdaQueryWrapper<MilitaryLibrary> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(MilitaryLibrary::getIsEnable, 1); // 1=启用,0=未启用
|
||||
queryWrapper.eq(MilitaryLibrary::getIsEnable, 1); // 1=启用、0=未启用
|
||||
MilitaryLibrary library = militaryLibraryService.getOne(queryWrapper);
|
||||
return library == null ? null : library.getPath();
|
||||
}
|
||||
@ -403,7 +403,7 @@ public class MilitaryLibraryController {
|
||||
militaryLibraryService.updateById(library);
|
||||
}
|
||||
|
||||
// 检查路径是否已存在(存在则启用,不存在则新增)
|
||||
// 检查路径是否已存在(存在则启用、不存在则新增)
|
||||
LambdaQueryWrapper<MilitaryLibrary> pathWrapper = new LambdaQueryWrapper<>();
|
||||
pathWrapper.eq(MilitaryLibrary::getPath, militaryPath);
|
||||
MilitaryLibrary existLibrary = militaryLibraryService.getOne(pathWrapper);
|
||||
|
||||
@ -2,7 +2,8 @@ package com.yj.earth.common.util;
|
||||
|
||||
import org.gdal.gdal.gdal;
|
||||
import org.gdal.ogr.*;
|
||||
import org.gdal.osr.SpatialReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@ -11,88 +12,236 @@ import java.util.Map;
|
||||
|
||||
public class GdalUtil {
|
||||
|
||||
// 静态初始化
|
||||
// 静态初始化ObjectMapper,避免重复创建
|
||||
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||
|
||||
// 静态初始化GDAL
|
||||
static {
|
||||
// 可以考虑设置缓存目录来提升性能
|
||||
gdal.SetConfigOption("GDAL_CACHEMAX", "1024"); // 设置1GB缓存
|
||||
gdal.SetConfigOption("PROJ_LIB", "./lib");
|
||||
gdal.AllRegister();
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取矢量文件并转换为标准GeoJSON格式
|
||||
* 读取矢量文件、将每个图层转换为符合指定格式的 GeoJSON
|
||||
*/
|
||||
public static String readVectorToGeoJson(String filePath) {
|
||||
// 打开矢量文件
|
||||
DataSource dataSource = ogr.Open(filePath);
|
||||
if (dataSource == null) {
|
||||
throw new RuntimeException("无法打开矢量文件: " + filePath);
|
||||
}
|
||||
|
||||
// 创建GeoJSON根对象 - FeatureCollection
|
||||
Map<String, Object> geoJson = new HashMap<>();
|
||||
geoJson.put("type", "FeatureCollection");
|
||||
|
||||
// 存储所有要素
|
||||
List<Map<String, Object>> features = new ArrayList<>();
|
||||
geoJson.put("features", features);
|
||||
|
||||
// 获取所有图层
|
||||
int layerCount = dataSource.GetLayerCount();
|
||||
for (int i = 0; i < layerCount; i++) {
|
||||
Layer layer = dataSource.GetLayer(i);
|
||||
if (layer == null) continue;
|
||||
|
||||
// 获取空间参考信息
|
||||
SpatialReference srs = layer.GetSpatialRef();
|
||||
if (srs != null && features.isEmpty()) { // 只添加一次CRS信息
|
||||
Map<String, Object> crs = new HashMap<>();
|
||||
Map<String, Object> crsProps = new HashMap<>();
|
||||
crsProps.put("name", srs.GetAttrValue("AUTHORITY", 0) + ":" + srs.GetAttrValue("AUTHORITY", 1));
|
||||
crs.put("type", "name");
|
||||
crs.put("properties", crsProps);
|
||||
geoJson.put("crs", crs);
|
||||
public static String readVectorToSpecifiedGeoJson(String filePath) {
|
||||
DataSource dataSource = null;
|
||||
try {
|
||||
// 打开矢量文件,使用只读模式
|
||||
dataSource = ogr.Open(filePath, 0);
|
||||
if (dataSource == null) {
|
||||
throw new RuntimeException("无法打开矢量文件: " + filePath + ", 错误信息: " + gdal.GetLastErrorMsg());
|
||||
}
|
||||
|
||||
// 创建结果结构,预先估计容量
|
||||
Map<String, Object> result = new HashMap<>(2);
|
||||
int layerCount = dataSource.GetLayerCount();
|
||||
List<Map<String, Object>> layerList = new ArrayList<>(layerCount);
|
||||
|
||||
// 处理每个图层
|
||||
for (int i = 0; i < layerCount; i++) {
|
||||
Layer layer = dataSource.GetLayer(i);
|
||||
if (layer == null) continue;
|
||||
|
||||
// 为当前图层创建符合格式的 GeoJSON
|
||||
Map<String, Object> layerGeoJson = convertLayerToSpecifiedFormat(layer);
|
||||
if (layerGeoJson != null) {
|
||||
layerList.add(layerGeoJson);
|
||||
}
|
||||
// 提前释放图层资源
|
||||
layer.delete();
|
||||
}
|
||||
|
||||
result.put("list", layerList);
|
||||
|
||||
// 使用单例ObjectMapper转换为JSON字符串
|
||||
return OBJECT_MAPPER.writeValueAsString(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("处理矢量文件时发生错误: " + e.getMessage() + ", 详细错误: " + gdal.GetLastErrorMsg(), e);
|
||||
} finally {
|
||||
// 确保数据源被正确释放
|
||||
if (dataSource != null) {
|
||||
dataSource.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将单个图层转换为符合指定格式的 GeoJSON
|
||||
*/
|
||||
private static Map<String, Object> convertLayerToSpecifiedFormat(Layer layer) {
|
||||
try {
|
||||
// 获取要素数量,预分配集合大小
|
||||
long featureCount = layer.GetFeatureCount();
|
||||
if (featureCount <= 0) {
|
||||
return null; // 跳过空图层
|
||||
}
|
||||
|
||||
// 创建符合格式的 GeoJSON FeatureCollection
|
||||
Map<String, Object> geoJson = new HashMap<>(3);
|
||||
geoJson.put("type", "FeatureCollection");
|
||||
geoJson.put("name", layer.GetName());
|
||||
|
||||
// 预分配要素列表容量
|
||||
List<Map<String, Object>> features = new ArrayList<>((int) Math.min(featureCount, Integer.MAX_VALUE));
|
||||
|
||||
// 获取字段定义
|
||||
FeatureDefn featureDefn = layer.GetLayerDefn();
|
||||
int fieldCount = featureDefn.GetFieldCount();
|
||||
|
||||
// 读取所有要素
|
||||
// 缓存字段信息,避免重复获取
|
||||
String[] fieldNames = new String[fieldCount];
|
||||
int[] fieldTypes = new int[fieldCount];
|
||||
for (int j = 0; j < fieldCount; j++) {
|
||||
FieldDefn fieldDef = featureDefn.GetFieldDefn(j);
|
||||
fieldNames[j] = fieldDef.GetName();
|
||||
fieldTypes[j] = fieldDef.GetFieldType();
|
||||
}
|
||||
|
||||
// 读取图层中的所有要素
|
||||
layer.ResetReading();
|
||||
Feature feature;
|
||||
|
||||
while ((feature = layer.GetNextFeature()) != null) {
|
||||
// 创建Feature对象
|
||||
Map<String, Object> featureObj = new HashMap<>();
|
||||
featureObj.put("type", "Feature");
|
||||
|
||||
// 存储属性信息
|
||||
Map<String, Object> properties = new HashMap<>();
|
||||
// 可以添加图层名称作为属性
|
||||
properties.put("layerName", layer.GetName());
|
||||
Map<String, Object> featureMap = new HashMap<>(3);
|
||||
featureMap.put("type", "Feature");
|
||||
|
||||
// 处理属性
|
||||
Map<String, Object> properties = new HashMap<>(fieldCount);
|
||||
for (int j = 0; j < fieldCount; j++) {
|
||||
String fieldName = featureDefn.GetFieldDefn(j).GetName();
|
||||
properties.put(fieldName, feature.GetFieldAsString(j));
|
||||
properties.put(fieldNames[j], getFieldValue(feature, j, fieldTypes[j]));
|
||||
}
|
||||
featureObj.put("properties", properties);
|
||||
featureMap.put("properties", properties);
|
||||
|
||||
// 存储几何信息,转换为GeoJSON格式
|
||||
// 处理几何信息 - 直接构建Map而不是先转JSON再解析
|
||||
Geometry geometry = feature.GetGeometryRef();
|
||||
if (geometry != null) {
|
||||
featureMap.put("geometry", convertGeometryToMap(geometry));
|
||||
|
||||
String geometryJson = geometry.ExportToJson();
|
||||
Map<String, Object> geometryObj = JsonUtil.jsonToMap(geometryJson);
|
||||
featureObj.put("geometry", geometryObj);
|
||||
}
|
||||
|
||||
features.add(featureObj);
|
||||
feature.delete();
|
||||
features.add(featureMap);
|
||||
feature.delete(); // 及时释放要素资源
|
||||
}
|
||||
|
||||
geoJson.put("features", features);
|
||||
return geoJson;
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("转换图层 " + layer.GetName() + " 时发生错误: " + e.getMessage() + ", 详细错误: " + gdal.GetLastErrorMsg());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将Geometry直接转换为Map,避免JSON序列化/反序列化开销
|
||||
*/
|
||||
private static Map<String, Object> convertGeometryToMap(Geometry geometry) {
|
||||
if (geometry == null) {
|
||||
return createEmptyMultiPolygon();
|
||||
}
|
||||
|
||||
// 关闭数据源
|
||||
dataSource.delete();
|
||||
try {
|
||||
String geometryType = geometry.GetGeometryName();
|
||||
Map<String, Object> geometryMap = new HashMap<>(2);
|
||||
|
||||
// 转换为JSON并返回
|
||||
return JsonUtil.toJson(geoJson);
|
||||
// 如果不是MultiPolygon,尝试转换
|
||||
if (!"MULTIPOLYGON".equals(geometryType)) {
|
||||
// 转换为MultiPolygon
|
||||
Geometry multiPolygon = geometry.MakeValid();
|
||||
if (!"MULTIPOLYGON".equals(multiPolygon.GetGeometryName())) {
|
||||
multiPolygon = geometry.Buffer(0); // 修复几何问题
|
||||
}
|
||||
if (!"MULTIPOLYGON".equals(multiPolygon.GetGeometryName())) {
|
||||
// 如果仍然不是MultiPolygon,创建空的
|
||||
return createEmptyMultiPolygon();
|
||||
}
|
||||
geometry = multiPolygon;
|
||||
}
|
||||
|
||||
geometryMap.put("type", "MultiPolygon");
|
||||
geometryMap.put("coordinates", getCoordinates(geometry));
|
||||
|
||||
return geometryMap;
|
||||
} catch (Exception e) {
|
||||
return createEmptyMultiPolygon();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接获取几何坐标,避免JSON转换
|
||||
*/
|
||||
private static List<?> getCoordinates(Geometry geometry) {
|
||||
List<Object> coordinates = new ArrayList<>();
|
||||
|
||||
if (geometry == null) {
|
||||
return coordinates;
|
||||
}
|
||||
|
||||
int geometryCount = geometry.GetGeometryCount();
|
||||
for (int i = 0; i < geometryCount; i++) {
|
||||
Geometry polygon = geometry.GetGeometryRef(i);
|
||||
if (polygon == null) continue;
|
||||
|
||||
List<Object> polygonCoords = new ArrayList<>();
|
||||
int ringCount = polygon.GetGeometryCount();
|
||||
for (int j = 0; j < ringCount; j++) {
|
||||
Geometry ring = polygon.GetGeometryRef(j);
|
||||
if (ring == null) continue;
|
||||
|
||||
List<List<Double>> ringCoords = new ArrayList<>();
|
||||
int pointCount = ring.GetPointCount();
|
||||
for (int k = 0; k < pointCount; k++) {
|
||||
double[] point = ring.GetPoint(k);
|
||||
List<Double> pointCoords = new ArrayList<>(2);
|
||||
pointCoords.add(point[0]);
|
||||
pointCoords.add(point[1]);
|
||||
ringCoords.add(pointCoords);
|
||||
}
|
||||
polygonCoords.add(ringCoords);
|
||||
}
|
||||
coordinates.add(polygonCoords);
|
||||
}
|
||||
|
||||
return coordinates;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字段值、根据字段类型处理
|
||||
*/
|
||||
private static Object getFieldValue(Feature feature, int fieldIndex, int fieldType) {
|
||||
try {
|
||||
switch (fieldType) {
|
||||
case ogr.OFTInteger:
|
||||
return feature.GetFieldAsInteger(fieldIndex);
|
||||
case ogr.OFTInteger64:
|
||||
return feature.GetFieldAsInteger64(fieldIndex);
|
||||
case ogr.OFTReal:
|
||||
return feature.GetFieldAsDouble(fieldIndex);
|
||||
case ogr.OFTString:
|
||||
case ogr.OFTStringList:
|
||||
case ogr.OFTWideString:
|
||||
return feature.GetFieldAsString(fieldIndex);
|
||||
case ogr.OFTDate:
|
||||
case ogr.OFTTime:
|
||||
case ogr.OFTDateTime:
|
||||
return feature.GetFieldAsString(fieldIndex);
|
||||
default:
|
||||
// 对于其他类型,统一返回字符串表示
|
||||
return feature.GetFieldAsString(fieldIndex);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return feature.GetFieldAsString(fieldIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建空的 MultiPolygon 几何对象
|
||||
*/
|
||||
private static Map<String, Object> createEmptyMultiPolygon() {
|
||||
Map<String, Object> geometry = new HashMap<>(2);
|
||||
geometry.put("type", "MultiPolygon");
|
||||
geometry.put("coordinates", new ArrayList<>());
|
||||
return geometry;
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ public class SQLiteUtil {
|
||||
// 统一日期格式(LocalDateTime)
|
||||
private static final DateTimeFormatter LOCAL_DATE_TIME_FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
|
||||
|
||||
// 连接池缓存:key=数据库文件路径,value=DBCP2数据源(支持多连接复用)
|
||||
// 连接池缓存:key=数据库文件路径、value=DBCP2数据源(支持多连接复用)
|
||||
private static final Map<String, BasicDataSource> DATA_SOURCE_POOL = new ConcurrentHashMap<>();
|
||||
|
||||
// 字段缓存:缓存类的字段映射(避免反射重复开销)
|
||||
@ -46,7 +46,7 @@ public class SQLiteUtil {
|
||||
if (dbFilePath == null || dbFilePath.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("数据库文件路径不能为空");
|
||||
}
|
||||
// 不存在则创建数据源,存在则直接从池获取连接
|
||||
// 不存在则创建数据源、存在则直接从池获取连接
|
||||
BasicDataSource dataSource = DATA_SOURCE_POOL.computeIfAbsent(dbFilePath, SQLiteUtil::createDataSource);
|
||||
return dataSource.getConnection();
|
||||
}
|
||||
@ -63,7 +63,7 @@ public class SQLiteUtil {
|
||||
dataSource.setDriverClassName("org.sqlite.JDBC");
|
||||
dataSource.setUrl("jdbc:sqlite:" + dbFilePath);
|
||||
|
||||
// 2. 连接池核心参数(根据并发量调整,SQLite不建议过多连接)
|
||||
// 2. 连接池核心参数(根据并发量调整、SQLite不建议过多连接)
|
||||
dataSource.setMaxTotal(30); // 最大连接数:20-50(根据服务器CPU/内存调整)
|
||||
dataSource.setMaxIdle(15); // 最大空闲连接:保留部分连接避免频繁创建
|
||||
dataSource.setMinIdle(5); // 最小空闲连接:保证基础并发响应速度
|
||||
@ -84,7 +84,7 @@ public class SQLiteUtil {
|
||||
try (Connection conn = dataSource.getConnection();
|
||||
Statement stmt = conn.createStatement()) {
|
||||
stmt.execute("PRAGMA journal_mode=WAL;"); // 启用WAL模式:支持多读者+单写者(核心优化)
|
||||
stmt.execute("PRAGMA cache_size=-20000;"); // 页面缓存20MB(负号表示KB单位,内存足可调大)
|
||||
stmt.execute("PRAGMA cache_size=-20000;"); // 页面缓存20MB(负号表示KB单位、内存足可调大)
|
||||
stmt.execute("PRAGMA synchronous=NORMAL;"); // 同步级别:平衡性能与安全(崩溃最多丢WAL日志)
|
||||
stmt.execute("PRAGMA temp_store=MEMORY;"); // 临时表/索引存内存(减少磁盘IO)
|
||||
stmt.execute("PRAGMA busy_timeout=2000;"); // 忙等待超时:2秒(避免瞬时并发锁等待)
|
||||
@ -165,7 +165,7 @@ public class SQLiteUtil {
|
||||
throw new IllegalArgumentException("查询SQL不能为空");
|
||||
}
|
||||
|
||||
// 预加载字段映射(缓存生效,避免重复反射)
|
||||
// 预加载字段映射(缓存生效、避免重复反射)
|
||||
Map<String, Field> fieldMap = getFieldMap(clazz);
|
||||
|
||||
// try-with-resources:自动关闭Connection、PreparedStatement、ResultSet
|
||||
@ -175,7 +175,7 @@ public class SQLiteUtil {
|
||||
|
||||
ResultSetMetaData metaData = rs.getMetaData();
|
||||
int columnCount = metaData.getColumnCount();
|
||||
// 预构建「列名-字段」映射(一次构建,循环复用)
|
||||
// 预构建「列名-字段」映射(一次构建、循环复用)
|
||||
ColumnFieldMapping[] mappings = buildColumnFieldMappings(metaData, columnCount, fieldMap);
|
||||
|
||||
// 处理结果集(反射赋值)
|
||||
@ -254,7 +254,7 @@ public class SQLiteUtil {
|
||||
*
|
||||
* @param dbFilePath 数据库路径
|
||||
* @param sql DDL语句
|
||||
* @param params SQL参数(可选,如动态表名)
|
||||
* @param params SQL参数(可选、如动态表名)
|
||||
*/
|
||||
public static void executeDDL(String dbFilePath, String sql, List<Object> params) throws SQLException {
|
||||
if (dbFilePath == null || dbFilePath.trim().isEmpty()) {
|
||||
@ -269,7 +269,7 @@ public class SQLiteUtil {
|
||||
pstmt.execute();
|
||||
} catch (SQLException e) {
|
||||
closeDataSource(dbFilePath);
|
||||
throw new SQLException("执行DDL失败(SQL:" + sql + ",路径:" + dbFilePath + ")", e);
|
||||
throw new SQLException("执行DDL失败(SQL:" + sql + "、路径:" + dbFilePath + ")", e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -284,7 +284,7 @@ public class SQLiteUtil {
|
||||
// ========================== 工具辅助方法 ==========================
|
||||
|
||||
/**
|
||||
* 创建PreparedStatement(复用Connection,避免重复获取)
|
||||
* 创建PreparedStatement(复用Connection、避免重复获取)
|
||||
*
|
||||
* @param conn 已获取的Connection(从连接池来)
|
||||
* @param sql SQL语句
|
||||
@ -362,7 +362,7 @@ public class SQLiteUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取类的字段缓存(含父类字段,一次反射永久缓存)
|
||||
* 获取类的字段缓存(含父类字段、一次反射永久缓存)
|
||||
*
|
||||
* @param clazz 目标类
|
||||
* @return 字段名→Field的映射(不可修改)
|
||||
@ -385,7 +385,7 @@ public class SQLiteUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 优化的字段赋值(处理类型转换,避免重复异常捕获)
|
||||
* 优化的字段赋值(处理类型转换、避免重复异常捕获)
|
||||
*
|
||||
* @param obj 目标对象
|
||||
* @param field 字段
|
||||
@ -407,7 +407,7 @@ public class SQLiteUtil {
|
||||
field.set(obj, convertedValue);
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
System.err.println("警告:字段赋值失败(字段:" + field.getName() + ",值类型:" + value.getClass().getName() + "):" + e.getMessage());
|
||||
System.err.println("警告:字段赋值失败(字段:" + field.getName() + "、值类型:" + value.getClass().getName() + "):" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ -451,7 +451,7 @@ public class SQLiteUtil {
|
||||
}
|
||||
|
||||
// 不支持的类型转换(打印警告)
|
||||
System.err.println("警告:不支持的类型转换(目标类型:" + targetType.getName() + ",原始值类型:" + value.getClass().getName() + ")");
|
||||
System.err.println("警告:不支持的类型转换(目标类型:" + targetType.getName() + "、原始值类型:" + value.getClass().getName() + ")");
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -547,7 +547,7 @@ public class SQLiteUtil {
|
||||
// ========================== 内部辅助类 ==========================
|
||||
|
||||
/**
|
||||
* 列-字段映射模型(一次性构建,减少循环内计算)
|
||||
* 列-字段映射模型(一次性构建、减少循环内计算)
|
||||
*/
|
||||
private static class ColumnFieldMapping {
|
||||
String columnName; // 数据库列名
|
||||
|
||||
Reference in New Issue
Block a user