【问题标题】:Insert Null Struct using EclipseLink JPA使用 EclipseLink JPA 插入 Null 结构
【发布时间】:2015-12-11 18:13:23
【问题描述】:

我有一个带有 SDO_GEOMETRY 列的 Oracle 表,我正在尝试使用 EclipseLink JPA2 2.6.1 进行持久性。我的实体类对几何对象使用 JTS 几何,并且我编写了一个 AttributeConverter 来从 SDO_GEOMETRY 转换为 JTS 几何。这很好用,我可以从数据库中读取和写入几何图形。我遇到的问题是我无法保留空 JTS 几何。我收到以下错误:

ORA-00932:不一致的数据类型:预期 MDSYS.SDO_GEOMETRY 得到 CHAR

不确定我做错了什么,或者 EclipseLink 或 Oracle 中是否存在错误。

persistence.xml

<?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>

实体类

package persistence.v1.dto;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

import persistence.v1.dao.jpa2.converters.GeometryConverter;

import com.vividsolutions.jts.geom.Geometry;

@Entity
@Table(name="AUTHORIZATION")
public class AuthorizationDto {

private String authorizationGuid;
private Geometry authorizationGeometry;

public AuthorizationDto() {
}

@Id
@GeneratedValue(strategy = GenerationType.AUTO, generator = "system-uuid")
@Column(name="AUTHORIZATION_GUID", nullable=false)
public String getAuthorizationGuid() {
    return this.authorizationGuid;
}

public void setAuthorizationGuid(String authorizationGuid) {
    this.authorizationGuid = authorizationGuid;
}


@javax.persistence.Convert(converter=GeometryConverter.class)
@Column(name="AUTHORIZATION_GEOMETRY")
public Geometry getAuthorizationGeometry() {
    return this.authorizationGeometry;
}

public void setAuthorizationGeometry(Geometry authorizationGeometry) {
    this.authorizationGeometry = authorizationGeometry;
}
}

GeometryConverter 类

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;
}
}

谢谢

【问题讨论】:

    标签: java oracle jpa eclipselink persistence.xml


    【解决方案1】:

    该异常是由 EclipseLink 使用 VARCHAR 作为未知类型的默认值引起的。奇怪的是,AttibuteConverter.convertToEntityAttribute 方法接收一个 Object 数组而不是 SDO_GEOMETRY 类型的 Struct,但期望 AttibuteConverter.convertToDatabaseColumn 方法返回一个 Struct。这可能是潜在问题的症状。可能有一种方法可以告诉 EclipseLink 使用注释或其他配置的类型是什么,但我无法发现这是我的解决方法。

    我创建了一个 EclipseLink SessionEventListener,它使用 prelogin 方法来识别返回 Geometry 类型的实体方法。我使用 DatabaseField 对象创建一个新的 ObjectRelationalDatabaseField,然后将 sqlType 属性设置为 Struct,将 sqlTypeName 属性设置为“MDSYS.SDO_GEOMETRY”。然后我用新的 ObjectRelationalDatabaseField 对象更新映射。 EclipseLink 代码现在有足够的信息来正确设置 Statement.setNull (String parameterName, int sqlType, String typeName) 方法。

    在persistence.xml中配置了SessionEventListener,现在空插入成功了。

    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);
                    }
                }
            }
        }
    }
    }
    

    Persistence.xml

    <?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>
    

    【讨论】:

      【解决方案2】:

      使用方法注释时,上述答案是正确的。使用字段注解时,使用 GeometryInitializerSessionEventListener 的如下实现:

      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);
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-11-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-02-19
        • 1970-01-01
        • 2013-04-29
        • 2017-10-22
        相关资源
        最近更新 更多