oracle 12c 中的休眠空间 - sdo_geometry 对象在 em.merge 上变为空

hibernate-spatial on oracle12c - sdo_geometry objects become empty on em.merge

有一个 table 包含代表一些点的 SDO_GEOMETRY 类型的列。稍后将执行像 'show all other points within distance x' 这样的空间查询。 通过 sqlloader 将一些数据泵入 import-table 之后,如果业务键仍然存在,我将把这些数据集带入 base-table 并合并。

我的应用程序是一个使用 hibernate 5.0.9hibernate-spatial 5.0.9 final、Oracle12c 数据库的 executable jar 并且 hibernate.dialect 已经

org.hibernate.dialect.Oracle12cDialect

以前但现在改为

org.hibernate.spatial.dialect.oracle.OracleSpatial10gDialect

我知道官方只有 Oracle10 的空间方言,也有 oracle11 和 oracle12 的测试。

现在的问题是,对象通过休眠存储到 base-table 但在 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之后,import-table中的所有坐标显示都很好

但在处理每个导入数据集后,其列 'imported' {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 一起使用(澄清它完全有可能)并且可能会在我这边的任何错误配置上得到一些帮助。

我申请的persistance.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>

提前致谢。

编辑: 在实体 class 中,sdo_geometry 列(此处称为 'Position')getter 注释如下:

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

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

编辑 2: 启用 hibernate sql 跟踪后,我发现 hibernate 正在尝试此更新:

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

我指的是 POSITION=? 我认为这不可能是正确的。 在这个位置,我希望 hibernate-dialect 发生。 可能会提示与 hibernate-dialect spatial10 和 oracle12 不兼容。

我找到了一个解决方法,它没有像我预期的那样回答我的问题,但到目前为止它是一个解决方法。

而不是使用休眠对象和 merge-operator 它在使用像这样的自定义查询时工作:

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-Query 我得到的坐标不会被清空。 由于它仍然是一个 HQL 查询,可能仍然使用 spatial-dialect 的某些内容,但它似乎无法与实体管理器的 merge-command 一起正常工作。我可能仍然缺少实体上的一些注释以使其正常工作可能与 Oracle12 不兼容。

我仍然希望得到比我更好的答案。

编辑: 因为我需要 merge-behaviour 我想通过实体管理器合并对象然后执行 sql 或 hql updatequery 来存储 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();

因此,我现在正在执行一个本机 oracle 合并语句,它确实按预期工作,但逃避了所有休眠行为,并且我认为它的架构很糟糕。

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();

Hibernate Spatial 默认将坐标存储在 SDO_Ordinates 数组中,即使对于点也是如此。 SDO_Geometry.SDO_Point 字段未使用,因此始终为空。在 SQL 中,您可以使用 SDO_UTIL.GetVertices() 函数来访问坐标数据。

在 geolatte-geom 版本 1.1 或更高版本中,您应该能够设置 GEOLATTE_USE_SDO_POINT_TYPE Java 系统 属性。设置 属性 后,点几何图形将存储在 SDO_Geometry.SDO_Point 字段中。如果您的 Hibernate 版本使用较旧的 Geolatte-geom 版本,您可能需要将更新的版本添加到您的类路径中,以便您可以使用此功能。

顺便说一句。 Hibernate Spatial 的工作原理是将 Java 几何图形编码为可以在准备好的语句中设置的值,因此“... Position=?”生成的 SQL 中的一部分很好。