mexxpower99:
I've ran into a problem concerning complex objects and Eclipselink which I can't seem to solve. I need to write / read spatial data as SDO_GEOMETRY
to my Oracle database.
I used to use oracle.spatial.geometry.JGeometry
, but for querying reasons I need to switch to org.geolatte.geom.Geometry
(which is the spatial type used by QueryDSL). So I wrote a custom implementation of org.eclipse.persistence.platform.database.converters.StructConverter
to handle the conversion from org.geolatte.geom.Geometry
to SDO_GEOMETRY
/ Struct
.
I used org.eclipse.persistence.platform.database.oracle.converters.JGeometryConverter
as a template.
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);
}
}
}
My entity Class is annotated the following way:
//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;
...
}
But for some odd reason the method convertToStruct
of my converter is not called when calling entityManager.persist(myEntryObject)
. Instead I get the following exception:
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
What is so odd about this is that when retrieving Entry
objects via the EntityManager
convertToObject
is indeed called and it works perfectly. But every time I try to insert / update an Entry
it doesn't work.
Is there any additional configuration I have to do to make EclipseLink recognize my converter? What else could be the cause of this problem?
I hope someone can help me. Thank you in advance.
EDIT My persistence unit looks like this:
org.eclipse.persistence.jpa.PersistenceProvider java:app/aphrodite4 false
I am loading it the following way:
@PersistenceContext(unitName = "jtaUnit")
protected EntityManager em;
Posted in S.E.F
via StackOverflow & StackExchange Atomic Web Robots
This Question have been answered
HERE