在Java中使用ModelMapper简化Shapefile属性转JavaBean实战

目录

前言

一、原始的处理办法

1、使用Set方法来转换

2、使用构造方法转换

二、基于ModelMapper的动态转换

1、ModelMapper简介

2、集成到项目中

3、Shapefile属性读取 

三、总结


前言

        在现代软件开发中,尤其是在多层架构中,经常需要将数据从一个域对象转换到另一个域对象,或者从数据库结果集转换到业务对象。手动编写这些转换代码不仅耗时,而且容易出错。我们需要一种自动化的方式来处理这些转换,减少了开发工作量和潜在的错误。JavaBean是一种遵循特定编写规范的Java类,通常具有get和set方法来访问私有属性,以及一个无参构造函数。JavaBean是我们平时的开发过程当中遇到最多的类,不管它是DO、DTO、VO或者POJO等等,这些类都是以JavaBean的形式存在的。

        在之前的系列博客中,尤其是涉及空间数据管理和查询的后台设计中。大家如果认真注意的话,我们分别在空间数据库中创建一张与属性字段对应的空间表。而在采用MVC的开发模式中,模型层通常会对应一个与空间表对应的空间表。与成熟的ORM映射不同的是,ORM框架自动的会将数据库中的字段快速得与JavaBean的属性进行对应,在执行查询语句的时候可以直接关联。但是在处理空间数据时,比如使用GDAL或者GeoTools来进行数据读取时,目前暂时没有找到成熟的框架或者组件支持空间属性表与JavaBean的快速映射需求。

        本文以Java语言为例,主要讲解如何使用Java语言进行空间数据的读取,空间属性信息的读取使用GeoTools,文章首先介绍最原始的做法,即在对象中自定义转换方法来实现转换,然后详细介绍一种基于ModelMapper的空间属性映射实现方法。ModelMapper在Java的其它领域应用很多,但是在GIS领域中使用的还不多。如果您是一名GISER,同时也面临着属性信息映射的问题,不妨来这里交流讨论。

一、原始的处理办法

        在介绍本文的处理办法之前,首先依然来看一下最原始的处理办法是什么。由此,可以对比不同的处理办法的不同点,也可以发现其有点。关于如何读取空间属性信息,不管是使用GDAL或者GeoTools,不管是Gdb数据或者Shapefile数据,之前的系列博客都进行了简单的说明。因此想了解具体的读取过程的,可以翻阅之前的博客。因此这里只将转换的过程进行说明。

1、使用Set方法来转换

        首先来介绍调用对象实例的Set方法来进行转换。这种情况使用与空间属性表的字段不是很多,我们在转换时可以先从空间属性表中分别读取出具体的字段,然后再调用对应的JavaBea的Set方法来进行字段的映射和转换。比如在进行省份的空间属性信息转换的代码如下所示:

List<Province> list = new ArrayList<Province>();
for (int i = 0; i < featureCount; i++) {
	Feature feature = layer.GetFeature(i);
	String code = feature.GetFieldAsString("province_c");
	String name = feature.GetFieldAsString("province_n");
	String type = feature.GetFieldAsString("type");
	Geometry geom = feature.GetGeometryRef();
	String wkt = geom.ExportToWkt();
	Province p = new Province();
	p.setCode(code);
	p.setName(name);
	p.setType(type);
	p.setGeom(wkt);
}

        上面是一个使用GDAL解析空间数据时的属性映射的实例代码。 这种方式相信大家很熟悉,通过创建Province对象后,再分别从feature中读取省份信息,最后设置到实例对象中,最后再保存到数据库中。

2、使用构造方法转换

        除了使用set方法来进行设置,我们也可以使用构造方法来进行属性赋值。与set方法相比,构造方法可以统一设置,不需逐行进行设置。这样代码显得比较优雅,可读性也高。构造方法是将属性赋值的过程抽象都对象的构造方法中,这样实现代码的整体复用。这里以城市对象构造方法为例:

public City(String provinceCode, String provinceName, String cityCode, String cityName, String type, String geom) {
	super();
	this.provinceCode = provinceCode;
	this.provinceName = provinceName;
	this.cityCode = cityCode;
	this.cityName = cityName;
	this.type = type;
	this.geom = geom;
}

        然后在解析的过程中就可以直接调用构造方法的模式简化设置的过程,代码如下:

List<City> list = new ArrayList<City>();
for (int i = 0; i < featureCount; i++) {
	Feature feature = layer.GetFeature(i);
	String code = feature.GetFieldAsString("province_c");
	String name = feature.GetFieldAsString("province_n");
	String cityCode = feature.GetFieldAsString("city_code");
	String cityName = feature.GetFieldAsString("city_name");
	String type = feature.GetFieldAsString("type");
	Geometry geom = feature.GetGeometryRef();
    String wkt = geom.ExportToWkt();
	list.add(new City(code,name,cityCode,cityName,type,wkt));
}
cityService.saveBatch(list,100);

        可以从代码中看到,通过构造方法的方式来进行赋值,能极大的减少set方法的调用,能减少许多的代码调用。可以看到,不管使用哪种方式,我们都需要有一个从feature中根据属性名解析字段,不得不说,这种方式于我们最开始学JDBC的模式非常相似,那么有什么办法实现动态映射,不需要单独读取呢?下面我们就来分享一种方法来进行转换。

二、基于ModelMapper的动态转换

        既然有了如上的需求场景,那么有什么方法可以实现快速的属性映射吗?在Java当中,很多人首选肯定是采用反射,对吧。确实如此,使用反射可以解决我们的问题,实现上述的需求。对于反射,相信很多的朋友可以自行进行编码。本着不重复造轮子的思路,我们可以从现有的一些成熟工具中来选择符合我们期望的组件。这里推荐一款转换组件,ModelMapper。

1、ModelMapper简介

        ModelMapper是一个Java对象映射库,它能够将一个对象的数据映射到另一个对象中,从而避免手动编写数据转换代码。ModelMapper通过使用简单的配置和API,能够自动地将源对象的属性复制到目标对象的属性中,这在处理不同数据层之间的数据转换时非常有用。ModelMapper简化了开发流程,因为它减少了手动编写数据转换代码的需要。这不仅提高了开发效率,还使得代码更加简洁和易于维护。通过使用ModelMapper,开发者可以将更多的精力投入到业务逻辑的实现上,而不是数据转换的细节上。ModelMapper提供了高度的灵活性和可配置性。开发者可以根据需要自定义映射规则,例如跳过某些属性的映射,或者在映射过程中进行条件判断和自定义转换。这种灵活性使得ModelMapper能够适应各种复杂的数据转换场景。

        首先,我们来看一下ModelMapper的官网介绍,modelmapper官网。大家可以先到官网看一下它的相关介绍可帮助文档。

2、集成到项目中

        对ModelMapper有了基本的了解之后,我们来看一下如何将ModelMapper集成到Java项目当中。首先我们需要使用Maven来进行资源的引入,在Pom.xml中引入依赖,关键代码如下所示:

<!--  增加模型映射 add by 夜郎king in 2024.11.11 begin -->
<!-- https://mvnrepository.com/artifact/org.modelmapper/modelmapper -->
<dependency>
	<groupId>org.modelmapper</groupId>
	<artifactId>modelmapper</artifactId>
	<version>3.1.1</version>
</dependency>
<!--  modelmapper add by 夜郎king in 2024.11.11 end -->

        然后在Java中进行相应的集成,虽然ModelMapper本身主要作用是用于JavaBean之间的转换,但是也可以在Map和JavaBean之间进行属性映射。

@Test
public void convertMap2BeanWithUnderscoreNamingConvention() {
	HashMap<String, Object> map = new HashMap<String, Object>();
	map.put("scalerank", 6);
	map.put("LS_NAME", "test241111");
	map.put("MAX_POP10", "23562");
	ModelMapper modelMapper = new ModelMapper();
	Ne10mPopulatedPlaces pp = modelMapper.map(map, Ne10mPopulatedPlaces.class);
	System.out.println(pp);
	System.out.println(pp.getLsName());
	System.out.println(pp.getMaxPop10());
	assertEquals("test241111", pp.getLsName());
	assertEquals(23562L, pp.getMaxPop10());
}

        代码很简单,首先定义一个HashMap,map的key是属性的名字,value是实际的值。然后我们创建ModelMapper,使用默认的转换策略和匹配模式,在这种策略下实现Map向Ne10mPopulatedPlaces对象的转换。Ne10mPopulatedPlaces对象就是之前提到过的人口城市空间属性数据。

        这个类的属性比较对,大概有137个属性。针对这么多的属性映射,不管是采用set方法还是构造方法,实现代码都会非常冗长。运行上面的测试方法后,可以看到如下结果:

3、Shapefile属性读取 

        有了以上的基础,结合之前的GeoTools的属性读取方法,我们来解析属性表格。解析的思路很简单,循环属性表格,将属性表列和值组成一个HashMap,表头为key,值为value的hashMap,然后将这个HashMap转换成对应的JavaBean对象。

@Test
public void convertDbf2BeanByDefault() throws Exception {
	File dbfFile = new File(SHP_FILE);
	ShpFiles shpFile = new ShpFiles(dbfFile);
	DbaseFileReader dbfReader = new DbaseFileReader(shpFile, true, Charset.defaultCharset());
	// 读取 DBF 文件的头信息
	DbaseFileHeader header = dbfReader.getHeader();
	List<Ne10mPopulatedPlaces> dataList = new ArrayList<Ne10mPopulatedPlaces>(header.getNumRecords());
		
	List<HashMap<String, Object>> mapList = new ArrayList<HashMap<String,Object>>();
		
	ModelMapper modelMapper = new ModelMapper();
	
	while (dbfReader.hasNext()) {
		Row row = dbfReader.readRow();
		HashMap<String, Object> map = new HashMap<String, Object>();
		for (int i = 0; i < header.getNumFields(); i++) {
			map.put(header.getFieldName(i), row.read(i));
		}
		mapList.add(map);
	}
		
	int index = 0;
	for(HashMap<String, Object> map : mapList) {
		if(index > 10) {
			break;
		}
		Ne10mPopulatedPlaces places = modelMapper.map(map, Ne10mPopulatedPlaces.class);
		System.out.println(places);
		dataList.add(places);
		index ++;
	}
	System.out.println(dataList.size());
	System.out.println("属性字段数:" + header.getNumFields());
	System.out.println("数据记录数:" + header.getNumRecords());
	dbfReader.close();
}

        也是默认的映射策略和模式,通过上述的代码可以看到以下输出:

        可以看到, 通过以上的代码已经成功的实现把HashMap转换成JavaBean,同时可以看到JavaBean的属性值都成功的进行了赋值。到此,大功告成。

三、总结

        以上就是本文的主要内容,本文以Java语言为例,主要讲解如何使用Java语言进行空间数据的读取,空间属性信息的读取使用GeoTools,文章首先介绍最原始的做法,即在对象中自定义转换方法来实现转换,然后详细介绍一种基于ModelMapper的空间属性映射实现方法。ModelMapper在Java的其它领域应用很多,但是在GIS领域中使用的还不多。本文基于ModelMapper解决了在Shapefile文件读取过程中,如何实现动态的将属性表格映射到指定对象的方法进行了详细介绍。如果您是一名GISER,同时也面临着属性信息映射的问题,不妨来这里交流讨论。行文仓促,难免有许多不足之处,如有不足,还请各位专家朋友在评论区留言批评指正,不甚感激。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/915320.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Arduino IDE Windows 系统 离线安装 esp32 开发板 亲测好用。

1、前提条件需要具备特殊网络。 2、官方文档地址&#xff1a;Installing - - — Arduino ESP32 latest documentation 3、系统&#xff1a;Windows10 Arduino IDE 版本2.3.3 之前安装的esp32开发板的版本是2.0.13&#xff0c;由于之前没有接触过esp32开发&#xff0c;也没…

期权懂|请问如何用期权进行风险管理?

期权小懂每日分享期权知识&#xff0c;帮助期权新手及时有效地掌握即市趋势与新资讯&#xff01; 请问如何用期权进行风险管理&#xff1f; 一、期权可以选择交易活跃的期权合约进行风险管理&#xff1a; 对于初级投资者来说&#xff0c;选择交易活跃的期权合约是非常重要的。…

GNU构建系统和Autotool

1、前言 经常使用Linux的开发人员或者运维人员&#xff0c;可能对configure->make->make install相当熟悉。事实上&#xff0c;这叫GNU构建系统&#xff0c;利用脚本和make程序在特定平台上构建软件。这种方式成为一种习惯&#xff0c;被广泛使用。本文从用户视角和开发…

NLP论文速读|ScPO:自我一致性的偏好优化(Self-Consistency Preference Optimization)

论文速读|Self-Consistency Preference Optimization 论文信息&#xff1a; 简介&#xff1a; 这篇论文试图解决的问题是如何在没有人类标注数据的情况下&#xff0c;提高大型语言模型&#xff08;LLMs&#xff09;在复杂推理任务上的性能。现有的自我对齐技术往往因为难以分配…

【前端学习指南】Vue computed 计算属性 watch 监听器

&#x1f36d; Hello&#xff0c;我是爱吃糖的范同学 &#x1f534; 想把自己学习技术的经历和一些总结分享给大家&#xff01; &#x1f534; 通过这样的方式记录自己成长&#xff0c;同时沉淀自己的技术&#xff0c;我会把所有额外的时间和经历投放到CSDN和公众号&#xff0…

自动驾驶合集(更新中)

文章目录 车辆模型控制路径规划 车辆模型 车辆模型基础合集 控制 控制合集 路径规划 规划合集

vcenter service基本异常处理

服务&#xff1a;vcenter service 版本&#xff1a; 7.0.3 问题描述&#xff1a;无法访问vcenter ui 排障思路&#xff1a; 1. 登入vcenter所在服务器执行基础排查&#xff1a;内存、cpu、磁盘、网络等&#xff0c;发现磁盘日志目录已经爆满&#xff0c;删除180天前的日志恢…

Background Tasks Kit(后台任务开发服务)

11_13日学习笔记 Background Tasks Kit&#xff08;后台任务开发服务&#xff09; Background Tasks Kit简介 设备返回主界面、锁屏、应用切换等操作会使应用退至后台。 应用退至后台后&#xff0c;如果继续活动&#xff0c;可能会造成设备耗电快、用户界面卡顿等现象。 为了…

modbus协议 Mthings模拟器使用

进制转换 HEX 16进制 (0、1、2、3、4、5、6、7、8、9、A、B、C、D、E、F表示0-15) dec 10进制 n(16进制) -> 10 abcd.efg(n) d*n^0 c*n^1 b*n^2 a*n^3 e*n^-1 f*n^-2 g*n^-3&#xff08;10&#xff09; 10 -> n(16进制) Modbus基础概念 高位为NUM_H&…

Python多进程间通讯(包含共享内存方式)

文章目录 1 通过非共享内存配合队列方式2 通过共享内存配合队列方式 注&#xff1a;本博文测试环境为Linux系统。 1 通过非共享内存配合队列方式 下面是一个常见的生产者与消费者的模式示例&#xff0c;这里分别启动了两个子进程&#xff0c;一个为生产者&#xff08;producer…

YOLOv11实战宠物狗分类

本文采用YOLOv11作为核心算法框架&#xff0c;结合PyQt5构建用户界面&#xff0c;使用Python3进行开发。YOLOv11以其高效的特征提取能力&#xff0c;在多个图像分类任务中展现出卓越性能。本研究针对5种宠物狗数据集进行训练和优化&#xff0c;该数据集包含丰富的宠物狗图像样本…

游戏引擎学习第八天

视频参考: https://www.bilibili.com/video/BV1ouUPYAErK/ 理解下面的代码 关于虚函数 代码分解 结构体 foo 的定义&#xff1a; struct foo {int32 X;int64 Y;virtual void Bar(int c); };foo 结构体有两个成员变量&#xff1a;X&#xff08;int32 类型&#xff09;和 Y&…

我要学kali-linux之shell脚本编程1

声明&#xff01; 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&a…

尽量通俗易懂地概述.Net U nity跨语言/跨平台相关知识

本文参考来自唐老狮,Unity3D高级编程:主程手记,ai等途径 仅作学习笔记交流分享 目录 1. .Net是什么? 2. .Net框架的核心要点? 跨语言和跨平台 .Net x Unity跨平台发展史 Net Framework 2002 Unity跨平台之 Mono 2004 Unity跨平台之 IL2CPP 2015 二者区别 .NET Core …

大陆 ARS513 / ARS510 标准雷达(解析二)

1。GW_ACU (0x40) • GW_ACU_LongAccel Longitudinal acceleration of ego vehicle. • GW_ACU_LongAccel_ValidFlag Valid flag of signal “GW_ACU_LongAccel”. • GW_ACU_LateralAccel Lateral acceleration of ego vehicle. Signal quality requirements for “GW_ACU_La…

【游戏引擎之路】登神长阶(十四)——OpenGL教程:士别三日,当刮目相看

【游戏引擎之路】登神长阶&#xff08;十四&#xff09;——OpenGL教程&#xff1a;士别三日&#xff0c;当刮目相看 2024年 5月20日-6月4日&#xff1a;攻克2D物理引擎。 2024年 6月4日-6月13日&#xff1a;攻克《3D数学基础》。 2024年 6月13日-6月20日&#xff1a;攻克《3D…

【C++动态规划】2304. 网格中的最小路径代价|1658

本文涉及知识点 C动态规划 LeetCode2304. 网格中的最小路径代价 给你一个下标从 0 开始的整数矩阵 grid &#xff0c;矩阵大小为 m x n &#xff0c;由从 0 到 m * n - 1 的不同整数组成。你可以在此矩阵中&#xff0c;从一个单元格移动到 下一行 的任何其他单元格。如果你位…

数据中台解决方案

文件是关于数据中台解决方案的详细介绍&#xff0c;内容涵盖了数据中台的定义、建设方案、实施步骤、以及在数字化转型中的作用。以下是对文件内容的分析和总结&#xff1a; 1. 数字化转型背景 国家政策支持&#xff1a;提到了《中华人民共和国国民经济和社会发展第十四个五年…

JS 实现WebSocket通讯和什么是WebSocket

WebSocket 介绍&#xff1a; WebSocket 是一种网络传输协议&#xff0c;可在单个 TCP 连接上进行全双工通信。它允许服务器主动向客户端推送信息&#xff0c;客户端也能实时接收服务器的响应。 客户端 这里实现了将input内的内容发送给客户端&#xff0c;并将接收到的服务器的…

K8S单节点部署及集群部署

1.Minikube搭建单节点K8S 前置条件&#xff1a;安装docker&#xff0c;注意版本兼容问题 # 配置docker源 wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo -O /etc/yum.repos.d/docker-ce.repo# 安装docker环境依赖 yum install -y yum-utils device-m…