oracle12c 上的休眠空间 - sdo_geometry 对象在 em.merge 上变为空

Posted

技术标签:

【中文标题】oracle12c 上的休眠空间 - sdo_geometry 对象在 em.merge 上变为空【英文标题】:hibernate-spatial on oracle12c - sdo_geometry objects become empty on em.merge 【发布时间】:2018-03-20 09:03:19 【问题描述】:

有一个表格,其中包含一个SDO_GEOMETRY 类型的列,代表一些点。稍后将执行诸如“显示距离 x 内的所有其他点”之类的空间查询。 在通过sqlloader 将一些数据泵入导入表后,如果业务密钥仍然存在,我将在合并时将这些数据集放入基表中。

我的应用程序是一个使用hibernate 5.0.9hibernate-spatial 5.0.9 final、Oracle12c 数据库和hibernate.dialect 的可执行jar

org.hibernate.dialect.Oracle12cDialect

以前但现在改为

org.hibernate.spatial.dialect.oracle.OracleSpatial10gDialect

我知道 Oracle10 官方只有一种空间方言,而且 oracle11 和 oracle12 也有 testet。

现在的问题是,对象通过休眠存储到基表中,但在 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中的所有坐标都显示好了。

但在处理完每个导入数据集后,它的“导入”列 Y, N 设置为“Y”。这样做我正在使用myEntityManager.merge(importDataset)。由于这个原因(尽管在 sqlloader 之后所有坐标都很好),导入数据集的坐标被清除为 null。

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

我猜是空间方言不能正常工作。 我担心这是由于最后一个版本的 hibernate-spatial 和 oracle12c 之间的一些不兼容而发生的。

我在这里写信是希望有人成功地使用了 oracle12 的 hibernate-spatial(以澄清它是可能的),并且可以就我这边的任何错误配置获得一些帮助。

我的应用程序的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>

提前致谢。

编辑: 在实体类中,sdo_geometry 列(这里称为 'Position')getter 注释如下:

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

public Point getPosition() 
    return this.position;

编辑 2: 启用hibernate sql trace后,我发现hibernate正在尝试这个更新:

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

我指的是POSITION=?,我认为这不可能。 在这个位置,我希望发生休眠方言。 可能会再次暗示与 hibernate-dialect spatial10 和 oracle12 不兼容。

【问题讨论】:

【参考方案1】:

默认情况下,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 中的一部分就可以了。

【讨论】:

看起来像你说的那样工作。结果符合预期。谢谢。【参考方案2】:

我找到了一种解决方法,它没有按照我的意图回答我的问题,但它是迄今为止的一种解决方法。

而不是使用休眠对象和合并操作符它在使用这样的自定义查询时起作用:

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

【讨论】:

以上是关于oracle12c 上的休眠空间 - sdo_geometry 对象在 em.merge 上变为空的主要内容,如果未能解决你的问题,请参考以下文章

Oracle 12c如何创建A和B两个表空间并创建用户X?

Oracle12c表空间管理脚本

oracle12c之 表空间维护总结

ORACLE12C PDB创建默认表空间和用户语句

Oracle12c从入门到精通(第二版) PDF 下载

Oracle 12c 创建表空间和用户并为用户授权