Java 使用EclipseLink JPA插入空结构
我有一个带有SDO_GEOMETRY列的Oracle表,我正在尝试使用EclipseLink JPA2 2.6.1进行持久化。我的entity类将JTS几何体用于几何体对象,并且我编写了一个AttributeConverter来将SDO_几何体转换为JTS几何体。这很好,我可以从数据库中读取和写入几何图形。我遇到的问题是,我无法保持空JTS几何体。我得到以下错误: ORA-00932:不一致的数据类型:应为MDSYS.SDO_ 不确定我是否做错了什么,或者EclipseLink或Oracle中是否存在错误 persistence.xmlJava 使用EclipseLink JPA插入空结构,java,oracle,jpa,eclipselink,persistence.xml,Java,Oracle,Jpa,Eclipselink,Persistence.xml,我有一个带有SDO_GEOMETRY列的Oracle表,我正在尝试使用EclipseLink JPA2 2.6.1进行持久化。我的entity类将JTS几何体用于几何体对象,并且我编写了一个AttributeConverter来将SDO_几何体转换为JTS几何体。这很好,我可以从数据库中读取和写入几何图形。我遇到的问题是,我无法保持空JTS几何体。我得到以下错误: ORA-00932:不一致的数据类型:应为MDSYS.SDO_ 不确定我是否做错了什么,或者EclipseLink或Oracle中是
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.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 http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="mainPersistence">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>persistence.v1.dao.jpa2.converters.GeometryConverter</class>
<class>persistence.v1.dto.AuthorizationDto</class>
<properties>
<property name="eclipselink.weaving" value="false"/>
</persistence-unit>
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.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 http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="mainPersistence">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider
<class>persistence.v1.dao.jpa2.converters.GeometryConverter</class>
<class>persistence.v1.dto.AuthorizationDto</class>
<properties>
<property name="eclipselink.weaving" value="false"/>
<property name="eclipselink.session-event-listener" value="persistence.v1.dao.jpa2.listeners.GeometryInitializerSessionEventListener"/>
</properties>
</persistence-unit>
几何变换器类
package persistence.v1.dao.jpa2.converters;
import java.sql.SQLException;
import java.sql.Struct;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import oracle.jdbc.OracleConnection;
import oracle.sql.STRUCT;
import oracle.sql.StructDescriptor;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.io.oracle.OraReader;
import com.vividsolutions.jts.io.oracle.OraWriter;
@Converter(autoApply = true)
public class GeometryConverter implements AttributeConverter<Geometry, Object> {
private static ThreadLocal<OracleConnection> currentConnection = new ThreadLocal<>();
public static void setConnection(OracleConnection connection) {
currentConnection.set(connection);
}
private Geometry toGeometry(Object geometryData) {
Geometry result = null;
OraReader reader = new OraReader();
try {
StructDescriptor descriptor = new StructDescriptor(
"MDSYS.SDO_GEOMETRY", currentConnection.get());
STRUCT geometryStruct = new STRUCT(descriptor,
currentConnection.get(), (Object[]) geometryData);
result = reader.read(geometryStruct);
} catch (SQLException e) {
logger.warn("Cound not create geometry from database column", e);
throw new RuntimeException(e);
}
return result;
}
private Struct fromGeometry(Geometry geometry) {
try {
return new OraWriter().write(geometry, currentConnection.get());
} catch (SQLException e) {
logger.warn("Cound not create database column from geometry "
+ geometry.toText(), e);
throw new RuntimeException(e);
}
}
@Override
public Object convertToDatabaseColumn(Geometry geometry) {
logger.debug("<convertToDatabaseColumn");
Object result = null;
if(geometry!=null) {
result = fromGeometry(geometry);
}
logger.debug(">convertToDatabaseColumn "+result);
return result;
}
@Override
public Geometry convertToEntityAttribute(Object geometryData) {
logger.debug("<convertToEntityAttribute");
Geometry result = null;
if(geometryData!=null) {
result = toGeometry(geometryData);
}
logger.debug(">convertToEntityAttribute "+result);
return result;
}
}
package persistence.v1.dao.jpa2.converters;
导入java.sql.SQLException;
导入java.sql.Struct;
导入javax.persistence.AttributeConverter;
导入javax.persistence.Converter;
导入oracle.jdbc.OracleConnection;
导入oracle.sql.STRUCT;
导入oracle.sql.StructDescriptor;
导入com.lividsolutions.jts.geom.Geometry;
导入com.lividsolutions.jts.io.oracle.OraReader;
导入com.lividsolutions.jts.io.oracle.OraWriter;
@转换器(自动应用=真)
公共类GeometryConverter实现AttributeConverter{
私有静态ThreadLocal currentConnection=新ThreadLocal();
公共静态void setConnection(OracleConnection连接){
currentConnection.set(连接);
}
专用几何体几何(对象几何体数据){
几何结果=空;
OraReader=新的OraReader();
试一试{
StructDescriptor描述符=新的StructDescriptor(
“MDSYS.SDO_几何体”,currentConnection.get();
结构几何体结构=新结构(描述符,
currentConnection.get(),(对象[])geometryData);
结果=reader.read(geometryStruct);
}捕获(SQLE异常){
logger.warn(“无法从数据库列创建几何体”,e);
抛出新的运行时异常(e);
}
返回结果;
}
私有结构fromGeometry(几何体){
试一试{
返回新的OraWriter().write(几何体,currentConnection.get());
}捕获(SQLE异常){
logger.warn(“无法从几何体创建数据库列”
+geometry.toText(),e);
抛出新的运行时异常(e);
}
}
@凌驾
公共对象convertToDatabaseColumn(几何图形){
debug(“convertToDatabaseColumn”+结果);
返回结果;
}
@凌驾
公共几何体convertToEntityAttribute(对象几何体数据){
调试(“convertToEntityAttribute”+结果);
返回结果;
}
}
谢谢异常是由EclipseLink使用VARCHAR作为未知类型的默认值引起的。奇怪的是,对于SDO_几何体类型,AttibuteConverter.convertToEntityAttribute方法接收一个对象数组而不是一个结构,但它希望AttibuteConverter.convertToDatabaseColumn方法返回一个结构。这可能是潜在问题的症状。也许有一种方法可以告诉EclipseLink使用注释或其他配置的类型是什么,但我无法发现这是我的解决方法 我创建了一个EclipseLink SessionEventListener,它使用prelogin方法来标识返回几何体类型的实体方法。我使用DatabaseField对象创建一个新的ObjectRelationalDatabaseField,然后将sqlType属性设置为Struct,将sqlTypeName属性设置为“MDSYS.SDO_GEOMETRY”。然后我用新的ObjectRelationalDatabaseField对象更新映射。EclipseLink代码现在有足够的信息来正确设置Statement.setNull(String parameterName,int sqlType,String typeName)方法 SessionEventListener在persistence.xml中配置,空插入现在成功 GeometryInitializerSessionEventListener类
package persistence.v1.dao.jpa2.listeners;
import java.lang.reflect.Method;
import java.sql.Types;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Vector;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.internal.descriptors.MethodAttributeAccessor;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.DirectToFieldMapping;
import org.eclipse.persistence.mappings.structures.ObjectRelationalDatabaseField;
import org.eclipse.persistence.sessions.Session;
import org.eclipse.persistence.sessions.SessionEvent;
import org.eclipse.persistence.sessions.SessionEventListener;
import com.vividsolutions.jts.geom.Geometry;
public class GeometryInitializerSessionEventListener implements SessionEventListener {
// Omitting empty interface methods
@Override
public void preLogin(SessionEvent event) {
Session s = event.getSession();
@SuppressWarnings("rawtypes")
Map<Class, ClassDescriptor> descriptorMap = s.getDescriptors();
for (@SuppressWarnings("rawtypes") Entry<Class, ClassDescriptor> entry : descriptorMap.entrySet()) {
Class<?> cls = entry.getKey();
ClassDescriptor desc = entry.getValue();
Vector<DatabaseMapping> mappings = desc.getMappings();
for(DatabaseMapping mapping:mappings) {
if (mapping.getAttributeAccessor() instanceof MethodAttributeAccessor) {
MethodAttributeAccessor maa = (MethodAttributeAccessor) mapping.getAttributeAccessor();
String methodName = maa.getGetMethodName();
Method method;
try {
method = cls.getMethod(methodName, new Class[]{});
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (SecurityException e) {
throw new RuntimeException(e);
}
Class<?> returnType = method.getReturnType();
if(Geometry.class.equals(returnType)) {
DirectToFieldMapping directToFieldMapping = (DirectToFieldMapping) mapping;
ObjectRelationalDatabaseField objectRelationalDatabaseField = new ObjectRelationalDatabaseField(mapping.getField());
objectRelationalDatabaseField.setSqlType(Types.STRUCT);
objectRelationalDatabaseField.setSqlTypeName("MDSYS.SDO_GEOMETRY");
directToFieldMapping.setField(objectRelationalDatabaseField);
}
}
}
}
}
}
package persistence.v1.dao.jpa2.listeners;
导入java.lang.reflect.Method;
导入java.sql.Types;
导入java.util.Map;
导入java.util.Map.Entry;
导入java.util.Vector;
导入org.eclipse.persistence.descriptors.ClassDescriptor;
导入org.eclipse.persistence.internal.descriptors.MethodAttributeAccessor;
导入org.eclipse.persistence.mappings.DatabaseMapping;
导入org.eclipse.persistence.mappings.DirectToFieldMapping;
导入org.eclipse.persistence.mappings.structures.ObjectRelationalDatabaseField;
导入org.eclipse.persistence.sessions.Session;
导入org.eclipse.persistence.sessions.SessionEvent;
导入org.eclipse.persistence.sessions.SessionEventListener;
导入com.lividsolutions.jts.geom.Geometry;
公共类GeometryInitializerSessionEventListener实现SessionEventListener{
//省略空接口方法
@凌驾
公共作废预登录(SessionEvent事件){
会话s=event.getSession();
@抑制警告(“原始类型”)
映射描述符Map=s.getDescriptors();
对于(@SuppressWarnings(“rawtypes”)条目:descriptorMap.entrySet()){
类cls=entry.getKey();
ClassDescriptor desc=entry.getValue();
向量映射=desc.getMappings();
for(数据库映射:映射){
if(mapping.getAttributeAccessor()实例of MethodAttributeAccessor){
MethodAttributeAccessor maa=(MethodAttributeAccessor)映射。getAttributeAccessor();
字符串methodName=maa.getMethodName();
方法;
试一试{
method=cls.getMethod(methodName,新类[]{});
}捕获(无此方法例外){
抛出新的运行时异常(e);
}捕获(安全异常e){
抛出新的运行时异常(e);
}
类returnType=method.getReturnType();
if(Geometry.class.equals(returnType)){
DirectToFieldMapping DirectToFieldMapping=(DirectToFieldMapping)映射;
对象关系数据库
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.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 http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="mainPersistence">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider
<class>persistence.v1.dao.jpa2.converters.GeometryConverter</class>
<class>persistence.v1.dto.AuthorizationDto</class>
<properties>
<property name="eclipselink.weaving" value="false"/>
<property name="eclipselink.session-event-listener" value="persistence.v1.dao.jpa2.listeners.GeometryInitializerSessionEventListener"/>
</properties>
</persistence-unit>
package persistence.dao.jpa2.listeners;
import java.lang.reflect.Field;
import java.sql.Types;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Vector;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.internal.descriptors.InstanceVariableAttributeAccessor;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.DirectToFieldMapping;
import org.eclipse.persistence.mappings.structures.ObjectRelationalDatabaseField;
import org.eclipse.persistence.sessions.Session;
import org.eclipse.persistence.sessions.SessionEvent;
import org.eclipse.persistence.sessions.SessionEventListener;
import com.vividsolutions.jts.geom.Geometry;
public class GeometryInitializerSessionEventListener implements SessionEventListener
{
@Override
public void preLogin(SessionEvent event)
{
Session s = event.getSession();
@SuppressWarnings("rawtypes")
Map<Class, ClassDescriptor> descriptorMap = s.getDescriptors();
for (@SuppressWarnings("rawtypes")
Entry<Class, ClassDescriptor> entry: descriptorMap.entrySet())
{
ClassDescriptor desc = entry.getValue();
Vector<DatabaseMapping> mappings = desc.getMappings();
for (DatabaseMapping mapping: mappings)
{
if (mapping.getAttributeAccessor() instanceof InstanceVariableAttributeAccessor)
{
InstanceVariableAttributeAccessor ivaa = (InstanceVariableAttributeAccessor)mapping.getAttributeAccessor();
try
{
Field field = getFieldInHierarchy(desc.getJavaClass(), ivaa.getAttributeName());
if (Geometry.class.equals(field.getType()))
{
DirectToFieldMapping directToFieldMapping = (DirectToFieldMapping)mapping;
ObjectRelationalDatabaseField objectRelationalDatabaseField = new ObjectRelationalDatabaseField(mapping.getField());
objectRelationalDatabaseField.setSqlType(Types.STRUCT);
objectRelationalDatabaseField.setSqlTypeName("MDSYS.SDO_GEOMETRY");
directToFieldMapping.setField(objectRelationalDatabaseField);
}
}
catch (IllegalArgumentException e)
{
throw new IllegalStateException(e);
}
}
}
}
}
private static Field getFieldInHierarchy(Class<?> clazz, String fieldName)
{
for (Class<?> c = clazz; c != null; c = c.getSuperclass())
{
try
{
return c.getDeclaredField(fieldName);
}
catch (final NoSuchFieldException e)
{
// continue, trying parent
}
catch (Exception e)
{
throw new IllegalArgumentException("Cannot access field " + clazz.getName() + "." + fieldName, e);
}
}
throw new IllegalArgumentException("Cannot find field " + clazz.getName() + "." + fieldName);
}
}