有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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.GeometrySDO_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) 个答案

  1. # 1 楼答案

    在没有找到上述问题的解决方案后,我找到了一个解决办法

    我编写了以下数据库函数:

    create or replace FUNCTION WITHIN_DISTANCE(g sdo_geometry, lon number, lat number, distLimit number)
    RETURN VARCHAR2 AS 
    
    dist NUMBER;
    result VARCHAR2(5);
    tmpGeo SDO_GEOMETRY;
    
    BEGIN
      tmpGeo := SDO_GEOMETRY(2001, 8307, SDO_POINT_TYPE(lon, lat, NULL), NULL, NULL);
      SELECT SDO_GEOM.SDO_DISTANCE(g, tmpGeo, 0.005) INTO dist FROM dual;
    
      IF dist <= distLimit THEN
        result := 'TRUE';
      ELSE
        result := 'FALSE';
      END IF;
    
      RETURN result;
    
    END WITHIN_DISTANCE;
    

    要在QueryDsl查询中使用此函数,我使用以下代码:

    Predicate pred = Expressions.stringTemplate("function('WITHIN_DISTANCE', {0}, {1}, {2}, {3})" , 
                            QEntry.entry.location,
                            loc.getLon(),
                            loc.getLat(),
                            crit.getRadius()).eq("TRUE");