hibernate-spatial на oracle12c — объекты sdo_geometry становятся пустыми при em.merge

есть таблица, содержащая столбец типа SDO_GEOMETRY, представляющий несколько точек. Позже должен быть выполнен пространственный запрос типа «показать все остальные точки на расстоянии x». После загрузки некоторых данных через sqlloader в таблицу импорта я собираюсь перенести эти наборы данных в базовую таблицу при объединении, если бизнес-ключ все еще существует.

Мое приложение представляет собой исполняемый файл jar, использующий базу данных hibernate 5.0.9, hibernate-spatial 5.0.9 final, Oracle12c, а hibernate.dialect был

org.hibernate.dialect.Oracle12cDialect

раньше, но теперь изменился на

org.hibernate.spatial.dialect.oracle.OracleSpatial10gDialect

Я знаю, что официально существует только пространственный диалект для Oracle10, а также тест с oracle11, но с oracle12.

Теперь проблема в том, что объекты сохраняются через спящий режим в базовую таблицу, но внутри SDO-Geometry все координаты равны нулю (в то время как объект sdo_geometry - нет).

select
 businessKey,
 my_sdo_geometry.sdo_point.x longitude,
 my_sdo_geometry.sdo_point.y latitude
from my_import_table;

наборы данных из базовой таблицы с нулевыми координатами

После sqlloader все координаты в таблице импорта отображаются хорошо.

наборы данных из таблицы импорта с правильно сформированными координатами

Но после обработки каждого набора данных импорта его столбец «импортированный» {Y, N} устанавливается в «Y». И для этого я использую myEntityManager.merge(importDataset). Из-за этого (хотя все координаты были в порядке после sqlloader) координаты набора данных импорта очищаются до нуля.

//coordinates still viewable in database    
importObject.setImported(Boolean.TRUE);
em.merge(importObject);
//coordinates null now

наборы данных из таблицы импорта с нулевыми координатами

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

Я пишу здесь с надеждой, что есть кто-нибудь, кто успешно использует hibernate-spatial с oracle12 (чтобы уточнить, что это вообще возможно) и может получить некоторую помощь с любой плохой конфигурацией на моей стороне.

Постоянство.xml моего приложения:

<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
    http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">

    <persistence-unit name="myUnit" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>

        <properties>
            <property name="hibernate.ejb.cfgfile" value="/hibernate.cfg.xml" />
            <property name="use_sql_comments" value="true" />
            <property name="hibernate.show_sql" value="false" />
            <property name="hibernate.format_sql" value="false" />
            <property name="hibernate.connection.driver_class" value="oracle.jdbc.driver.OracleDriver" />
            <property name="hibernate.connection.url" value="jdbc:oracle:thin:@123.456.789.1:1234/cm" />
            <property name="hibernate.connection.username" value="testMe" />
            <property name="hibernate.connection.password" value="********" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.Oracle12cDialect" />
        </properties>
    </persistence-unit> </persistence>

Заранее спасибо.

изменить: в классе сущностей столбец sdo_geometry (здесь называемый «Позиция») геттер аннотируется следующим образом:

@Column(name = "POSITION", nullable = false)

public Point getPosition() {
    return this.position;
}

изменить 2: после включения трассировки спящего режима sql я обнаружил, что спящий режим пытается это обновление:

Hibernate: update IMPORT_TABLE set HB_VERSION=?, BUSINESS_KEY=?, IMPORTED=?, [...], POSITION=? where IMPORT_TABLE_ID=? and HB_VERSION=?

Я указываю на POSITION=?, что не может быть правильным, я считаю. В этой позиции я ожидаю, что спящий диалект будет иметь место. Еще один намек на несовместимость с спящим диалектом, пространственным10 и оракулом12.


person Kaspatoo    schedule 20.03.2018    source источник


Ответы (2)


Hibernate Spatial по умолчанию сохраняет координаты в массиве SDO_Ordinates, даже для точек. Поле SDO_Geometry.SDO_Point не используется, поэтому всегда будет нулевым. В SQL вы можете использовать функцию SDO_UTIL.GetVertices() для доступа к данным координат.

В версии geolatte-geom 1.1 или выше вы должны иметь возможность установить системное свойство Java GEOLATTE_USE_SDO_POINT_TYPE. Если установлено это свойство, геометрия точек будет храниться в поле SDO_Geometry.SDO_Point. Если ваша версия Hibernate использует более старую версию Geolatte-geom, вы можете добавить более новую версию в свой путь к классам, чтобы использовать эту функцию.

Кстати. Hibernate Spatial работает путем кодирования геометрии Java в значения, которые можно установить в подготовленном операторе, поэтому «... Position=?» часть в сгенерированном SQL в порядке.

person Karel Maesen    schedule 23.03.2018
comment
вроде работает как вы говорите. Результаты соответствуют ожиданиям. Спасибо. - person Kaspatoo; 24.05.2018

Я нашел обходной путь, который не отвечает на мой вопрос, как я намеревался, но пока это обходной путь.

Вместо использования спящих объектов и оператора слияния он работает, если вместо этого используется пользовательский запрос, подобный этому:

Query q = em.createQuery("INSERT INTO base_table"
            + "(techId, myBuildingFK, [...], my_sdo_geometry) "
            + "SELECT l.impTechId, b, [...], l.my_sdo_geometry"
            + "FROM import_table l, Buildiung b "
            + "WHERE l.businessKey = g.businessKey ");
q.executeUpdate();

С помощью этого HQL-запроса я получаю, что координаты не очищаются. Из-за того, что он все еще является запросом HQL, возможно, что-то из пространственного диалекта все еще используется, но, похоже, он не работает должным образом с командой слияния диспетчера сущностей. Может быть, мне не хватает какой-то аннотации на объекте, чтобы заставить эту работу работать, возможно, она несовместима с Oracle12.

Я все еще надеюсь на другие и, возможно, лучшие ответы, чем мои.

редактировать: поскольку мне нужно поведение слияния, я подумал о том, чтобы просто объединить объект через диспетчер сущностей, а затем выполнить запрос на обновление sql или hql для сохранения sdo_geometry, но поскольку транзакция не завершена, нет набора данных для обновления.

//        //not working
//        em.merge(mergeObject);

//        String updateQueryString =
//                "update BaseTable set sdo_geometry = :sdoGeometry" + " where baseTableId = :baseTableId";
//        Query updateQuery = em.createQuery(updateQueryString);
//        updateQuery.setParameter("sdoGeometry", mergeObject.getSdoGeometry());
//        updateQuery.setParameter("baseTableId", mergeObject.getBaseTableId());
//        int cnt = updateQuery.executeUpdate();

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

String mergeQueryString = "MERGE INTO Base_Table gl "
    + "USING (SELECT * FROM Import_Table WHERE import_table_id = :importTableId) igl "
    + "ON (gl.base_table_id = :baseTableId) "
    + "WHEN MATCHED THEN UPDATE SET gl.my_sdo_geometry = igl.my_sdo_geometry "
    + "WHEN NOT MATCHED THEN INSERT (gl.baseTableId, gl.anyFkId, gl.my_sdo_geometry) "
    + "VALUES (id_gen_function, :anyFkId, igl.my_sdo_geometry)";
Query mergeQuery = em.createNativeQuery(mergeQueryString);
mergeQuery.setParameter("importTableId", importObject.getImportTableId());
mergeQuery.setParameter("baseTableId", refModelObject.getBaseTableId());
mergeQuery.setParameter("anyFkId", refModelObject.getAnyFkObject().getAnyFkId());
int cnt = mergeQuery.executeUpdate();
person Kaspatoo    schedule 20.03.2018