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.9
、hibernate-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 中的一部分很好。
有一个 table 包含代表一些点的 SDO_GEOMETRY
类型的列。稍后将执行像 'show all other points within distance x' 这样的空间查询。
通过 sqlloader
将一些数据泵入 import-table 之后,如果业务键仍然存在,我将把这些数据集带入 base-table 并合并。
我的应用程序是一个使用 hibernate 5.0.9
、hibernate-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 中的一部分很好。