Рубрики
Без рубрики

Введение в спящий режим Пространственный

Быстрое и практичное введение в режим гибернации.

Автор оригинала: baeldung.

1. введение

В этой статье мы рассмотрим пространственное расширение Hibernate, hibernate-spatial .

Начиная с версии 5, Hibernate Spatial предоставляет стандартный интерфейс для работы с географическими данными .

2. Фон в режиме гибернации

Географические данные включают представление таких объектов, как Точка, линия, полигон . Такие типы данных не являются частью спецификации JDBC, поэтому JTS (JTS Topology Suite) стал стандартом для представления пространственных типов данных.

Помимо JTS, Hibernate spatial также поддерживает Geolatte-geom – недавнюю библиотеку, которая имеет некоторые функции, недоступные в JTS.

Обе библиотеки уже включены в проект hibernate-spatial. Использование одной библиотеки над другой-это просто вопрос того, из какой jar мы импортируем типы данных.

Хотя Hibernate spatial поддерживает различные базы данных, такие как Oracle, MySQL, Postgresql/PostGIS и некоторые другие, поддержка конкретных функций базы данных неоднородна.

Лучше обратиться к последней документации по Hibernate, чтобы проверить список функций, для которых hibernate предоставляет поддержку для данной базы данных.

В этой статье мы будем использовать в памяти Mariadb4j -который поддерживает полную функциональность MySQL.

Конфигурация для Mariadb и MySQL аналогична, даже библиотека mysql-connector работает для обеих этих баз данных.

3. Зависимости Maven

Давайте посмотрим на зависимости Maven, необходимые для настройки простого пространственного проекта hibernate:


    org.hibernate
    hibernate-core
    5.2.12.Final


    org.hibernate
    hibernate-spatial
    5.2.12.Final


    mysql
    mysql-connector-java
    6.0.6


    ch.vorburger.mariaDB4j
    mariaDB4j
    2.2.3

Зависимость hibernate-spatial обеспечивает поддержку типов пространственных данных. Последние версии hibernate-core , hibernate-spatial , mysql-connector-java и MariaDB4j можно получить в Maven Central.

4. Настройка пространственного режима гибернации

Первым шагом является создание файла hibernate.properties в каталоге resources :

hibernate.dialect=org.hibernate.spatial.dialect.mysql.MySQL56SpatialDialect
// ...

Единственное, что характерно для hibernate-spatial, – это пространственный диалект MySQL56 диалект . Этот диалект расширяет диалект MySQL55 и предоставляет дополнительные функции, связанные с типами пространственных данных.

Код, специфичный для загрузки файла свойств , создания SessionFactory и создания экземпляра Mariadb4j, такой же, как и в стандартном проекте hibernate.

5. Понимание типа геометрии

Геометрия является базовым типом для всех пространственных типов в JTS. Это означает, что другие типы , такие как Point , Polygon и другие, простираются от Geometry . Тип Geometry в java также соответствует типу GEOMETRY в MySQL.

Анализируя представление типа String , мы получаем экземпляр Geometry . Служебный класс WKTReader , предоставляемый JTS, может использоваться для преобразования любого хорошо известного текста представления в Геометрию тип:

public Geometry wktToGeometry(String wellKnownText) 
  throws ParseException {
 
    return new WKTReader().read(wellKnownText);
}

Теперь давайте посмотрим на этот метод в действии:

@Test
public void shouldConvertWktToGeometry() {
    Geometry geometry = wktToGeometry("POINT (2 5)");
 
    assertEquals("Point", geometry.getGeometryType());
    assertTrue(geometry instanceof Point);
}

Как мы видим, даже если возвращаемый тип метода read() метод Geometry , фактическим экземпляром является экземпляр Point .

6. Сохранение точки в БД

Теперь , когда у нас есть хорошее представление о том, что такое Геометрия тип и как получить Точку из Строки , давайте посмотрим на точечность :

@Entity
public class PointEntity {

    @Id
    @GeneratedValue
    private Long id;

    private Point point;

    // standard getters and setters
}

Обратите внимание, что сущность Точечная сущность содержит пространственный тип Точка . Как было показано ранее, точка | представлена двумя координатами:

public void insertPoint(String point) {
    PointEntity entity = new PointEntity();
    entity.setPoint((Point) wktToGeometry(point));
    session.persist(entity);
}

Метод insert Point() принимает хорошо известное текстовое (WKT) представление Point , преобразует его в экземпляр Point и сохраняет в БД.

Напомним, что session не является специфичным для hibernate-spatial и создается аналогично другому проекту hibernate.

Здесь мы можем заметить, что после создания экземпляра Point процесс хранения Point Entity аналогичен любому обычному объекту.

Давайте рассмотрим некоторые тесты:

@Test
public void shouldInsertAndSelectPoints() {
    PointEntity entity = new PointEntity();
    entity.setPoint((Point) wktToGeometry("POINT (1 1)"));

    session.persist(entity);
    PointEntity fromDb = session
      .find(PointEntity.class, entity.getId());
 
    assertEquals("POINT (1 1)", fromDb.getPoint().toString());
    assertTrue(geometry instanceof Point);
}

Вызов toString() на точке возвращает представление WKT Точки . Это связано с тем, что класс Geometry переопределяет метод toString() и внутренне использует WKTWriter, дополнительный класс к WKTReader , который мы видели ранее.

Как только мы запустим этот тест, hibernate создаст для нас Точечную сущность таблицу.

Давайте взглянем на этот стол:

desc PointEntity;
Field    Type          Null    Key
id       bigint(20)    NO      PRI
point    geometry      YES

Как и ожидалось, Тип поля |/Точки является ГЕОМЕТРИЕЙ . Из-за этого при извлечении данных с помощью нашего редактора SQL (например, MySQL workbench) нам необходимо преобразовать этот тип ГЕОМЕТРИИ в удобочитаемый текст:

select id, astext(point) from PointEntity;

id      astext(point)
1       POINT(2 4)

Однако, поскольку hibernate уже возвращает представление WKT при вызове метода toString() в Geometry или любом из его подклассов, нам не нужно беспокоиться об этом преобразовании.

7. Использование Пространственных Функций

7.1. Пример ST_WITHIN()

Теперь мы рассмотрим использование функций базы данных, которые работают с пространственными типами данных.

Одной из таких функций в MySQL является ST_WITHIN () , которая сообщает, находится ли одна Геометрия внутри другой. Хорошим примером здесь было бы выяснить все точки в пределах заданного радиуса.

Давайте начнем с того, как создать круг:

public Geometry createCircle(double x, double y, double radius) {
    GeometricShapeFactory shapeFactory = new GeometricShapeFactory();
    shapeFactory.setNumPoints(32);
    shapeFactory.setCentre(new Coordinate(x, y));
    shapeFactory.setSize(radius * 2);
    return shapeFactory.createCircle();
}

Окружность представлена конечным набором точек, заданных методом Setnump () . radius удваивается перед вызовом метода setSize () , так как нам нужно нарисовать круг вокруг центра в обоих направлениях.

Давайте теперь двинемся вперед и посмотрим, как получить точки в пределах заданного радиуса:

@Test
public void shouldSelectAllPointsWithinRadius() throws ParseException {
    insertPoint("POINT (1 1)");
    insertPoint("POINT (1 2)");
    insertPoint("POINT (3 4)");
    insertPoint("POINT (5 6)");

    Query query = session.createQuery("select p from PointEntity p where 
      within(p.point, :circle) = true", PointEntity.class);
    query.setParameter("circle", createCircle(0.0, 0.0, 5));

    assertThat(query.getResultList().stream()
      .map(p -> ((PointEntity) p).getPoint().toString()))
      .containsOnly("POINT (1 1)", "POINT (1 2)");
    }

Hibernate сопоставляет свою функцию within() с функцией ST_WITHIN() MySQL.

Интересным наблюдением здесь является то, что точка (3, 4) падает точно на окружность. Тем не менее, запрос не возвращает эту точку. Это происходит потому, что функция within() возвращает true только в том случае, если данная Геометрия полностью находится в другой Геометрии .

7.2. Пример ST_TOUCHES()

Здесь мы приведем пример, который вставляет набор Polygon s в базу данных и выбирает Polygon s, которые примыкают к заданному Polygon . Давайте быстро взглянем на объект Polygon класс:

@Entity
public class PolygonEntity {

    @Id
    @GeneratedValue
    private Long id;

    private Polygon polygon;

    // standard getters and setters
}

Единственное, что здесь отличается от предыдущей сущности Point , это то, что мы используем тип Polygon вместо Point .

Теперь давайте перейдем к тесту:

@Test
public void shouldSelectAdjacentPolygons() throws ParseException {
    insertPolygon("POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0))");
    insertPolygon("POLYGON ((3 0, 3 5, 8 5, 8 0, 3 0))");
    insertPolygon("POLYGON ((2 2, 3 1, 2 5, 4 3, 3 3, 2 2))");

    Query query = session.createQuery("select p from PolygonEntity p 
      where touches(p.polygon, :polygon) = true", PolygonEntity.class);
    query.setParameter("polygon", wktToGeometry("POLYGON ((5 5, 5 10, 10 10, 10 5, 5 5))"));
    assertThat(query.getResultList().stream()
      .map(p -> ((PolygonEntity) p).getPolygon().toString())).containsOnly(
      "POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0))", "POLYGON ((3 0, 3 5, 8 5, 8 0, 3 0))");
}

Метод insert Polygon() аналогичен методу insert Point () , который мы видели ранее. Источник содержит полную реализацию этого метода.

Мы используем функцию touch() , чтобы найти Полигон s, прилегающий к заданному Полигону . Очевидно, что третий Полигон не возвращается в результате, так как нет ребра, соприкасающегося с данным полигоном .

8. Заключение

В этой статье мы видели, что hibernate-spatial значительно упрощает работу с пространственными типами данных, поскольку он заботится о деталях низкого уровня.

Несмотря на то, что в этой статье используется Mariadb4j, мы можем заменить его MySQL без изменения какой-либо конфигурации.

Как всегда, полный исходный код этой статьи можно найти на GitHub .