diff --git a/hibernate-core/hibernate-core.gradle b/hibernate-core/hibernate-core.gradle index 272a920c0b..a19d4996b3 100644 --- a/hibernate-core/hibernate-core.gradle +++ b/hibernate-core/hibernate-core.gradle @@ -64,6 +64,7 @@ dependencies { testImplementation libraries.mockito_inline testImplementation libraries.jodaTime testImplementation libraries.assertj + testImplementation 'org.orbisgis:h2gis:1.5.0' testRuntimeOnly libraries.byteBuddy testRuntimeOnly libraries.jakarta_weld diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcSelectExecutorStandardImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcSelectExecutorStandardImpl.java index 086e11b813..ae24ac5d35 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcSelectExecutorStandardImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcSelectExecutorStandardImpl.java @@ -16,6 +16,7 @@ import java.util.stream.Stream; import java.util.stream.StreamSupport; import org.hibernate.CacheMode; +import org.hibernate.Internal; import org.hibernate.LockOptions; import org.hibernate.ScrollMode; import org.hibernate.cache.spi.QueryKey; @@ -89,6 +90,28 @@ public class JdbcSelectExecutorStandardImpl implements JdbcSelectExecutor { ); } + @Internal + public List list( + JdbcSelect jdbcSelect, + JdbcParameterBindings jdbcParameterBindings, + ExecutionContext executionContext, + RowTransformer rowTransformer, + ListResultsConsumer.UniqueSemantic uniqueSemantic, + Function, DeferredResultSetAccess> resultSetAccessCreator) { + // Only do auto flushing for top level queries + return executeQuery( + jdbcSelect, + executionContext, + rowTransformer, + (sql) -> executionContext.getSession() + .getJdbcCoordinator() + .getStatementPreparer() + .prepareStatement( sql ), + resultSetAccessCreator, + ListResultsConsumer.instance( uniqueSemantic ) + ); + } + @Override public ScrollableResultsImplementor scroll( JdbcSelect jdbcSelect, @@ -112,6 +135,26 @@ public class JdbcSelectExecutorStandardImpl implements JdbcSelectExecutor { ); } + @Internal + public ScrollableResultsImplementor scroll( + JdbcSelect jdbcSelect, + ExecutionContext executionContext, + RowTransformer rowTransformer, + Function, DeferredResultSetAccess> resultSetAccessCreator) { + // Only do auto flushing for top level queries + return executeQuery( + jdbcSelect, + executionContext, + rowTransformer, + (sql) -> executionContext.getSession() + .getJdbcCoordinator() + .getStatementPreparer() + .prepareStatement( sql ), + resultSetAccessCreator, + ScrollableResultsConsumer.instance() + ); + } + @Override public Stream stream( JdbcSelect jdbcSelect, @@ -132,6 +175,25 @@ public class JdbcSelectExecutorStandardImpl implements JdbcSelectExecutor { return stream.onClose( scrollableResults::close ); } + @Internal + public Stream stream( + JdbcSelect jdbcSelect, + ExecutionContext executionContext, + RowTransformer rowTransformer, + Function, DeferredResultSetAccess> resultSetAccessCreator) { + final ScrollableResultsImplementor scrollableResults = scroll( + jdbcSelect, + executionContext, + rowTransformer, + resultSetAccessCreator + ); + final ScrollableResultsIterator iterator = new ScrollableResultsIterator<>( scrollableResults ); + final Spliterator spliterator = Spliterators.spliteratorUnknownSize( iterator, Spliterator.NONNULL ); + + final Stream stream = StreamSupport.stream( spliterator, false ); + return stream.onClose( scrollableResults::close ); + } + private T executeQuery( JdbcSelect jdbcSelect, JdbcParameterBindings jdbcParameterBindings, @@ -163,6 +225,7 @@ public class JdbcSelectExecutorStandardImpl implements JdbcSelectExecutor { } } } + private T doExecuteQuery( JdbcSelect jdbcSelect, JdbcParameterBindings jdbcParameterBindings, @@ -170,19 +233,38 @@ public class JdbcSelectExecutorStandardImpl implements JdbcSelectExecutor { RowTransformer rowTransformer, Function statementCreator, ResultsConsumer resultsConsumer) { - - final DeferredResultSetAccess deferredResultSetAccess = new DeferredResultSetAccess( + return executeQuery( jdbcSelect, - jdbcParameterBindings, executionContext, - statementCreator + rowTransformer, + (sql) -> executionContext.getSession() + .getJdbcCoordinator() + .getStatementPreparer() + .prepareStatement( sql ), + (stmntCreator) -> new DeferredResultSetAccess( + jdbcSelect, + jdbcParameterBindings, + executionContext, + statementCreator + ), + resultsConsumer ); + } + + private T executeQuery( + JdbcSelect jdbcSelect, + ExecutionContext executionContext, + RowTransformer rowTransformer, + Function statementCreator, + Function, DeferredResultSetAccess> resultSetAccessCreator, + ResultsConsumer resultsConsumer) { + final DeferredResultSetAccess resultSetAccess = resultSetAccessCreator.apply( statementCreator ); final JdbcValues jdbcValues = resolveJdbcValuesSource( - executionContext.getQueryIdentifier( deferredResultSetAccess.getFinalSql() ), + executionContext.getQueryIdentifier( resultSetAccess.getFinalSql() ), jdbcSelect, resultsConsumer.canResultsBeCached(), executionContext, - deferredResultSetAccess + resultSetAccess ); if ( rowTransformer == null ) { @@ -251,7 +333,7 @@ public class JdbcSelectExecutorStandardImpl implements JdbcSelectExecutor { // because these lock options are only for Initializers. // If we wouldn't omit this, the follow on lock requests would be no-ops, // because the EntityEntrys would already have the desired lock mode - deferredResultSetAccess.usesFollowOnLocking() + resultSetAccess.usesFollowOnLocking() ? LockOptions.NONE : executionContext.getQueryOptions().getLockOptions(), rowTransformer, diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/DeferredResultSetAccess.java b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/DeferredResultSetAccess.java index 2b34013237..c1e76562e9 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/DeferredResultSetAccess.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/DeferredResultSetAccess.java @@ -214,7 +214,7 @@ public class DeferredResultSetAccess extends AbstractResultSetAccess { } try { eventListenerManager.jdbcExecuteStatementStart(); - resultSet = preparedStatement.executeQuery(); + resultSet = wrapResultSet( preparedStatement.executeQuery() ); } finally { eventListenerManager.jdbcExecuteStatementEnd(); @@ -260,6 +260,10 @@ public class DeferredResultSetAccess extends AbstractResultSetAccess { } } + protected ResultSet wrapResultSet(ResultSet resultSet) throws SQLException { + return resultSet; + } + protected LockMode determineFollowOnLockMode(LockOptions lockOptions) { final LockMode lockModeToUse = lockOptions.findGreatestLockMode(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/FirePoint.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/FirePoint.java new file mode 100644 index 0000000000..4c33335f73 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/FirePoint.java @@ -0,0 +1,59 @@ +/* + * 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 http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.mapping.type.contribution.jts; + +import org.hibernate.annotations.JavaType; + +import jakarta.persistence.Basic; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import org.locationtech.jts.geom.Point; + +/** + * @author Steve Ebersole + */ +@Entity(name = "FirePoint") +@Table(name = "FirePoint") +public class FirePoint { + @Id + private Integer id; + @Basic + private String name; + @Basic + @JavaType( PointJavaType.class ) + private Point coordinate; + + private FirePoint() { + } + + public FirePoint(Integer id, String name, Point coordinate) { + this.id = id; + this.name = name; + this.coordinate = coordinate; + } + + public Integer getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Point getCoordinate() { + return coordinate; + } + + public void setCoordinate(Point coordinate) { + this.coordinate = coordinate; + } +} \ No newline at end of file diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/JtsContributorTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/JtsContributorTests.java new file mode 100644 index 0000000000..274bc2216c --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/JtsContributorTests.java @@ -0,0 +1,56 @@ +/* + * 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 http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.mapping.type.contribution.jts; + +import org.hibernate.boot.model.TypeContributor; +import org.hibernate.orm.test.mapping.type.contribution.jts.infrastructure.CustomConnectionProviderInitiator; +import org.hibernate.orm.test.mapping.type.contribution.jts.infrastructure.CustomJdbcServicesInitiator; + +import org.hibernate.testing.orm.junit.BootstrapServiceRegistry; +import org.hibernate.testing.orm.junit.BootstrapServiceRegistry.JavaService; +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.AfterEach; +import org.junit.jupiter.api.Test; + +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.GeometryFactory; + +/** + * @author Steve Ebersole + */ +@BootstrapServiceRegistry( + javaServices = @JavaService( role = TypeContributor.class, impl = JtsTypeContributor.class ) +) +@ServiceRegistry( + initiators = { CustomJdbcServicesInitiator.class, CustomConnectionProviderInitiator.class } +) +@DomainModel( annotatedClasses = FirePoint.class ) +@SessionFactory +public class JtsContributorTests { + @Test + public void simpleTest(SessionFactoryScope scope) { + scope.inTransaction( (session) -> { + // save one with a Point + final GeometryFactory geometryFactory = new GeometryFactory(); + session.save( new FirePoint( 1, "alpha", geometryFactory.createPoint( new Coordinate( 1, 2, 3 ) ) ) ); + // and one without (null) + session.save( new FirePoint( 2, "bravo", null ) ); + } ); + + scope.inTransaction( (session) -> { + session.createQuery( "select fp.coordinate from FirePoint fp" ).list(); + } ); + } + + @AfterEach + public void dropTestData(SessionFactoryScope scope) { + scope.inTransaction( (session) -> session.createQuery( "delete FirePoint" ).executeUpdate() ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/JtsTypeContributor.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/JtsTypeContributor.java new file mode 100644 index 0000000000..91cbd18568 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/JtsTypeContributor.java @@ -0,0 +1,22 @@ +/* + * 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 http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.mapping.type.contribution.jts; + +import org.hibernate.boot.model.TypeContributions; +import org.hibernate.boot.model.TypeContributor; +import org.hibernate.service.ServiceRegistry; + +/** + * TypeContributor for applying the H2GIS JTS type descriptors + */ +public class JtsTypeContributor implements TypeContributor { + @Override + public void contribute(TypeContributions typeContributions, ServiceRegistry serviceRegistry) { + typeContributions.contributeJavaTypeDescriptor( PointJavaType.INSTANCE ); + typeContributions.contributeJdbcTypeDescriptor( new PointJdbcType() ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/PointJavaType.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/PointJavaType.java new file mode 100644 index 0000000000..14bec78013 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/PointJavaType.java @@ -0,0 +1,63 @@ +/* + * 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 http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.mapping.type.contribution.jts; + +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.BasicJavaType; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.descriptor.jdbc.JdbcTypeDescriptorIndicators; + +import org.locationtech.jts.geom.Point; + +/** + * @author Steve Ebersole + */ +public class PointJavaType implements BasicJavaType { + /** + * Singleton access + */ + public static final PointJavaType INSTANCE = new PointJavaType(); + + @Override + public Class getJavaTypeClass() { + return Point.class; + } + + @Override + public JdbcType getRecommendedJdbcType(JdbcTypeDescriptorIndicators context) { + return context + .getTypeConfiguration() + .getJdbcTypeDescriptorRegistry() + .getDescriptor( PointJdbcType.POINT_TYPE_CODE ); + } + + @Override + public Point fromString(CharSequence string) { + throw unsupported( String.class ); + } + + private static UnsupportedOperationException unsupported(Class type) { + return new UnsupportedOperationException( "At the moment, Point cannot be converted to/from other types : " + type.getName() ); + } + + @SuppressWarnings("unchecked") + @Override + public X unwrap(Point value, Class type, WrapperOptions options) { + if ( value == null || type.isInstance( value ) ) { + return (X) value; + } + throw unsupported( type ); + } + + @Override + public Point wrap(X value, WrapperOptions options) { + if ( value == null || value instanceof Point ) { + return (Point) value; + } + throw unsupported( value.getClass() ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/PointJdbcType.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/PointJdbcType.java new file mode 100644 index 0000000000..950a4815b5 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/PointJdbcType.java @@ -0,0 +1,94 @@ +/* + * 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 http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.mapping.type.contribution.jts; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; + +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.JdbcType; + +import org.locationtech.jts.geom.Point; + +/** + * @author Steve Ebersole + */ +public class PointJdbcType implements JdbcType { + public static final int POINT_TYPE_CODE = SqlTypes.GEOMETRY; + + @Override + public String getFriendlyName() { + return "POINT"; + } + + @Override + public int getJdbcTypeCode() { + return POINT_TYPE_CODE; + } + + private final ValueBinder binder = new BasicBinder( PointJavaType.INSTANCE, this ) { + @Override + protected void doBindNull(PreparedStatement st, int index, WrapperOptions options) throws SQLException { + // according to documentation, can be treated as character data + st.setNull( index, Types.VARCHAR ); + } + + @Override + protected void doBind(PreparedStatement st, Point value, int index, WrapperOptions options) throws SQLException { + st.setObject( index,value ); + } + + @Override + protected void doBindNull(CallableStatement st, String name, WrapperOptions options) throws SQLException { + // according to documentation, can be treated as character data + st.setNull( name, Types.VARCHAR ); + } + + @Override + protected void doBind(CallableStatement st, Point value, String name, WrapperOptions options) throws SQLException { + st.setObject( name, value ); + } + }; + + private final ValueExtractor extractor = new BasicExtractor( PointJavaType.INSTANCE, this ) { + @Override + protected Point doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException { + return rs.getObject( paramIndex, Point.class ); + } + + @Override + protected Point doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException { + return statement.getObject( index, Point.class ); + } + + @Override + protected Point doExtract(CallableStatement statement, String name, WrapperOptions options) throws SQLException { + return statement.getObject( name, Point.class ); + } + }; + + @Override + public ValueBinder getBinder(JavaType javaTypeDescriptor) { + //noinspection unchecked + return (ValueBinder) binder; + } + + @Override + public ValueExtractor getExtractor(JavaType javaTypeDescriptor) { + //noinspection unchecked + return (ValueExtractor) extractor; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/infrastructure/CustomConnectionProvider.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/infrastructure/CustomConnectionProvider.java new file mode 100644 index 0000000000..0ec04e8f47 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/infrastructure/CustomConnectionProvider.java @@ -0,0 +1,76 @@ +/* + * 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 http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.mapping.type.contribution.jts.infrastructure; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Map; + +import org.hibernate.engine.jdbc.connections.internal.ConnectionProviderInitiator; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.exception.JDBCConnectionException; +import org.hibernate.service.spi.Configurable; +import org.hibernate.service.spi.ServiceRegistryAwareService; +import org.hibernate.service.spi.ServiceRegistryImplementor; +import org.hibernate.service.spi.Stoppable; + +import org.h2gis.utilities.SFSUtilities; + +/** + * @author Steve Ebersole + */ +public class CustomConnectionProvider implements ConnectionProvider, Stoppable { + private final ConnectionProvider delegate; + + public CustomConnectionProvider(Map configurationValues, ServiceRegistryImplementor registry) { + delegate = ConnectionProviderInitiator.INSTANCE.initiateService( configurationValues, registry ); + ( (Configurable) delegate ).configure( configurationValues ); + ( (ServiceRegistryAwareService) delegate ).injectServices( registry ); + } + + @Override + public Connection getConnection() throws SQLException { + final Connection wrappedConnection = SFSUtilities.wrapConnection( delegate.getConnection() ); + try { + final Statement statement = wrappedConnection.createStatement(); + statement.execute( "CREATE ALIAS IF NOT EXISTS H2GIS_SPATIAL FOR \"org.h2gis.functions.factory.H2GISFunctions.load\"" ); + statement.execute( "CALL H2GIS_SPATIAL()" ); + return wrappedConnection; + } + catch (SQLException e) { + throw new JDBCConnectionException( "Error creating H2GIS wrapped Connection", e ); + } + } + + @Override + public void closeConnection(Connection conn) throws SQLException { + delegate.closeConnection( conn ); + } + + @Override + public boolean supportsAggressiveRelease() { + return false; + } + + @Override + public boolean isUnwrappableAs(Class unwrapType) { + return false; + } + + @Override + public T unwrap(Class unwrapType) { + return null; + } + + @Override + public void stop() { + if ( delegate instanceof Stoppable ) { + ( (Stoppable) delegate ).stop(); + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/infrastructure/CustomConnectionProviderInitiator.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/infrastructure/CustomConnectionProviderInitiator.java new file mode 100644 index 0000000000..cbd116e79a --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/infrastructure/CustomConnectionProviderInitiator.java @@ -0,0 +1,28 @@ +/* + * 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 http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.mapping.type.contribution.jts.infrastructure; + +import java.util.Map; + +import org.hibernate.boot.registry.StandardServiceInitiator; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.service.spi.ServiceRegistryImplementor; + +/** + * @author Steve Ebersole + */ +public class CustomConnectionProviderInitiator implements StandardServiceInitiator { + @Override + public ConnectionProvider initiateService(Map configurationValues, ServiceRegistryImplementor registry) { + return new CustomConnectionProvider( configurationValues, registry ); + } + + @Override + public Class getServiceInitiated() { + return ConnectionProvider.class; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/infrastructure/CustomJdbcSelectExecutor.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/infrastructure/CustomJdbcSelectExecutor.java new file mode 100644 index 0000000000..64c3899ca9 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/infrastructure/CustomJdbcSelectExecutor.java @@ -0,0 +1,92 @@ +/* + * 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 http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.mapping.type.contribution.jts.infrastructure; + +import java.util.List; +import java.util.stream.Stream; + +import org.hibernate.ScrollMode; +import org.hibernate.query.spi.ScrollableResultsImplementor; +import org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl; +import org.hibernate.sql.exec.spi.ExecutionContext; +import org.hibernate.sql.exec.spi.JdbcParameterBindings; +import org.hibernate.sql.exec.spi.JdbcSelect; +import org.hibernate.sql.exec.spi.JdbcSelectExecutor; +import org.hibernate.sql.results.spi.ListResultsConsumer; +import org.hibernate.sql.results.spi.RowTransformer; + +/** + * @author Steve Ebersole + */ +public class CustomJdbcSelectExecutor implements JdbcSelectExecutor { + private final JdbcSelectExecutorStandardImpl standardSelectExecutor; + + public CustomJdbcSelectExecutor() { + this.standardSelectExecutor = JdbcSelectExecutorStandardImpl.INSTANCE; + } + + @Override + public List list( + JdbcSelect jdbcSelect, + JdbcParameterBindings jdbcParameterBindings, + ExecutionContext executionContext, + RowTransformer rowTransformer, + ListResultsConsumer.UniqueSemantic uniqueSemantic) { + return standardSelectExecutor.list( + jdbcSelect, + jdbcParameterBindings, + executionContext, + rowTransformer, + uniqueSemantic, + (preparedStatementFunction) -> new CustomResultSetAccess( + jdbcSelect, + jdbcParameterBindings, + executionContext, + preparedStatementFunction + ) + ); + } + + @Override + public ScrollableResultsImplementor scroll + (JdbcSelect jdbcSelect, + ScrollMode scrollMode, + JdbcParameterBindings jdbcParameterBindings, + ExecutionContext executionContext, + RowTransformer rowTransformer) { + return standardSelectExecutor.scroll( + jdbcSelect, + executionContext, + rowTransformer, + (preparedStatementFunction) -> new CustomResultSetAccess( + jdbcSelect, + jdbcParameterBindings, + executionContext, + preparedStatementFunction + ) + ); + } + + @Override + public Stream stream( + JdbcSelect jdbcSelect, + JdbcParameterBindings jdbcParameterBindings, + ExecutionContext executionContext, + RowTransformer rowTransformer) { + return standardSelectExecutor.stream( + jdbcSelect, + executionContext, + rowTransformer, + (preparedStatementFunction) -> new CustomResultSetAccess( + jdbcSelect, + jdbcParameterBindings, + executionContext, + preparedStatementFunction + ) + ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/infrastructure/CustomJdbcServices.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/infrastructure/CustomJdbcServices.java new file mode 100644 index 0000000000..179f950b34 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/infrastructure/CustomJdbcServices.java @@ -0,0 +1,96 @@ +/* + * 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 http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.mapping.type.contribution.jts.infrastructure; + +import java.util.Map; + +import org.hibernate.dialect.Dialect; +import org.hibernate.engine.jdbc.LobCreationContext; +import org.hibernate.engine.jdbc.LobCreator; +import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; +import org.hibernate.engine.jdbc.env.spi.ExtractedDatabaseMetaData; +import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; +import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.engine.jdbc.spi.SqlExceptionHelper; +import org.hibernate.engine.jdbc.spi.SqlStatementLogger; +import org.hibernate.service.spi.Configurable; +import org.hibernate.service.spi.ServiceRegistryAwareService; +import org.hibernate.service.spi.ServiceRegistryImplementor; +import org.hibernate.sql.exec.spi.JdbcMutationExecutor; +import org.hibernate.sql.exec.spi.JdbcSelectExecutor; + +/** + * @author Steve Ebersole + */ +public class CustomJdbcServices implements JdbcServices, ServiceRegistryAwareService, Configurable { + private final JdbcServices delegate; + private final CustomJdbcSelectExecutor selectExecutor; + + public CustomJdbcServices(JdbcServices delegate) { + this.delegate = delegate; + selectExecutor = new CustomJdbcSelectExecutor(); + } + + @Override + public JdbcSelectExecutor getJdbcSelectExecutor() { + return selectExecutor; + } + + @Override + public JdbcEnvironment getJdbcEnvironment() { + return delegate.getJdbcEnvironment(); + } + + @Override + public JdbcConnectionAccess getBootstrapJdbcConnectionAccess() { + return delegate.getBootstrapJdbcConnectionAccess(); + } + + @Override + public Dialect getDialect() { + return delegate.getDialect(); + } + + @Override + public SqlStatementLogger getSqlStatementLogger() { + return delegate.getSqlStatementLogger(); + } + + @Override + public SqlExceptionHelper getSqlExceptionHelper() { + return delegate.getSqlExceptionHelper(); + } + + @Override + public ExtractedDatabaseMetaData getExtractedMetaDataSupport() { + return delegate.getExtractedMetaDataSupport(); + } + + @Override + public LobCreator getLobCreator(LobCreationContext lobCreationContext) { + return delegate.getLobCreator( lobCreationContext ); + } + + @Override + public JdbcMutationExecutor getJdbcMutationExecutor() { + return delegate.getJdbcMutationExecutor(); + } + + @Override + public void configure(Map configurationValues) { + if ( delegate instanceof Configurable ) { + ( (Configurable) delegate ).configure( configurationValues ); + } + } + + @Override + public void injectServices(ServiceRegistryImplementor serviceRegistry) { + if ( delegate instanceof ServiceRegistryAwareService ) { + ( (ServiceRegistryAwareService) delegate ).injectServices( serviceRegistry ); + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/infrastructure/CustomJdbcServicesInitiator.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/infrastructure/CustomJdbcServicesInitiator.java new file mode 100644 index 0000000000..d08598e8fa --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/infrastructure/CustomJdbcServicesInitiator.java @@ -0,0 +1,30 @@ +/* + * 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 http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.mapping.type.contribution.jts.infrastructure; + +import java.util.Map; + +import org.hibernate.boot.registry.StandardServiceInitiator; +import org.hibernate.engine.jdbc.internal.JdbcServicesInitiator; +import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.service.spi.ServiceRegistryImplementor; + +/** + * @author Steve Ebersole + */ +public class CustomJdbcServicesInitiator implements StandardServiceInitiator { + @Override + public JdbcServices initiateService(Map configurationValues, ServiceRegistryImplementor registry) { + final JdbcServices standard = JdbcServicesInitiator.INSTANCE.initiateService( configurationValues, registry ); + return new CustomJdbcServices( standard ); + } + + @Override + public Class getServiceInitiated() { + return JdbcServices.class; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/infrastructure/CustomResultSetAccess.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/infrastructure/CustomResultSetAccess.java new file mode 100644 index 0000000000..1dd66fbc88 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/infrastructure/CustomResultSetAccess.java @@ -0,0 +1,37 @@ +/* + * 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 http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.mapping.type.contribution.jts.infrastructure; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.function.Function; + +import org.hibernate.sql.exec.spi.ExecutionContext; +import org.hibernate.sql.exec.spi.JdbcParameterBindings; +import org.hibernate.sql.exec.spi.JdbcSelect; +import org.hibernate.sql.results.jdbc.internal.DeferredResultSetAccess; + +import org.h2gis.utilities.SpatialResultSet; + +/** + * @author Steve Ebersole + */ +public class CustomResultSetAccess extends DeferredResultSetAccess { + public CustomResultSetAccess( + JdbcSelect jdbcSelect, + JdbcParameterBindings jdbcParameterBindings, + ExecutionContext executionContext, + Function statementCreator) { + super( jdbcSelect, jdbcParameterBindings, executionContext, statementCreator ); + } + + @Override + protected ResultSet wrapResultSet(ResultSet resultSet) throws SQLException { + return resultSet.unwrap( SpatialResultSet.class ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/package-info.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/package-info.java new file mode 100644 index 0000000000..05adc2b4e5 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/jts/package-info.java @@ -0,0 +1,14 @@ +/* + * 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 http://www.gnu.org/licenses/lgpl-2.1.html + */ + +/** + * Verify support for contributing Java and JDBC type extensions using + * H2 JTS support. + * + * Smoke test for hibernate-spatial + */ +package org.hibernate.orm.test.mapping.type.contribution.jts; \ No newline at end of file diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/ServiceRegistry.java b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/ServiceRegistry.java index 45785a7159..dc01a9b235 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/ServiceRegistry.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/ServiceRegistry.java @@ -72,14 +72,27 @@ public @interface ServiceRegistry { Class[] initiators() default {}; Service[] services() default {}; + JavaService[] javaServices() default {}; Setting[] settings() default {}; SettingProvider[] settingProviders() default {}; + /** + * A Hibernate Service registration + */ @interface Service { Class role(); Class impl(); } + /** + * A Java service loadable via {@link java.util.ServiceLoader} + */ + @interface JavaService { + Class role(); + Class[] impls(); + } + + }