我有一个带有SDO_GEOMETRY列的Oracle表,我正尝试使用EclipseLink JPA2 2.6.1进行持久化。我的实体类使用JTS Geometry作为几何对象,我编写了一个AttributeConverter将SDO_GEOMETRY转换为JTS Geometry。这很好用,我可以从数据库读取和写入几何。我遇到的问题是我无法持久化空的JTS Geometry。出现以下错误:
ORA-00932:不一致的数据类型:应为MDSYS. SDO_GEOMETRY,但获得了CHAR
不确定我是做错了什么,还是EclipseLink或Oracle中有bug。
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;
}
}
几何转换器类
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;
}
}
谢谢
3条答案
按热度按时间vddsk6oq1#
该异常是由EclipseLink使用VARCHAR作为未知类型的默认值引起的。奇怪的是,AttibuteConverter.convertToEntityAttribute方法接收到SDO_GEOMETRY类型的Object数组而不是Struct,但却期望AttibuteConverter.convertToDatabaseColumn方法返回Struct。这可能是潜在问题的症状。可能有一种方法可以使用注解或其他一些配置来告诉EclipseLink是什么类型,但我无法发现如何做到这一点,所以这是我的变通办法。
我创建了一个EclipseLink SessionEventListener,它使用prelogin方法来识别返回Geometry类型的Entity方法。我使用DatabaseField对象来创建一个新的ObjectRelationalDatabaseField,然后我将sqlType属性设置为Struct,并将sqlTypeName属性设置为“MDSYS.SDO_GEOMETRY”。然后我使用新的ObjectRelationalDatabaseField对象更新Map。EclipseLink代码现在有足够的信息来正确设置语句。setNull(字符串参数名称,int sqlType,字符串类型名称)方法。
在persistence.xml中配置了SessionEventListener,现在空插入成功。
几何初始化器会话事件侦听器类
Persistence.xml
tp5buhyn2#
使用方法标注时,上述答案是正确的。使用字段标注时,请使用GeometryInitializerSessionEventListener的以下实现:
8qgya5xd3#
这个错误是我忘记注解模型类时发生的,您至少需要使用
@Entity
和@Id
,当然还要在persistance.xml中注册class或your_models.jar