Spring Data JPA
的极度简便的使用方式让我们爱不释手,但是我们在项目中经常会有使用空间数据的场景,而不同的数据库对空间数据实现的方式不同,而这些不仅是JPA
或者Spring Data JPA
都是不支持的这时我们需要引入hibernate-spatial
来去除数据库支持的异构性。
在本例中集成了Spring Data JPA
、hibernate -spatial
、PostGIS
一起的使用方式。
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-spatial</artifactId>
<version>5.2.10.Final</version>
</dependency>
spring.jpa.database-platform: org.hibernate.spatial.dialect.postgis.PostgisPG9Dialect
org.hibernate.spatial.dialect
下还有h2
,mysql
,oracle
,sqlserver
的方言。
@Column(columnDefinition = "geometry(Point,4326)")
private Point point;
线的映射使用:
@Column(columnDefinition = "geometry(LineString,4326)")
private LineString line;
public interface CityRepository extends JpaRepository<City,Long> {
@Query("select city from City as city where equals(city.point,:point) = TRUE")
List<City> findByPoint(@Param("point") Point point);
}
@Bean
CommandLineRunner geometrySave(CityRepository cityRepository){
return e ->{
City city = new City();
city.setName("合肥");
Geometry point = wktReader().read("POINT (117.2 31.8)");
Point pointToSave = point.getInteriorPoint();
pointToSave.setSRID(4326);
city.setPoint(pointToSave);
cityRepository.save(city);
};
}
@Bean
CommandLineRunner geometryRead(CityRepository cityRepository){
return e -> {
City city = cityRepository.findOne(3l);
Point point = city.getPoint();
log.info("经度:" + point.getX() + " 维度:" + point.getY() + " 坐标系统:" + point.getSRID());
};
}
@Bean
CommandLineRunner geometryQuery(CityRepository cityRepository){
return e -> {
Geometry point = wktReader().read("POINT (117.2 31.8)");
Point pointToQuery = point.getInteriorPoint();
pointToQuery.setSRID(4326);
List<City> cities = cityRepository.findByPoint(pointToQuery);
for (City city : cities) {
log.info("查询结果为:" + city.getId() + "/" +city.getName() + "/" +city.getPoint());
}
};
}
我们在cityRepository
中使用了hibernate-spatial
中的空间函数equals
,具体空间函数列表请查看
http://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#spatial-configuration-dialect
https://github.com/wiselyman/jpa-postgis
在上一篇文章利用hibernate-spatial让Spring Data JPA支持空间数据,我们使用hibernate spatial
,spring data jpa
成功支持空间字段的映射与增删查改。但是我们目前面临的问题是客户端传过来的json数据如何转换为Geometry(Point、LineString),后台的Geometry如何直接转换为JSON。在GIS的世界里有一个标准的GIS JSON格式叫做geojson
。在本文将使用geojson
格式与Geometry对象互相转换。
这里我们的思路是Spring Boot为我们自动注册了MappingJackson2HttpMessageConverter
,在org.springframework.boot.autoconfigure.web.JacksonHttpMessageConvertersConfiguration
如:
@Bean
@ConditionalOnMissingBean(value = MappingJackson2HttpMessageConverter.class, ignoredType = {
"org.springframework.hateoas.mvc.TypeConstrainedMappingJackson2HttpMessageConverter",
"org.springframework.data.rest.webmvc.alps.AlpsJsonHttpMessageConverter" })
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(
ObjectMapper objectMapper) {
return new MappingJackson2HttpMessageConverter(objectMapper);
}
据此看出Spring Boot是使用当前的Message Converter来实现对象(Geometry)和json之间转换的,我们只需要自定义objectMapper让其支持geojson即可。
添加第三方的依赖:
<dependency>
<groupId>com.bedatadriven</groupId>
<artifactId>jackson-datatype-jts</artifactId>
<version>2.4</version>
</dependency>
<repositories>
<repository>
<id>sonatype-oss</id>
<url>https://oss.sonatype.org/content/groups/public</url>
</repository>
</repositories>
自定义object让其支持Geometry与geojson之间的准换:
@Bean
public ObjectMapper objectMapper(){
ObjectMapper objectMapper = new ObjectMapper();
objectMapper().registerModule(new JtsModule());
return objectMapper;
}
这个第三方依赖目前有个bug就是不支持空间坐标系,在一个单一的系统里一般情况下坐标系应该也是一定的,所以不支持问题也不大,所以我们将前面例子的字段映射修改为,:
//@Column(columnDefinition = "geometry(Point,4326)")
@Column(columnDefinition = "geometry(Point)")
private Point point;
测试控制器:
@RestController
@Slf4j
@RequestMapping("/cities")
public class CityController {
@Autowired
CityRepository cityRepository;
@PostMapping
public City testIn(@RequestBody City city){
log.info(city.getPoint().getSRID()
+ "/" + city.getPoint().getX()
+ "/" +city.getPoint().getY());
return cityRepository.save(city);
}
}
测试数据,使用request body向后台POST
如下结果:
{
"name": "南京",
"point": {
"type": "Point",
"coordinates": [
110.4,
20.1
]
}
}
返回值为:
{
"id": 58,
"name": "南京",
"point": {
"type": "Point",
"coordinates": [
110.4,
20.1
]
}
}
这时我们实现了自动的Geometry和geojson数据的转换。