diff --git a/nop-dao/src/main/java/io/nop/dao/dialect/IDialect.java b/nop-dao/src/main/java/io/nop/dao/dialect/IDialect.java index dd2895c9f..833d4fb18 100644 --- a/nop-dao/src/main/java/io/nop/dao/dialect/IDialect.java +++ b/nop-dao/src/main/java/io/nop/dao/dialect/IDialect.java @@ -37,7 +37,7 @@ public interface IDialect extends IComponentModel { SQLDataType stdToNativeSqlType(StdSqlType sqlType, int precision, int scale); - IDataParameterBinder getGeometryDataParameterBinder(); + IDataTypeHandler getGeometryTypeHandler(); ISQLExceptionTranslator getSQLExceptionTranslator(); diff --git a/nop-dao/src/main/java/io/nop/dao/dialect/impl/DialectImpl.java b/nop-dao/src/main/java/io/nop/dao/dialect/impl/DialectImpl.java index f76daa710..e8dacfba6 100644 --- a/nop-dao/src/main/java/io/nop/dao/dialect/impl/DialectImpl.java +++ b/nop-dao/src/main/java/io/nop/dao/dialect/impl/DialectImpl.java @@ -20,6 +20,7 @@ import io.nop.commons.util.IoHelper; import io.nop.commons.util.StringHelper; import io.nop.core.reflect.ReflectionManager; +import io.nop.dao.dialect.IDataTypeHandler; import io.nop.dao.dialect.IDialect; import io.nop.dao.dialect.SQLDataType; import io.nop.dao.dialect.exception.ISQLExceptionTranslator; @@ -78,7 +79,7 @@ public class DialectImpl implements IDialect { private final boolean convertStringToNull = CFG_AUTO_CONVERT_EMPTY_STRING_TO_NULL.get(); - private final IDataParameterBinder geometryParameterBinder; + private final IDataTypeHandler geometryTypeHandler; public DialectImpl(DialectModel dialectModel) { this.dialectModel = dialectModel; @@ -95,13 +96,13 @@ public DialectImpl(DialectModel dialectModel) { this.renameMap.putAll(dialectModel.getRename()); } - IDataParameterBinder binder = null; + IDataTypeHandler handler = null; try { - binder = newInstance(dialectModel.getGeometryParameterBinder(), null); + handler = newInstance(dialectModel.getGeometryTypeHandler(), null); } catch (Exception e) { - LOG.warn("nop.err.dao.load-geometry-parameter-binder-fail", e); + LOG.warn("nop.err.dao.load-geometry-type-handler-fail", e); } - this.geometryParameterBinder = binder; + this.geometryTypeHandler = handler; } static T newInstance(String className, T defaultImpl) { @@ -140,8 +141,8 @@ String buildFunctionSql(String funcName) { } @Override - public IDataParameterBinder getGeometryDataParameterBinder() { - return geometryParameterBinder; + public IDataTypeHandler getGeometryTypeHandler() { + return geometryTypeHandler; } public DialectModel getDialectModel() { @@ -508,6 +509,10 @@ public String getValueLiteral(Object value) { return getDateTimeLiteral((LocalDateTime) value); if (value instanceof java.util.Date) return getDateTimeLiteral(ConvertHelper.toLocalDateTime(value)); + + IDataTypeHandler typeHandler = getGeometryTypeHandler(); + if (typeHandler != null && typeHandler.isJavaType(value)) + return typeHandler.toLiteral(value, this); return value.toString(); } @@ -517,7 +522,7 @@ public IDataParameterBinder getDataParameterBinder(StdDataType stdType, StdSqlTy return DataParameterBinders.STRING_EX; if (sqlType == StdSqlType.GEOMETRY) - return getGeometryDataParameterBinder(); + return getGeometryTypeHandler(); IDataParameterBinder binder = DataParameterBinders.getDefaultBinder(sqlType.getName()); if (binder == null) diff --git a/nop-dao/src/main/java/io/nop/dao/dialect/model/_gen/_DialectModel.java b/nop-dao/src/main/java/io/nop/dao/dialect/model/_gen/_DialectModel.java index 5259efcae..bc112e965 100644 --- a/nop-dao/src/main/java/io/nop/dao/dialect/model/_gen/_DialectModel.java +++ b/nop-dao/src/main/java/io/nop/dao/dialect/model/_gen/_DialectModel.java @@ -86,10 +86,10 @@ public abstract class _DialectModel extends io.nop.core.resource.component.Abstr /** * - * xml name: geometryParameterBinder - * JDBC使用这个类存取GEOMETRY类型数据。 IDataParameterBinder类型 + * xml name: geometryTypeHandler + * JDBC使用这个类存取GEOMETRY类型数据。 IDataTypeHandler类型 */ - private java.lang.String _geometryParameterBinder ; + private java.lang.String _geometryTypeHandler ; /** * @@ -401,19 +401,19 @@ public boolean hasFunctions(){ /** * - * xml name: geometryParameterBinder - * JDBC使用这个类存取GEOMETRY类型数据。 IDataParameterBinder类型 + * xml name: geometryTypeHandler + * JDBC使用这个类存取GEOMETRY类型数据。 IDataTypeHandler类型 */ - public java.lang.String getGeometryParameterBinder(){ - return _geometryParameterBinder; + public java.lang.String getGeometryTypeHandler(){ + return _geometryTypeHandler; } - public void setGeometryParameterBinder(java.lang.String value){ + public void setGeometryTypeHandler(java.lang.String value){ checkAllowChange(); - this._geometryParameterBinder = value; + this._geometryTypeHandler = value; } @@ -711,7 +711,7 @@ protected void outputJson(IJsonHandler out){ out.put("errorCodes",this.getErrorCodes()); out.put("features",this.getFeatures()); out.put("functions",this.getFunctions()); - out.put("geometryParameterBinder",this.getGeometryParameterBinder()); + out.put("geometryTypeHandler",this.getGeometryTypeHandler()); out.put("jdbcUrlPattern",this.getJdbcUrlPattern()); out.put("keywordQuote",this.getKeywordQuote()); out.put("keywordUnderscore",this.getKeywordUnderscore()); diff --git a/nop-dao/src/main/resources/_vfs/nop/dao/dialect/geo-support.dialect.xml b/nop-dao/src/main/resources/_vfs/nop/dao/dialect/geo-support.dialect.xml index efd246259..c4a91cfd1 100644 --- a/nop-dao/src/main/resources/_vfs/nop/dao/dialect/geo-support.dialect.xml +++ b/nop-dao/src/main/resources/_vfs/nop/dao/dialect/geo-support.dialect.xml @@ -6,7 +6,7 @@ PostgresSQL, Oracle 10g/11g,MySQL,SQLServer,H2GIS,DB2,CockroachDB - io.nop.orm.geo.binder.GeometryDataParameterBinder + io.nop.orm.geo.type.GeometryTypeHandler diff --git a/nop-orm-geo/src/main/java/io/nop/orm/geo/binder/GeometryDataParameterBinder.java b/nop-orm-geo/src/main/java/io/nop/orm/geo/binder/GeometryDataParameterBinder.java deleted file mode 100644 index 2ea80cf5d..000000000 --- a/nop-orm-geo/src/main/java/io/nop/orm/geo/binder/GeometryDataParameterBinder.java +++ /dev/null @@ -1,22 +0,0 @@ -package io.nop.orm.geo.binder; - -import io.nop.commons.type.StdSqlType; -import io.nop.dataset.binder.IDataParameterBinder; -import io.nop.dataset.binder.IDataParameters; - -public class GeometryDataParameterBinder implements IDataParameterBinder { - @Override - public StdSqlType getStdSqlType() { - return StdSqlType.GEOMETRY; - } - - @Override - public Object getValue(IDataParameters params, int index) { - return null; - } - - @Override - public void setValue(IDataParameters params, int index, Object value) { - - } -} diff --git a/nop-orm-geo/src/main/java/io/nop/orm/geo/type/GeometryTypeHandler.java b/nop-orm-geo/src/main/java/io/nop/orm/geo/type/GeometryTypeHandler.java new file mode 100644 index 000000000..b7bde1343 --- /dev/null +++ b/nop-orm-geo/src/main/java/io/nop/orm/geo/type/GeometryTypeHandler.java @@ -0,0 +1,138 @@ +package io.nop.orm.geo.type; + +import io.nop.api.core.exceptions.NopException; +import io.nop.commons.type.StdSqlType; +import io.nop.commons.util.IoHelper; +import io.nop.dao.dialect.IDataTypeHandler; +import io.nop.dao.dialect.IDialect; +import io.nop.dataset.binder.IDataParameters; +import org.geolatte.geom.ByteBuffer; +import org.geolatte.geom.ByteOrder; +import org.geolatte.geom.C2D; +import org.geolatte.geom.Envelope; +import org.geolatte.geom.Geometry; +import org.geolatte.geom.Polygon; +import org.geolatte.geom.PositionSequence; +import org.geolatte.geom.PositionSequenceBuilders; +import org.geolatte.geom.codec.Wkb; +import org.geolatte.geom.codec.WkbDecoder; +import org.geolatte.geom.codec.WkbEncoder; +import org.geolatte.geom.codec.Wkt; +import org.geolatte.geom.crs.CoordinateReferenceSystems; +import org.geolatte.geom.jts.JTS; + +import java.io.InputStream; +import java.sql.Blob; + +public class GeometryTypeHandler implements IDataTypeHandler { + + @Override + public StdSqlType getStdSqlType() { + return StdSqlType.GEOMETRY; + } + + protected String getGeomFromTextFunc() { + return "ST_GeomFromText"; + } + + protected Wkt.Dialect getWktDialect() { + return Wkt.Dialect.SFA_1_1_0; + } + + protected Wkb.Dialect getWkbDialect() { + return Wkb.Dialect.POSTGIS_EWKB_1; + } + + protected Geometry toGeometry(Object value) { + return (Geometry) value; + } + + protected ByteOrder getByteOrder() { + return ByteOrder.NDR; + } + + @Override + public String toLiteral(Object value, IDialect dialect) { + Geometry geom = toGeometry(value); + StringBuilder sb = new StringBuilder(); + sb.append(getGeomFromTextFunc()); + sb.append("('").append(Wkt.toWkt(geom, getWktDialect())); + sb.append("',").append(Math.max(geom.getSRID(), 0)); + sb.append(')'); + return sb.toString(); + } + + + @Override + public Object fromLiteral(String text, IDialect dialect) { + return null; + } + + @Override + public boolean isJavaType(Object value) { + return value instanceof Geometry; + } + + @Override + public Object getValue(IDataParameters params, int index) { + Object value = params.getObject(index); + return fromValue(value); + } + + @Override + public void setValue(IDataParameters params, int index, Object value) { + final Geometry geom = (Geometry) value; + params.setBytes(index, toBytes(geom, getWkbDialect(), getByteOrder())); + } + + protected byte[] toBytes(Geometry geom, Wkb.Dialect dialect, ByteOrder byteOrder) { + final WkbEncoder encoder = Wkb.newEncoder(dialect); + final ByteBuffer buffer = encoder.encode(geom, byteOrder); + return (buffer == null ? null : buffer.toByteArray()); + } + + protected Geometry fromValue(Object object) { + if (object == null) { + return null; + } + try { + if (object instanceof org.locationtech.jts.geom.Geometry) { + return JTS.from((org.locationtech.jts.geom.Geometry) object); + } + final WkbDecoder decoder = Wkb.newDecoder(Wkb.Dialect.POSTGIS_EWKB_1); + if (object instanceof Blob) { + return decoder.decode(toByteBuffer((Blob) object)); + } else if (object instanceof byte[]) { + return decoder.decode(ByteBuffer.from((byte[]) object)); + } else if (object instanceof org.locationtech.jts.geom.Envelope) { + return toPolygon(JTS.from((org.locationtech.jts.geom.Envelope) object)); + } else { + throw new IllegalArgumentException(); + } + } catch (Exception e) { + throw NopException.adapt(e); + } + } + + private static Geometry toPolygon(Envelope env) { + final PositionSequence ps = PositionSequenceBuilders.fixedSized(4, C2D.class) + .add(env.lowerLeft().getCoordinate(0), env.lowerLeft().getCoordinate(1)) + .add(env.lowerLeft().getCoordinate(0), env.upperRight().getCoordinate(1)) + .add(env.upperRight().getCoordinate(0), env.upperRight().getCoordinate(1)) + .add(env.lowerLeft().getCoordinate(0), env.lowerLeft().getCoordinate(1)) + .toPositionSequence(); + return new Polygon(ps, CoordinateReferenceSystems.PROJECTED_2D_METER); + } + + private static ByteBuffer toByteBuffer(Blob blob) { + InputStream is = null; + try { + is = blob.getBinaryStream(); + return ByteBuffer.from(IoHelper.readBytes(is)); + } catch (Exception e) { + throw NopException.adapt(e); + } finally { + IoHelper.safeCloseObject(is); + } + } +} diff --git a/nop-xdefs/src/main/resources/_vfs/nop/schema/orm/dialect.xdef b/nop-xdefs/src/main/resources/_vfs/nop/schema/orm/dialect.xdef index 3ee483d5a..b52285b5e 100644 --- a/nop-xdefs/src/main/resources/_vfs/nop/schema/orm/dialect.xdef +++ b/nop-xdefs/src/main/resources/_vfs/nop/schema/orm/dialect.xdef @@ -23,8 +23,8 @@ - - + +