jpa 也能直接写原生sql,原生sql,直接写postgis的函数,不多说
@Query(value = "select t from DemoPointDO t where st_contains(:polygon, t.point) is true", nativeQuery = true)
List<DemoPointDO> containsQuery1(@Param("polygon") Polygon polygon);
现在说两种不写原生sql去调用postgis的函数,这里以一个空间返回查询为例,查询该矩形里面的所有点。
一个最简单的表,id + 名称 + 空间点位
create table demo_point (
point_id varchar(36) not null primary key,
point_name varchar(32) not null,
location geometry
);
spring cloud、alibaba cloud版本和Spring boot版本如下
<spring-cloud.version>2021.0.5</spring-cloud.version>
<spring-boot.version>2.7.6</spring-boot.version>
<alibaba-cloud.version>2021.0.5.0</alibaba-cloud.version>
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.lutuo.iot</groupId>
<artifactId>ltb-iot-equipment</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>demo-spatial</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-spatial</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.lutuo.jpa.plugin</groupId>
<artifactId>lutuo-jpa-plugin</artifactId>
</dependency>
<dependency>
<groupId>com.graphhopper.external</groupId>
<artifactId>jackson-datatype-jts</artifactId>
<version>2.14</version>
</dependency>
</dependencies>
</project>
实体类定义,这里这个Point是JTS里面的那个
@Data
@Entity
@Table(name = "demo_point")
public class DemoPointDO {
/** uuid-36 */
@Id
@GenericGenerator(name = "uuid", strategy = "com.lutuo.jpa.plugin.config.CustomerUuidGenerator")
@GeneratedValue(generator = "uuid")
@Column(length = 36)
private String pointId;
@Column(length = 32)
private String pointName;
/**
* 注意这里:columnDefinition = "geometry"
* 这里指定了jackson的序列化和反序列化器
*/
@Column(name = "location", columnDefinition = "geometry")
@JsonDeserialize(using = GeometryDeserializer.class)
@JsonSerialize(using = GeometrySerializer.class)
private Point point;
}
repository接口
public interface DemoPointRepository extends JpaRepository<DemoPointDO, String>, JpaSpecificationExecutor<DemoPointDO> {
/** 空间返回查询方式一 */
@Query("select t from DemoPointDO t where st_contains(:polygon, t.point) is true")
List<DemoPointDO> containsQuery(@Param("polygon") Polygon polygon);
@Query(value = "select t from demo_point t where st_contains(:polygon, t.location) is true", nativeQuery = true)
List<DemoPointDO> containsQuery1(@Param("polygon") Polygon polygon);
}
单元测试,demo里面没有加回滚
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = SpatialApplication.class)
public class DemoPointRepositoryTest {
private DemoPointRepository demoPointRepository;
@Test
public void findAllTest() {
List<DemoPointDO> list = demoPointRepository.findAll();
assertNotNull("查询结果为空", list);
}
/** 保存一条数据 */
@Test
public void saveTest() {
DemoPointDO pointDO = new DemoPointDO();
pointDO.setPointName("点位2");
GeometryFactory geometryFactory = new GeometryFactory();
Point point = geometryFactory.createPoint(new Coordinate(130.40180135416841035156, 33.015156103111531));
pointDO.setPoint(point);
DemoPointDO save = demoPointRepository.save(pointDO);
assertNotNull("保存结果为空", save);
}
/**
* 方式一:repositor直接定义方法,并写@Query(),注意:st_contains返回值本来是bool,理论上可以不加is true,但是这里不加会出现语法错误。
* 原生sql,这里返回的是true:SELECT st_contains(
ST_GeomFromText('POLYGON((-180 90, 180 90, 180 -90, -180 -90, -180 90))'),
ST_GeomFromText('POINT(132.3416515 32.156135)')
);
*/
@Test
public void containsQueryTest() throws Exception {
GeometryFactory geometryFactory = new GeometryFactory();
Coordinate[] coordinates = new Coordinate[5];
// -180 90, 180 90, 180 -90, -180 -90, -180 90
coordinates[0] = new Coordinate(-180, 90);
coordinates[1] = new Coordinate(180, 90);
coordinates[2] = new Coordinate(180, -90);
coordinates[3] = new Coordinate(-180, -90);
coordinates[4] = new Coordinate(-180, 90);
Polygon polygon = geometryFactory.createPolygon(coordinates);
List<DemoPointDO> list = demoPointRepository.containsQuery(polygon);
assertNotNull("查询结果为空", list);
ObjectMapper objectMapper = new ObjectMapper();
for (DemoPointDO pointDO : list) {
System.out.println(objectMapper.writeValueAsString(pointDO));
}
}
/**
* 方式二:repository继承JpaSpecificationExecutor,调用List<T> findAll(@Nullable Specification<T> spec);
* 这里的重点是如何构建Specification对象,这里用于动态构建sql的情况
*/
@Test
public void findAll1Test() throws JsonProcessingException {
GeometryFactory geometryFactory = new GeometryFactory();
Coordinate[] coordinates = new Coordinate[5];
// -180 90, 180 90, 180 -90, -180 -90, -180 90
coordinates[0] = new Coordinate(-180, 90);
coordinates[1] = new Coordinate(180, 90);
coordinates[2] = new Coordinate(180, -90);
coordinates[3] = new Coordinate(-180, -90);
coordinates[4] = new Coordinate(-180, 90);
Polygon polygon = geometryFactory.createPolygon(coordinates);
Specification<DemoPointDO> specification = (root, query, criteriaBuilder) -> {
return criteriaBuilder.isTrue(criteriaBuilder.function("st_contains", Boolean.class, criteriaBuilder.literal(polygon), root.get("point")));
};
List<DemoPointDO> list = demoPointRepository.findAll(specification);
assertNotNull("查询结果为空", list);
ObjectMapper objectMapper = new ObjectMapper();
for (DemoPointDO pointDO : list) {
System.out.println(objectMapper.writeValueAsString(pointDO));
}
}
@Autowired
public void setDemoPointRepository(DemoPointRepository demoPointRepository) {
this.demoPointRepository = demoPointRepository;
}
}