需求是这样的:需要根据用户的位置信息(经纬度)筛选方圆3km之内的数据
但是由于架构使用hibernate,加之需要在原来的搜索条件上补充添加,所以不方便直接使用原生SQL来写
需要在Specification接口下的toPredicate方法中拼接条件,而且复杂的是,需要使用到MYSQL提供的st_distance_sphere函数,但是st_distance_sphere的参数是两个point函数(函数套函数)
本文也主要说明在Specification中如何使用MYSQL的函数:
首先hibernate提供了一系列的function,可以参考程序包org.hibernate.query.criteria.internal.expression.function
但是没有我需要的function,所以我需要自己定义如下的PointFunction,大意就是通过重写 BasicFunctionExpression的方法实现一个拼接SQL字符串的目的,代码如下:
import javax.persistence.criteria.Expression;
import java.io.Serializable;
/**
* 用于JPA链接MYSQL的funtion
* author: wangyanlong
* date: 2023/03/20
*/
public class PointFunction extends BasicFunctionExpression<String> implements Serializable {
public static final String NAME = "point";
private final Expression<Double> longitude;
private final Expression<Double> latgitude;
public PointFunction(CriteriaBuilderImpl criteriaBuilder, Expression<Double> longitude, Expression<Double> latgitude) {
super(criteriaBuilder, String.class, "point");
this.longitude = longitude;
this.latgitude = latgitude;
}
public PointFunction(CriteriaBuilderImpl criteriaBuilder, Double longitude, Double latgitude) {
this(criteriaBuilder, new LiteralExpression<>(criteriaBuilder, longitude), new LiteralExpression<>(criteriaBuilder, latgitude));
}
......
public String render(RenderingContext renderingContext) {
renderingContext.getFunctionStack().push(this);
String var3;
try {
StringBuilder buffer = new StringBuilder();
buffer.append("point(").append(((Renderable) this.getLongitude()).render(renderingContext)).append(',').append(((Renderable) this.getLatgitude()).render(renderingContext));
var3 = buffer.append(')').toString();
} finally {
renderingContext.getFunctionStack().pop();
}
return var3;
}
}
其中最重要的是render方法,即返回需要拼接的字符串
接下来使用pointfunction:
criteriaBuilder.function("st_distance_sphere", Double.class,
new PointFunction(
(CriteriaBuilderImpl) criteriaBuilder,
Double.valueOf(mQueryCondition.getLon()),
Double.valueOf(mQueryCondition.getLat())),
new PointFunction(
(CriteriaBuilderImpl) criteriaBuilder,
root.get("longitude"),
root.get("latitude"))
)
到此即可使用st_distance_sphere与point函数实现不同经纬度之间的计算了
但是可以看到的是在上面的代码中criteriaBuilder提供了.function的API,其实可以直接使用.function如法炮制point函数,而不用自己构造一个类了,具体.function API如何使用,有需要的自己去查吧