Использование JPA Criteria Api и гибернации пространственного 4 вместе
Приведенный пример запроса здесь: http://www.hibernatespatial.org/tutorial-hs4.html
Query query = em.createQuery("select e from Event e where within(e.location, :filter) = true", Event.class);
query.setParameter("filter", filter);
Можно ли переписать запрос, используя jpa 2 критерий API?(Я не уверен, как мне поступить с within(e.location, :filter)
часть.
2 ответа
Я недавно работаю над точно такой же проблемой. Мое решение - это собственный предикат для ключевого слова.
public class WithinPredicate extends AbstractSimplePredicate implements Serializable {
private final Expression<Point> matchExpression;
private final Expression<Geometry> area;
public WithinPredicate(CriteriaBuilderImpl criteriaBuilder, Expression<Point> matchExpression, Geometry area) {
this(criteriaBuilder, matchExpression, new LiteralExpression<Geometry>(criteriaBuilder, area));
}
public WithinPredicate(CriteriaBuilderImpl criteriaBuilder, Expression<Point> matchExpression, Expression<Geometry> area) {
super(criteriaBuilder);
this.matchExpression = matchExpression;
this.area = area;
}
public Expression<Point> getMatchExpression() {
return matchExpression;
}
public Expression<Geometry> getArea() {
return area;
}
public void registerParameters(ParameterRegistry registry) {
// Nothing to register
}
@Override
public String render(boolean isNegated, RenderingContext renderingContext) {
StringBuilder buffer = new StringBuilder();
buffer.append(" within(")
.append(((Renderable) getMatchExpression()).render(renderingContext))
.append(", ")
.append(((Renderable) getArea()).render(renderingContext))
.append(") = true ");
return buffer.toString();
}
}
Ваш запрос будет выглядеть так:
public List<Event> findEventInArea(Geometry area){
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Event> c = cb.createQuery(Event.class);
Root<Event> event = c.from(Event.class);
c.where(new WithinPredicate((CriteriaBuilderImpl) cb, event.get(Event_.location), area));
Query query = entityManager.createQuery(c);
return query.getResultList();
}
JPA не поддерживает пространственную. Однако вы можете развернуть сеанс гибернации из вашего JPA EntityManager и запустить пространственные критерии.
Широкие границы в этом примере кода являются произвольными.
@PersistenceContext(unitName = "myPuName")
private EntityManager entityManager;
@Override
public List<City> findCities() {
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
Session session = entityManager.unwrap(Session.class);
Criteria criteria = session.createCriteria(City.class);
GeometryFactory geometryFactory = new GeometryFactory();
Coordinate[] coordinates = {new Coordinate(-9,-9,0),new Coordinate(-9,9,0),new Coordinate(9,9,0),new Coordinate(9,-9,0),new Coordinate(-9,-9,0)};
LinearRing polygon = geometryFactory.createLinearRing(coordinates);
Polygon po = geometryFactory.createPolygon(polygon,null);
criteria.add(SpatialRestrictions.within(City_.location.getName(), po));
List list = criteria.list();
return list;
}
Вот еще немного кода, не имеющего прямого отношения к вопросу. Этот класс можно использовать в качестве критерия "Порядок" для добавления в критерии гибернации. Результаты будут отсортированы по расстоянию от местоположения аргумента:
public class KnnOrder extends Order {
private final Point fromPoint;
public KnnOrder(String propertyName, boolean ascending, Point fromPoint) {
super(propertyName, ascending);
this.fromPoint = fromPoint;
}
@Override
public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) {
Dialect dialect = criteriaQuery.getFactory().getDialect();
if (!dialect.getClass().isAssignableFrom(PostgisDialect.class)) {
throw new UnsupportedOperationException("This supports only postgis dialect. Was requested: " + dialect.toString());
}
// final String[] columns = criteriaQuery.getColumnsUsingProjection(criteria, super.getPropertyName());
// String fromPointWkt = WKTWriter.toPoint(fromPoint.getCoordinate());
return "location <-> st_setsrid(st_makepoint(" + fromPoint.getX() + "," + fromPoint.getY() + "),4326)";
}
}
В JPA2 вы можете использовать построитель функциональных выражений. Больше никаких специальных вещей не требуется. Также работает для выражения заказа.
public List<Event> listThem(Geometry area) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Event> cq = cb.createQuery(Event.class);
Root<Event> root = cq.from(Event.class);
ParameterExpression<Geometry> circleParm = cb.parameter(Geometry.class);
cq.where(cb.isTrue(cb.function("st_within", Boolean.class,
root.get(Event_.location), circleParm)));
TypedQuery<Event> tq = em.createQuery(cq);
tq.setParameter(circleParm, area);
return tq.getResultList();
}
Небольшая цена: название функции зависит от базы данных. В PostgreSQL внутренняя функция называется st_within.