在Java中使用ModelMapper簡化Shapefile屬性轉(zhuǎn)JavaBean實(shí)戰(zhàn)過程
前言
在現(xiàn)代軟件開發(fā)中,尤其是在多層架構(gòu)中,經(jīng)常需要將數(shù)據(jù)從一個(gè)域?qū)ο筠D(zhuǎn)換到另一個(gè)域?qū)ο?,或者從?shù)據(jù)庫結(jié)果集轉(zhuǎn)換到業(yè)務(wù)對(duì)象。手動(dòng)編寫這些轉(zhuǎn)換代碼不僅耗時(shí),而且容易出錯(cuò)。我們需要一種自動(dòng)化的方式來處理這些轉(zhuǎn)換,減少了開發(fā)工作量和潛在的錯(cuò)誤。JavaBean是一種遵循特定編寫規(guī)范的Java類,通常具有g(shù)et和set方法來訪問私有屬性,以及一個(gè)無參構(gòu)造函數(shù)。JavaBean是我們平時(shí)的開發(fā)過程當(dāng)中遇到最多的類,不管它是DO、DTO、VO或者POJO等等,這些類都是以JavaBean的形式存在的。

在之前的系列博客中,尤其是涉及空間數(shù)據(jù)管理和查詢的后臺(tái)設(shè)計(jì)中。大家如果認(rèn)真注意的話,我們分別在空間數(shù)據(jù)庫中創(chuàng)建一張與屬性字段對(duì)應(yīng)的空間表。而在采用MVC的開發(fā)模式中,模型層通常會(huì)對(duì)應(yīng)一個(gè)與空間表對(duì)應(yīng)的空間表。與成熟的ORM映射不同的是,ORM框架自動(dòng)的會(huì)將數(shù)據(jù)庫中的字段快速得與JavaBean的屬性進(jìn)行對(duì)應(yīng),在執(zhí)行查詢語句的時(shí)候可以直接關(guān)聯(lián)。但是在處理空間數(shù)據(jù)時(shí),比如使用GDAL或者GeoTools來進(jìn)行數(shù)據(jù)讀取時(shí),目前暫時(shí)沒有找到成熟的框架或者組件支持空間屬性表與JavaBean的快速映射需求。
本文以Java語言為例,主要講解如何使用Java語言進(jìn)行空間數(shù)據(jù)的讀取,空間屬性信息的讀取使用GeoTools,文章首先介紹最原始的做法,即在對(duì)象中自定義轉(zhuǎn)換方法來實(shí)現(xiàn)轉(zhuǎn)換,然后詳細(xì)介紹一種基于ModelMapper的空間屬性映射實(shí)現(xiàn)方法。ModelMapper在Java的其它領(lǐng)域應(yīng)用很多,但是在GIS領(lǐng)域中使用的還不多。如果您是一名GISER,同時(shí)也面臨著屬性信息映射的問題,不妨來這里交流討論。
一、原始的處理辦法
在介紹本文的處理辦法之前,首先依然來看一下最原始的處理辦法是什么。由此,可以對(duì)比不同的處理辦法的不同點(diǎn),也可以發(fā)現(xiàn)其有點(diǎn)。關(guān)于如何讀取空間屬性信息,不管是使用GDAL或者GeoTools,不管是Gdb數(shù)據(jù)或者Shapefile數(shù)據(jù),之前的系列博客都進(jìn)行了簡單的說明。因此想了解具體的讀取過程的,可以翻閱之前的博客。因此這里只將轉(zhuǎn)換的過程進(jìn)行說明。
1、使用Set方法來轉(zhuǎn)換
首先來介紹調(diào)用對(duì)象實(shí)例的Set方法來進(jìn)行轉(zhuǎn)換。這種情況使用與空間屬性表的字段不是很多,我們?cè)谵D(zhuǎn)換時(shí)可以先從空間屬性表中分別讀取出具體的字段,然后再調(diào)用對(duì)應(yīng)的JavaBea的Set方法來進(jìn)行字段的映射和轉(zhuǎn)換。比如在進(jìn)行省份的空間屬性信息轉(zhuǎn)換的代碼如下所示:
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);
}上面是一個(gè)使用GDAL解析空間數(shù)據(jù)時(shí)的屬性映射的實(shí)例代碼。 這種方式相信大家很熟悉,通過創(chuàng)建Province對(duì)象后,再分別從feature中讀取省份信息,最后設(shè)置到實(shí)例對(duì)象中,最后再保存到數(shù)據(jù)庫中。
2、使用構(gòu)造方法轉(zhuǎn)換
除了使用set方法來進(jìn)行設(shè)置,我們也可以使用構(gòu)造方法來進(jìn)行屬性賦值。與set方法相比,構(gòu)造方法可以統(tǒng)一設(shè)置,不需逐行進(jìn)行設(shè)置。這樣代碼顯得比較優(yōu)雅,可讀性也高。構(gòu)造方法是將屬性賦值的過程抽象都對(duì)象的構(gòu)造方法中,這樣實(shí)現(xiàn)代碼的整體復(fù)用。這里以城市對(duì)象構(gòu)造方法為例:
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;
}然后在解析的過程中就可以直接調(diào)用構(gòu)造方法的模式簡化設(shè)置的過程,代碼如下:
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);可以從代碼中看到,通過構(gòu)造方法的方式來進(jìn)行賦值,能極大的減少set方法的調(diào)用,能減少許多的代碼調(diào)用。可以看到,不管使用哪種方式,我們都需要有一個(gè)從feature中根據(jù)屬性名解析字段,不得不說,這種方式于我們最開始學(xué)JDBC的模式非常相似,那么有什么辦法實(shí)現(xiàn)動(dòng)態(tài)映射,不需要單獨(dú)讀取呢?下面我們就來分享一種方法來進(jìn)行轉(zhuǎn)換。
二、基于ModelMapper的動(dòng)態(tài)轉(zhuǎn)換
既然有了如上的需求場(chǎng)景,那么有什么方法可以實(shí)現(xiàn)快速的屬性映射嗎?在Java當(dāng)中,很多人首選肯定是采用反射,對(duì)吧。確實(shí)如此,使用反射可以解決我們的問題,實(shí)現(xiàn)上述的需求。對(duì)于反射,相信很多的朋友可以自行進(jìn)行編碼。本著不重復(fù)造輪子的思路,我們可以從現(xiàn)有的一些成熟工具中來選擇符合我們期望的組件。這里推薦一款轉(zhuǎn)換組件,ModelMapper。
1、ModelMapper簡介
ModelMapper是一個(gè)Java對(duì)象映射庫,它能夠?qū)⒁粋€(gè)對(duì)象的數(shù)據(jù)映射到另一個(gè)對(duì)象中,從而避免手動(dòng)編寫數(shù)據(jù)轉(zhuǎn)換代碼。ModelMapper通過使用簡單的配置和API,能夠自動(dòng)地將源對(duì)象的屬性復(fù)制到目標(biāo)對(duì)象的屬性中,這在處理不同數(shù)據(jù)層之間的數(shù)據(jù)轉(zhuǎn)換時(shí)非常有用。ModelMapper簡化了開發(fā)流程,因?yàn)樗鼫p少了手動(dòng)編寫數(shù)據(jù)轉(zhuǎn)換代碼的需要。這不僅提高了開發(fā)效率,還使得代碼更加簡潔和易于維護(hù)。通過使用ModelMapper,開發(fā)者可以將更多的精力投入到業(yè)務(wù)邏輯的實(shí)現(xiàn)上,而不是數(shù)據(jù)轉(zhuǎn)換的細(xì)節(jié)上。ModelMapper提供了高度的靈活性和可配置性。開發(fā)者可以根據(jù)需要自定義映射規(guī)則,例如跳過某些屬性的映射,或者在映射過程中進(jìn)行條件判斷和自定義轉(zhuǎn)換。這種靈活性使得ModelMapper能夠適應(yīng)各種復(fù)雜的數(shù)據(jù)轉(zhuǎn)換場(chǎng)景。
首先,我們來看一下ModelMapper的官網(wǎng)介紹,modelmapper官網(wǎng)。大家可以先到官網(wǎng)看一下它的相關(guān)介紹可幫助文檔。

2、集成到項(xiàng)目中
對(duì)ModelMapper有了基本的了解之后,我們來看一下如何將ModelMapper集成到Java項(xiàng)目當(dāng)中。首先我們需要使用Maven來進(jìn)行資源的引入,在Pom.xml中引入依賴,關(guān)鍵代碼如下所示:
<!-- 增加模型映射 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中進(jìn)行相應(yīng)的集成,雖然ModelMapper本身主要作用是用于JavaBean之間的轉(zhuǎn)換,但是也可以在Map和JavaBean之間進(jìn)行屬性映射。
@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());
}代碼很簡單,首先定義一個(gè)HashMap,map的key是屬性的名字,value是實(shí)際的值。然后我們創(chuàng)建ModelMapper,使用默認(rèn)的轉(zhuǎn)換策略和匹配模式,在這種策略下實(shí)現(xiàn)Map向Ne10mPopulatedPlaces對(duì)象的轉(zhuǎn)換。Ne10mPopulatedPlaces對(duì)象就是之前提到過的人口城市空間屬性數(shù)據(jù)。

這個(gè)類的屬性比較對(duì),大概有137個(gè)屬性。針對(duì)這么多的屬性映射,不管是采用set方法還是構(gòu)造方法,實(shí)現(xiàn)代碼都會(huì)非常冗長。運(yùn)行上面的測(cè)試方法后,可以看到如下結(jié)果:

3、Shapefile屬性讀取
有了以上的基礎(chǔ),結(jié)合之前的GeoTools的屬性讀取方法,我們來解析屬性表格。解析的思路很簡單,循環(huán)屬性表格,將屬性表列和值組成一個(gè)HashMap,表頭為key,值為value的hashMap,然后將這個(gè)HashMap轉(zhuǎn)換成對(duì)應(yīng)的JavaBean對(duì)象。
@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("屬性字段數(shù):" + header.getNumFields());
System.out.println("數(shù)據(jù)記錄數(shù):" + header.getNumRecords());
dbfReader.close();
}也是默認(rèn)的映射策略和模式,通過上述的代碼可以看到以下輸出:

可以看到, 通過以上的代碼已經(jīng)成功的實(shí)現(xiàn)把HashMap轉(zhuǎn)換成JavaBean,同時(shí)可以看到JavaBean的屬性值都成功的進(jìn)行了賦值。到此,大功告成。
三、總結(jié)
以上就是本文的主要內(nèi)容,本文以Java語言為例,主要講解如何使用Java語言進(jìn)行空間數(shù)據(jù)的讀取,空間屬性信息的讀取使用GeoTools,文章首先介紹最原始的做法,即在對(duì)象中自定義轉(zhuǎn)換方法來實(shí)現(xiàn)轉(zhuǎn)換,然后詳細(xì)介紹一種基于ModelMapper的空間屬性映射實(shí)現(xiàn)方法。ModelMapper在Java的其它領(lǐng)域應(yīng)用很多,但是在GIS領(lǐng)域中使用的還不多。本文基于ModelMapper解決了在Shapefile文件讀取過程中,如何實(shí)現(xiàn)動(dòng)態(tài)的將屬性表格映射到指定對(duì)象的方法進(jìn)行了詳細(xì)介紹。如果您是一名GISER,同時(shí)也面臨著屬性信息映射的問題,不妨來這里交流討論。行文倉促,難免有許多不足之處,如有不足,還請(qǐng)各位專家朋友在評(píng)論區(qū)留言批評(píng)指正,不甚感激。
到此這篇關(guān)于在Java中使用ModelMapper簡化Shapefile屬性轉(zhuǎn)JavaBean實(shí)戰(zhàn)的文章就介紹到這了,更多相關(guān)Java轉(zhuǎn)JavaBean內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring data elasticsearch使用方法詳解
這篇文章主要介紹了Spring data elasticsearch使用方法詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-01-01
Spring MVC結(jié)合Spring Data JPA實(shí)現(xiàn)按條件查詢和分頁
這篇文章主要為大家詳細(xì)介紹了Spring MVC結(jié)合Spring Data JPA實(shí)現(xiàn)按條件查詢,以及分頁效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10
Assert.assertEquals的使用方法及注意事項(xiàng)說明
這篇文章主要介紹了Assert.assertEquals的使用方法及注意事項(xiàng)說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05
Java如何處理json字符串value多余雙引號(hào)
這篇文章主要介紹了Java如何處理json字符串value多余雙引號(hào),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03
如何使用spring?boot的程序主線程中異步訪問外部接口
CompletableFuture.supplyAsync提供了一種強(qiáng)大的工具,使您能夠以異步方式執(zhí)行操作,充分利用多核處理器和提高程序性能,同時(shí)保持代碼的清晰性和可維護(hù)性,本文給大家介紹使用spring?boot的程序主線程中異步訪問外部接口,感興趣的朋友一起看看吧2023-10-10
java之CSV大批量數(shù)據(jù)入庫的實(shí)現(xiàn)
本文主要介紹了java之CSV大批量數(shù)據(jù)入庫的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02
Java及數(shù)據(jù)庫對(duì)日期進(jìn)行格式化方式
這篇文章主要介紹了Java及數(shù)據(jù)庫對(duì)日期進(jìn)行格式化方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
Java Collection集合遍歷運(yùn)行代碼實(shí)例
這篇文章主要介紹了Java Collection集合遍歷運(yùn)行代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04

