The Hibernate ORM 空间文档 https://docs.jboss.org/hibernate/orm/current/userguide/html_single/Hibernate_User_Guide.html#spatial-configuration,在描述空间配置时,对于 SQL Server,表示:
目前不支持 GEOGRAPHY 类型。
这可能是你的错误的原因。
作为一种可能的解决方法,您可以使用AttributeConverter https://docs.oracle.com/javaee/7/api/javax/persistence/AttributeConverter.html来处理这个特殊情况。
An AttributeConverter
允许在数据库和属性的 Java 表示形式之间进行转换。
在此用例中,请考虑以下示例:
import javax.persistence.AttributeConverter;
import javax.persistence.PersistenceException;
import org.springframework.data.geo.Point;
import com.microsoft.sqlserver.jdbc.Geography;
import com.microsoft.sqlserver.jdbc.SQLServerException;
public class SQLServerGeographyAttributeConverter implements AttributeConverter<Point, byte[]> {
@Override
public byte[] convertToDatabaseColumn(Point attribute) {
try {
Geography geography = Geography.point(attribute.getY(), attribute.getX(), 4326);
return geography.serialize();
} catch (SQLServerException throwables) {
throwables.printStackTrace();
}
return null;
}
@Override
public Point convertToEntityAttribute(byte[] dbData) {
try {
Geography geography = Geography.deserialize(dbData);
double latitude = geography.getLatitude();
double longitude = geography.getLongitude();
Point point = new Point(longitude, latitude);
return point;
} catch (SQLServerException sqle) {
// Handle as appropriate
sqle.printStackTrace();
throw new PersistenceException("An error occurred while converting Geography to Point", sqle);
}
}
}
我使用了Spring数据Point
为了简单起见,该类的代码应该看起来与 JTS 类似类非常相似。
请注意,我们正在从byte[]
而不是来自Geography
类型本身:在内部,SQL Server JDBC 驱动程序将信息处理为 UDT 和类型VARBINARY
.
然后,用这个新的注释实体中的相应字段AttributeConverter
:
@Convert(converter = SQLServerGeographyAttributeConverter.class)
@Column(columnDefinition = "geography")
public Point point;
它应该给你想要的结果:
您可以使用自定义 Hibernate@Type
也是为了这个目的。请参阅以下示例(为简单起见,让我们考虑新类型不可变):
package slartidan.sqlserverspatial;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.springframework.data.geo.Point;
import com.microsoft.sqlserver.jdbc.Geography;
public class SQLServerGeographyType
extends ImmutableType<Point> {
public SQLServerGeographyType() {
super(Point.class);
}
@Override
public int[] sqlTypes() {
return new int[]{Types.VARBINARY};
}
@Override
public Point get(
ResultSet rs,
String[] names,
SharedSessionContractImplementor session,
Object owner)
throws SQLException {
byte[] wkb = rs.getBytes(names[0]);
Geography geography = Geography.deserialize(wkb);
double latitude = geography.getLatitude();
double longitude = geography.getLongitude();
Point point = new Point(longitude, latitude);
return point;
}
@Override
public void set(
PreparedStatement st,
Point value,
int index,
SharedSessionContractImplementor session)
throws SQLException {
if (value == null) {
st.setNull(index, Types.VARBINARY);
} else {
Geography geography = Geography.point(value.getY(), value.getX(), 4326);
byte[] wkb = geography.serialize();
st.setBytes(index, wkb);
}
}
}
The Inmutable
课程取自于此优秀的文章 https://vladmihalcea.com/how-to-implement-a-custom-basic-type-using-hibernate-usertype/弗拉德·米哈尔恰:
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Objects;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.usertype.UserType;
public abstract class ImmutableType<T> implements UserType {
private final Class<T> clazz;
protected ImmutableType(Class<T> clazz) {
this.clazz = clazz;
}
@Override
public Object nullSafeGet(
ResultSet rs,
String[] names,
SharedSessionContractImplementor session,
Object owner)
throws SQLException {
return get(rs, names, session, owner);
}
@Override
public void nullSafeSet(
PreparedStatement st,
Object value,
int index,
SharedSessionContractImplementor session)
throws SQLException {
set(st, clazz.cast(value), index, session);
}
protected abstract T get(
ResultSet rs,
String[] names,
SharedSessionContractImplementor session,
Object owner) throws SQLException;
protected abstract void set(
PreparedStatement st,
T value,
int index,
SharedSessionContractImplementor session)
throws SQLException;
@Override
public Class<T> returnedClass() {
return clazz;
}
@Override
public boolean equals(Object x, Object y) {
return Objects.equals(x, y);
}
@Override
public int hashCode(Object x) {
return x.hashCode();
}
@Override
public Object deepCopy(Object value) {
return value;
}
@Override
public boolean isMutable() {
return false;
}
@Override
public Serializable disassemble(Object o) {
return (Serializable) o;
}
@Override
public Object assemble(
Serializable cached,
Object owner) {
return cached;
}
@Override
public Object replace(
Object o,
Object target,
Object owner) {
return o;
}
}
现在point
字段应该这样注释:
@Type(type = "slartidan.sqlserverspatial.SQLServerGeographyType")
@Column(columnDefinition = "geography")
public Point point;