From 51c40cd6a1fff435f00a2cd0bdd09a1d6de6cfdc Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Tue, 5 Apr 2022 11:37:38 +0200 Subject: [PATCH] HHH-15165 Add support for SQL geography type --- .../process/spi/MetadataBuildingProcess.java | 14 ++ .../hibernate/dialect/CockroachDialect.java | 6 +- .../hibernate/dialect/PostgreSQLDialect.java | 6 +- .../hibernate/dialect/SQLServerDialect.java | 1 + .../java/org/hibernate/type/SqlTypes.java | 7 + .../cockroachdb/CockroachDbContributor.java | 2 + .../dialect/postgis/PGGeographyJdbcType.java | 168 ++++++++++++++++++ .../postgis/PostgisDialectContributor.java | 1 + .../SqlServerDialectContributor.java | 1 + .../sqlserver/SqlServerGeographyType.java | 131 ++++++++++++++ .../spatial/mapping/GeographyMappingTest.java | 118 ++++++++++++ .../spatial/mapping/GeometryMappingTest.java | 63 +++---- 12 files changed, 486 insertions(+), 32 deletions(-) create mode 100644 hibernate-spatial/src/main/java/org/hibernate/spatial/dialect/postgis/PGGeographyJdbcType.java create mode 100644 hibernate-spatial/src/main/java/org/hibernate/spatial/dialect/sqlserver/SqlServerGeographyType.java create mode 100644 hibernate-spatial/src/test/java/org/hibernate/spatial/mapping/GeographyMappingTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java b/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java index 72fc8bcb6d..1716691d99 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java @@ -51,6 +51,7 @@ import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry; import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JsonJdbcType; import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; +import org.hibernate.type.descriptor.sql.DdlType; import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl; import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry; import org.hibernate.type.internal.NamedBasicTypeImpl; @@ -396,8 +397,10 @@ public class MetadataBuildingProcess { } addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.GEOMETRY, SqlTypes.VARBINARY ); addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.POINT, SqlTypes.VARBINARY ); + addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.GEOGRAPHY, SqlTypes.GEOMETRY ); final DdlTypeRegistry ddlTypeRegistry = typeConfiguration.getDdlTypeRegistry(); + // Fallback to the biggest varchar DdlType when json is requested ddlTypeRegistry.addDescriptorIfAbsent( new DdlTypeImpl( SqlTypes.JSON, @@ -405,6 +408,17 @@ public class MetadataBuildingProcess { dialect ) ); + // Fallback to the geometry DdlType when geography is requested + final DdlType geometryType = ddlTypeRegistry.getDescriptor( SqlTypes.GEOMETRY ); + if ( geometryType != null ) { + ddlTypeRegistry.addDescriptorIfAbsent( + new DdlTypeImpl( + SqlTypes.GEOGRAPHY, + geometryType.getTypeName( null, null, null ), + dialect + ) + ); + } // add explicit application registered types typeConfiguration.addBasicTypeRegistrationContributions( options.getBasicTypeRegistrations() ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java index 24a18b6ad2..4eb681046b 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java @@ -56,6 +56,7 @@ import static org.hibernate.type.SqlTypes.BINARY; import static org.hibernate.type.SqlTypes.BLOB; import static org.hibernate.type.SqlTypes.CHAR; import static org.hibernate.type.SqlTypes.CLOB; +import static org.hibernate.type.SqlTypes.GEOGRAPHY; import static org.hibernate.type.SqlTypes.GEOMETRY; import static org.hibernate.type.SqlTypes.INET; import static org.hibernate.type.SqlTypes.JSON; @@ -161,6 +162,7 @@ public class CockroachDialect extends Dialect { ddlTypeRegistry.addDescriptor( new DdlTypeImpl( UUID, "uuid", this ) ); ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOMETRY, "geometry", this ) ); + ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOGRAPHY, "geography", this ) ); ddlTypeRegistry.addDescriptor( new Scale6IntervalSecondDdlType( this ) ); // Prefer jsonb if possible @@ -193,9 +195,11 @@ public class CockroachDialect extends Dialect { jdbcTypeCode = INET; break; case "geometry": - case "geography": jdbcTypeCode = GEOMETRY; break; + case "geography": + jdbcTypeCode = GEOGRAPHY; + break; } } return jdbcTypeRegistry.getDescriptor( jdbcTypeCode ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java index 1914c79c3d..3a692f5fdf 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java @@ -88,6 +88,7 @@ import static org.hibernate.type.SqlTypes.BINARY; import static org.hibernate.type.SqlTypes.BLOB; import static org.hibernate.type.SqlTypes.CHAR; import static org.hibernate.type.SqlTypes.CLOB; +import static org.hibernate.type.SqlTypes.GEOGRAPHY; import static org.hibernate.type.SqlTypes.GEOMETRY; import static org.hibernate.type.SqlTypes.INET; import static org.hibernate.type.SqlTypes.JSON; @@ -204,6 +205,7 @@ public class PostgreSQLDialect extends Dialect { ddlTypeRegistry.addDescriptor( new DdlTypeImpl( INET, "inet", this ) ); ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOMETRY, "geometry", this ) ); + ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOGRAPHY, "geography", this ) ); ddlTypeRegistry.addDescriptor( new Scale6IntervalSecondDdlType( this ) ); if ( getVersion().isSameOrAfter( 8, 2 ) ) { @@ -257,9 +259,11 @@ public class PostgreSQLDialect extends Dialect { jdbcTypeCode = INET; break; case "geometry": - case "geography": jdbcTypeCode = GEOMETRY; break; + case "geography": + jdbcTypeCode = GEOGRAPHY; + break; } } return jdbcTypeRegistry.getDescriptor( jdbcTypeCode ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java index 85352868cc..d09fb76a00 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java @@ -171,6 +171,7 @@ public class SQLServerDialect extends AbstractTransactSQLDialect { final DdlTypeRegistry ddlTypeRegistry = typeContributions.getTypeConfiguration().getDdlTypeRegistry(); if ( getVersion().isSameOrAfter( 10 ) ) { ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOMETRY, "geometry", this ) ); + ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOGRAPHY, "geography", this ) ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/SqlTypes.java b/hibernate-core/src/main/java/org/hibernate/type/SqlTypes.java index 3d6e767108..3df348ccfa 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/SqlTypes.java +++ b/hibernate-core/src/main/java/org/hibernate/type/SqlTypes.java @@ -457,6 +457,13 @@ public class SqlTypes { */ public static final int POINT = 3201; + /** + * The constant in the Java programming language, sometimes referred to + * as a type code, that identifies the generic SQL type + * {@code GEOGRAPHY}. + */ + public static final int GEOGRAPHY = 3250; + private SqlTypes() { } diff --git a/hibernate-spatial/src/main/java/org/hibernate/spatial/dialect/cockroachdb/CockroachDbContributor.java b/hibernate-spatial/src/main/java/org/hibernate/spatial/dialect/cockroachdb/CockroachDbContributor.java index 7051a90f67..d797adbfb5 100644 --- a/hibernate-spatial/src/main/java/org/hibernate/spatial/dialect/cockroachdb/CockroachDbContributor.java +++ b/hibernate-spatial/src/main/java/org/hibernate/spatial/dialect/cockroachdb/CockroachDbContributor.java @@ -14,6 +14,7 @@ import org.hibernate.service.ServiceRegistry; import org.hibernate.spatial.FunctionKey; import org.hibernate.spatial.HSMessageLogger; import org.hibernate.spatial.contributor.ContributorImplementor; +import org.hibernate.spatial.dialect.postgis.PGGeographyJdbcType; import org.hibernate.spatial.dialect.postgis.PGGeometryJdbcType; import org.hibernate.spatial.dialect.postgis.PostgisSqmFunctionDescriptors; @@ -29,6 +30,7 @@ public class CockroachDbContributor implements ContributorImplementor { public void contributeJdbcTypes(TypeContributions typeContributions) { HSMessageLogger.SPATIAL_MSG_LOGGER.typeContributions( this.getClass().getCanonicalName() ); typeContributions.contributeJdbcType( PGGeometryJdbcType.INSTANCE_WKB_2 ); + typeContributions.contributeJdbcType( PGGeographyJdbcType.INSTANCE_WKB_2 ); } @Override diff --git a/hibernate-spatial/src/main/java/org/hibernate/spatial/dialect/postgis/PGGeographyJdbcType.java b/hibernate-spatial/src/main/java/org/hibernate/spatial/dialect/postgis/PGGeographyJdbcType.java new file mode 100644 index 0000000000..f36b0f671b --- /dev/null +++ b/hibernate-spatial/src/main/java/org/hibernate/spatial/dialect/postgis/PGGeographyJdbcType.java @@ -0,0 +1,168 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ + +package org.hibernate.spatial.dialect.postgis; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; + +import org.hibernate.dialect.Dialect; +import org.hibernate.spatial.GeometryLiteralFormatter; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.type.SqlTypes; +import org.hibernate.type.descriptor.ValueBinder; +import org.hibernate.type.descriptor.ValueExtractor; +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.JavaType; +import org.hibernate.type.descriptor.jdbc.BasicBinder; +import org.hibernate.type.descriptor.jdbc.BasicExtractor; +import org.hibernate.type.descriptor.jdbc.JdbcLiteralFormatter; +import org.hibernate.type.descriptor.jdbc.JdbcType; + +import org.geolatte.geom.ByteBuffer; +import org.geolatte.geom.ByteOrder; +import org.geolatte.geom.Geometry; +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.codec.WktDecoder; +import org.postgresql.util.PGobject; + +/** + * Type Descriptor for the Postgis Geography type + * + * @author Karel Maesen, Geovise BVBA + */ +public class PGGeographyJdbcType implements JdbcType { + + + private final Wkb.Dialect wkbDialect; + + // Type descriptor instance using EWKB v2 (postgis versions >= 2.2.2, see: https://trac.osgeo.org/postgis/ticket/3181) + public static final PGGeographyJdbcType INSTANCE_WKB_2 = new PGGeographyJdbcType( Wkb.Dialect.POSTGIS_EWKB_2 ); + + @Override + public JdbcLiteralFormatter getJdbcLiteralFormatter(JavaType javaType) { + return new PGGeometryLiteralFormatter<>( javaType ); + } + + private PGGeographyJdbcType(Wkb.Dialect dialect) { + wkbDialect = dialect; + } + + public Geometry toGeometry(Object object) { + if ( object == null ) { + return null; + } + ByteBuffer buffer; + if ( object instanceof PGobject ) { + String pgValue = ( (PGobject) object ).getValue(); + if (pgValue == null) { + return null; + } + if ( pgValue.startsWith( "00" ) || pgValue.startsWith( "01" ) ) { + //we have a WKB because this pgValue starts with the bit-order byte + buffer = ByteBuffer.from( pgValue ); + final WkbDecoder decoder = Wkb.newDecoder( wkbDialect ); + return decoder.decode( buffer ); + } + else { + return parseWkt( pgValue ); + } + + } + throw new IllegalStateException( "Received object of type " + object.getClass().getCanonicalName() ); + } + + private static Geometry parseWkt(String pgValue) { + final WktDecoder decoder = Wkt.newDecoder( Wkt.Dialect.POSTGIS_EWKT_1 ); + return decoder.decode( pgValue ); + } + + + @Override + public int getJdbcTypeCode() { + return Types.OTHER; + } + + @Override + public int getDefaultSqlTypeCode() { + return SqlTypes.GEOGRAPHY; + } + + @Override + public ValueBinder getBinder(final JavaType javaType) { + return new BasicBinder( javaType, this ) { + @Override + protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) + throws SQLException { + final PGobject obj = toPGobject( value, options ); + st.setObject( index, obj ); + } + + @Override + protected void doBind(CallableStatement st, X value, String name, WrapperOptions options) + throws SQLException { + final PGobject obj = toPGobject( value, options ); + st.setObject( name, obj ); + } + + private PGobject toPGobject(X value, WrapperOptions options) throws SQLException { + final WkbEncoder encoder = Wkb.newEncoder( wkbDialect ); + final Geometry geometry = getJavaType().unwrap( value, Geometry.class, options ); + final String hexString = encoder.encode( geometry, ByteOrder.NDR ).toString(); + final PGobject obj = new PGobject(); + obj.setType( "geography" ); + obj.setValue( hexString ); + return obj; + } + + }; + } + + @Override + public ValueExtractor getExtractor(final JavaType javaType) { + return new BasicExtractor( javaType, this ) { + + + @Override + protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException { + return getJavaType().wrap( toGeometry( rs.getObject( paramIndex ) ), options ); + } + + @Override + protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException { + return getJavaType().wrap( toGeometry( statement.getObject( index ) ), options ); + } + + @Override + protected X doExtract(CallableStatement statement, String name, WrapperOptions options) + throws SQLException { + return getJavaType().wrap( toGeometry( statement.getObject( name ) ), options ); + } + }; + } + + static class PGGeometryLiteralFormatter extends GeometryLiteralFormatter { + public PGGeometryLiteralFormatter(JavaType javaType) { + super( javaType, Wkt.Dialect.POSTGIS_EWKT_1, "" ); + } + + @Override + public void appendJdbcLiteral( + SqlAppender appender, T value, Dialect dialect, WrapperOptions wrapperOptions) { + Geometry geom = javaType.unwrap( value, Geometry.class, wrapperOptions ); + appender.appendSql( "st_geogfromtext('" ); + appender.appendSql( Wkt.toWkt( geom, wktDialect ) ); + appender.appendSql( "')" ); + } + } +} diff --git a/hibernate-spatial/src/main/java/org/hibernate/spatial/dialect/postgis/PostgisDialectContributor.java b/hibernate-spatial/src/main/java/org/hibernate/spatial/dialect/postgis/PostgisDialectContributor.java index 2550a5d66a..da4fcf446f 100644 --- a/hibernate-spatial/src/main/java/org/hibernate/spatial/dialect/postgis/PostgisDialectContributor.java +++ b/hibernate-spatial/src/main/java/org/hibernate/spatial/dialect/postgis/PostgisDialectContributor.java @@ -26,6 +26,7 @@ public class PostgisDialectContributor implements ContributorImplementor { public void contributeJdbcTypes(TypeContributions typeContributions) { HSMessageLogger.SPATIAL_MSG_LOGGER.typeContributions( this.getClass().getCanonicalName() ); typeContributions.contributeJdbcType( PGGeometryJdbcType.INSTANCE_WKB_2 ); + typeContributions.contributeJdbcType( PGGeographyJdbcType.INSTANCE_WKB_2 ); } @Override diff --git a/hibernate-spatial/src/main/java/org/hibernate/spatial/dialect/sqlserver/SqlServerDialectContributor.java b/hibernate-spatial/src/main/java/org/hibernate/spatial/dialect/sqlserver/SqlServerDialectContributor.java index 1e93cfe680..5022169d12 100644 --- a/hibernate-spatial/src/main/java/org/hibernate/spatial/dialect/sqlserver/SqlServerDialectContributor.java +++ b/hibernate-spatial/src/main/java/org/hibernate/spatial/dialect/sqlserver/SqlServerDialectContributor.java @@ -25,6 +25,7 @@ public class SqlServerDialectContributor implements ContributorImplementor { public void contributeJdbcTypes(TypeContributions typeContributions) { HSMessageLogger.SPATIAL_MSG_LOGGER.typeContributions( this.getClass().getCanonicalName() ); typeContributions.contributeJdbcType( SqlServerGeometryType.INSTANCE ); + typeContributions.contributeJdbcType( SqlServerGeographyType.INSTANCE ); } @Override diff --git a/hibernate-spatial/src/main/java/org/hibernate/spatial/dialect/sqlserver/SqlServerGeographyType.java b/hibernate-spatial/src/main/java/org/hibernate/spatial/dialect/sqlserver/SqlServerGeographyType.java new file mode 100644 index 0000000000..6b98f3abcf --- /dev/null +++ b/hibernate-spatial/src/main/java/org/hibernate/spatial/dialect/sqlserver/SqlServerGeographyType.java @@ -0,0 +1,131 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ + +package org.hibernate.spatial.dialect.sqlserver; + +import java.sql.Blob; +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; + +import org.hibernate.spatial.GeometryLiteralFormatter; +import org.hibernate.type.SqlTypes; +import org.hibernate.type.descriptor.ValueBinder; +import org.hibernate.type.descriptor.ValueExtractor; +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.JavaType; +import org.hibernate.type.descriptor.jdbc.BasicBinder; +import org.hibernate.type.descriptor.jdbc.BasicExtractor; +import org.hibernate.type.descriptor.jdbc.JdbcLiteralFormatter; +import org.hibernate.type.descriptor.jdbc.JdbcType; + +import org.geolatte.geom.Geometry; +import org.geolatte.geom.codec.Wkt; +import org.geolatte.geom.codec.db.sqlserver.Decoders; +import org.geolatte.geom.codec.db.sqlserver.Encoders; + +/** + * Type descriptor for the SQL Server 2008 Geography type. + * + * @author Karel Maesen, Geovise BVBA + * creation-date: 8/23/11 + */ +public class SqlServerGeographyType implements JdbcType { + + /** + * An instance of the descrtiptor + */ + public static final SqlServerGeographyType INSTANCE = new SqlServerGeographyType(); + + @Override + public int getJdbcTypeCode() { + return Types.ARRAY; + } + + @Override + public int getDefaultSqlTypeCode() { + return SqlTypes.GEOGRAPHY; + } + + @Override + public JdbcLiteralFormatter getJdbcLiteralFormatter(JavaType javaType) { + return new GeometryLiteralFormatter<>( javaType, Wkt.Dialect.SFA_1_2_1, "geography::STGeomFromText" ); + } + + @Override + public ValueBinder getBinder(final JavaType javaType) { + return new BasicBinder( javaType, this ) { + @Override + protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) + throws SQLException { + final Geometry geometry = getJavaType().unwrap( value, Geometry.class, options ); + final byte[] bytes = Encoders.encode( geometry ); + st.setObject( index, bytes ); + } + + @Override + protected void doBind(CallableStatement st, X value, String name, WrapperOptions options) + throws SQLException { + final Geometry geometry = getJavaType().unwrap( value, Geometry.class, options ); + final byte[] bytes = Encoders.encode( geometry ); + st.setObject( name, bytes ); + } + + }; + } + + @Override + public ValueExtractor getExtractor(final JavaType javaType) { + return new BasicExtractor( javaType, this ) { + + @Override + protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException { + return getJavaType().wrap( toGeometry( rs.getObject( paramIndex ) ), options ); + } + + @Override + protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException { + return getJavaType().wrap( toGeometry( statement.getObject( index ) ), options ); + } + + @Override + protected X doExtract(CallableStatement statement, String name, WrapperOptions options) + throws SQLException { + return getJavaType().wrap( toGeometry( statement.getObject( name ) ), options ); + } + }; + } + + private Geometry toGeometry(Object obj) { + byte[] raw = null; + if ( obj == null ) { + return null; + } + if ( ( obj instanceof byte[] ) ) { + raw = (byte[]) obj; + } + else if ( obj instanceof Blob ) { + raw = toByteArray( (Blob) obj ); + } + else { + throw new IllegalArgumentException( "Expected byte array or BLOB" ); + } + return Decoders.decode( raw ); + } + + private byte[] toByteArray(Blob blob) { + try { + return blob.getBytes( 1, (int) blob.length() ); + } + catch (SQLException e) { + throw new RuntimeException( "Error on transforming blob into array.", e ); + } + } + +} diff --git a/hibernate-spatial/src/test/java/org/hibernate/spatial/mapping/GeographyMappingTest.java b/hibernate-spatial/src/test/java/org/hibernate/spatial/mapping/GeographyMappingTest.java new file mode 100644 index 0000000000..a54f60b1ce --- /dev/null +++ b/hibernate-spatial/src/test/java/org/hibernate/spatial/mapping/GeographyMappingTest.java @@ -0,0 +1,118 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ + +package org.hibernate.spatial.mapping; + +import org.hibernate.annotations.JdbcTypeCode; +import org.hibernate.metamodel.mapping.BasicValuedModelPart; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.type.SqlTypes; +import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry; +import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import org.geolatte.geom.G2D; +import org.geolatte.geom.Point; +import org.geolatte.geom.codec.Wkt; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; + + +@DomainModel(annotatedClasses = { GeographyMappingTest.PointEntity.class }) +@ServiceRegistry +@SessionFactory +public class GeographyMappingTest { + + @Test + public void testSimpleEntity(SessionFactoryScope scope) { + final EntityPersister entityDescriptor = scope.getSessionFactory() + .getRuntimeMetamodels() + .getMappingMetamodel() + .getEntityDescriptor( PointEntity.class ); + final JdbcTypeRegistry jdbcTypeRegistry = entityDescriptor.getFactory() + .getTypeConfiguration() + .getJdbcTypeRegistry(); + + final JavaTypeRegistry javaTypeRegistry = entityDescriptor.getFactory() + .getTypeConfiguration() + .getJavaTypeRegistry(); + + BasicValuedModelPart part = (BasicValuedModelPart) entityDescriptor.findSubPart( "location" ); + assertThat( part.getJdbcMapping().getJdbcType(), equalTo( jdbcTypeRegistry.getDescriptor( SqlTypes.GEOGRAPHY ) ) ); + scope.inTransaction( + s -> { + s.persist( + new PointEntity( + 1, + "test", + (Point) Wkt.fromWkt( + "SRID=4326;POINT(48.2083736 16.3724441)" + ) + ) + ); + s.flush(); + s.clear(); + + PointEntity pointEntity = s.find( PointEntity.class, 1 ); + assertThat( pointEntity.location, is( notNullValue() ) ); + } + ); + } + + @Entity(name = "MLEntity") + public static class PointEntity { + + @Id + private Integer id; + private String type; + @JdbcTypeCode(SqlTypes.GEOGRAPHY) + private Point location; + + public PointEntity() { + } + + public PointEntity(Integer id, String type, Point location) { + this.id = id; + this.type = type; + this.location = location; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Point getLineString() { + return location; + } + + public void setLineString(Point location) { + this.location = location; + } + } +} \ No newline at end of file diff --git a/hibernate-spatial/src/test/java/org/hibernate/spatial/mapping/GeometryMappingTest.java b/hibernate-spatial/src/test/java/org/hibernate/spatial/mapping/GeometryMappingTest.java index 081197df95..f172d30306 100644 --- a/hibernate-spatial/src/test/java/org/hibernate/spatial/mapping/GeometryMappingTest.java +++ b/hibernate-spatial/src/test/java/org/hibernate/spatial/mapping/GeometryMappingTest.java @@ -28,7 +28,7 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; -@DomainModel(annotatedClasses = { MLEntity.class }) +@DomainModel(annotatedClasses = { GeometryMappingTest.MLEntity.class }) @ServiceRegistry @SessionFactory public class GeometryMappingTest { @@ -51,43 +51,46 @@ public class GeometryMappingTest { assertThat( part.getJavaType(), equalTo( GeolatteGeometryJavaType.MULTILINESTRING_INSTANCE ) ); } -} -@Entity -class MLEntity { + @Entity(name = "MLEntity") + public static class MLEntity { - @Id - private Integer id; - private String type; - private MultiLineString lineString; + @Id + private Integer id; + private String type; + private MultiLineString lineString; - public MLEntity(Integer id, String type, MultiLineString lineString) { - this.id = id; - this.type = type; - this.lineString = lineString; - } + public MLEntity() { + } - public Integer getId() { - return id; - } + public MLEntity(Integer id, String type, MultiLineString lineString) { + this.id = id; + this.type = type; + this.lineString = lineString; + } - public void setId(Integer id) { - this.id = id; - } + public Integer getId() { + return id; + } - public String getType() { - return type; - } + public void setId(Integer id) { + this.id = id; + } - public void setType(String type) { - this.type = type; - } + public String getType() { + return type; + } - public MultiLineString getLineString() { - return lineString; - } + public void setType(String type) { + this.type = type; + } - public void setLineString(MultiLineString lineString) { - this.lineString = lineString; + public MultiLineString getLineString() { + return lineString; + } + + public void setLineString(MultiLineString lineString) { + this.lineString = lineString; + } } } \ No newline at end of file