java Eclipselink:从未调用的用于Spatial的自定义StructConverter
我遇到了一个关于复杂物体和日食的问题,我似乎无法解决。我需要将空间数据作为SDO_GEOMETRY
写入/读取到我的Oracle数据库中
我以前使用oracle.spatial.geometry.JGeometry
,但出于查询原因,我需要切换到org.geolatte.geom.Geometry
(这是QueryDSL使用的空间类型)。因此,我编写了一个org.eclipse.persistence.platform.database.converters.StructConverter
的自定义实现来处理从org.geolatte.geom.Geometry
到SDO_GEOMETRY
/Struct
的转换
我使用org.eclipse.persistence.platform.database.oracle.converters.JGeometryConverter
作为模板
import com.mysema.query.sql.spatial.JGeometryConverter;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Struct;
import oracle.spatial.geometry.JGeometry;
import org.eclipse.persistence.platform.database.converters.StructConverter;
import org.geolatte.geom.Geometry;
public class GeometryConverter implements StructConverter {
private static final String JGEOMETRY_DB_TYPE = "MDSYS.SDO_GEOMETRY";
private Class JGEOMETRY_CLASS;
private MethodHandle loadJSMethod;
private MethodHandle storeJSMethod;
public GeometryConverter() {
try {
JGEOMETRY_CLASS = Class.forName("oracle.spatial.geometry.JGeometry");
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
MethodHandles.Lookup lookup = MethodHandles.lookup();
try {
loadJSMethod = lookup.unreflect(JGEOMETRY_CLASS.getMethod("loadJS", new Class[] {
Struct.class
}));
storeJSMethod = lookup.unreflect(JGEOMETRY_CLASS.getMethod("storeJS", new Class[] {
JGEOMETRY_CLASS,
Connection.class
}));
} catch (IllegalAccessException | NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
@Override
public String getStructName() {
return JGEOMETRY_DB_TYPE;
}
@Override
public Class getJavaType() {
return Geometry.class;
}
@Override
public Object convertToObject(Struct struct) throws SQLException {
System.out.println("-------------------- CALLED convertToObject");
if (struct == null) {
return null;
}
try {
return JGeometryConverter.convert((JGeometry)loadJSMethod.invokeWithArguments(new Object[] {
struct
}));
} catch (Throwable throwable) {
throw new SQLException(throwable);
}
}
@Override
public Struct convertToStruct(Object geometry, Connection connection) throws SQLException {
System.out.println("-------------------- CALLED convertToStruct");
if (geometry == null) {
return null;
}
try {
return (Struct) storeJSMethod.invokeWithArguments(new Object[] {
JGeometryConverter.convert((Geometry)geometry), connection
});
} catch (Throwable throwable) {
throw new SQLException(throwable);
}
}
}
我的实体类按以下方式进行注释:
//Real package replaced by 'myPkg' for privacy reasons
@StructConverter(name = "Geometry", converter = "myPkg.GeometryConverter")
@Table(name = "ENTRY", catalog = "")
public class Entry implements Serializable {
...
@Column(name = "LOCATION")
@Convert("Geometry")
private Geometry location;
...
}
但是由于一些奇怪的原因,在调用entityManager.persist(myEntryObject)
时,我的转换器的方法convertToStruct
没有被调用。
相反,我得到了以下异常:
Caused by: java.sql.SQLException: Invalid column type
at oracle.jdbc.driver.OraclePreparedStatement.setObjectCritical(OraclePreparedStatement.java:8488)
at oracle.jdbc.driver.OraclePreparedStatement.setObjectInternal(OraclePreparedStatement.java:7995)
at oracle.jdbc.driver.OraclePreparedStatement.setObjectInternal(OraclePreparedStatement.java:8735)
at oracle.jdbc.driver.OraclePreparedStatement.setObject(OraclePreparedStatement.java:8714)
at oracle.jdbc.driver.OraclePreparedStatementWrapper.setObject(OraclePreparedStatementWrapper.java:219)
at org.eclipse.persistence.internal.databaseaccess.DatabasePlatform.setParameterValueInDatabaseCall(DatabasePlatform.java:2506)
at org.eclipse.persistence.platform.database.oracle.Oracle9Platform.setParameterValueInDatabaseCall(Oracle9Platform.java:525)
at org.eclipse.persistence.internal.databaseaccess.BindCallCustomParameter.set(BindCallCustomParameter.java:69)
at org.eclipse.persistence.internal.databaseaccess.DatabasePlatform.setParameterValueInDatabaseCall(DatabasePlatform.java:2500)
at org.eclipse.persistence.platform.database.oracle.Oracle9Platform.setParameterValueInDatabaseCall(Oracle9Platform.java:525)
at org.eclipse.persistence.internal.databaseaccess.DatabaseCall.prepareStatement(DatabaseCall.java:797)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:621)
... 101 more
奇怪的是,当通过EntityManager
{Entry
对象时,确实调用了Entry
{Entry
时,它都不起作用
要让EclipseLink识别我的转换器,我还需要进行其他配置吗?这个问题的原因还可能是什么
我希望有人能帮助我。先谢谢你
编辑
我的持久化单元如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" 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 persistence_1_0.xsd">
<persistence-unit name="jtaUnit" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>java:app/aphrodite4</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="toplink.target-database" value ="oracle.toplink.essentials.extension.spatial.Oracle10SpatialPlatform"/>
<property name="eclipselink.cache.shared.default" value="false"/>
<property name="javax.persistence.validation.group.pre-persist" value="javax.validation.groups.Default"/>
<property name="javax.persistence.validation.group.pre-update" value="javax.validation.groups.Default,myPkg.ConstraintGroups.Update"/>
</properties>
</persistence-unit>
</persistence>
我以以下方式加载它:
@PersistenceContext(unitName = "jtaUnit")
protected EntityManager em;
# 1 楼答案
在没有找到上述问题的解决方案后,我找到了一个解决办法
我编写了以下数据库函数:
要在QueryDsl查询中使用此函数,我使用以下代码: