diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistenceContext.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistenceContext.java index 44057ed85e..f95776dcb7 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistenceContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistenceContext.java @@ -17,10 +17,10 @@ import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.MappingException; import org.hibernate.collection.spi.PersistentCollection; -import org.hibernate.engine.loading.internal.LoadContexts; import org.hibernate.internal.util.MarkerObject; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.sql.results.spi.LoadContexts; /** * Represents the state of "stuff" Hibernate is tracking, including (not exhaustive): diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AllowableOutputParameterType.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AllowableOutputParameterType.java index 4c1fec79ff..948e430bd6 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AllowableOutputParameterType.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AllowableOutputParameterType.java @@ -10,13 +10,14 @@ import java.sql.CallableStatement; import java.sql.SQLException; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.type.descriptor.sql.SqlTypeDescriptor; /** * Specialization of DomainType for types that can be used as a * parameter output for a {@link org.hibernate.procedure.ProcedureCall} * - * @apiNote Generally speaking, an allowable output parameter type - * can only effectively be a basic type. + * @apiNote We assume a type that maps to exactly one SQL value, hence + * {@link #getSqlTypeDescriptor()} * * @author Steve Ebersole */ @@ -28,6 +29,11 @@ public interface AllowableOutputParameterType extends AllowableParameterType< */ boolean canDoExtraction(); + /** + * Descriptor for the SQL type mapped by this type. + */ + SqlTypeDescriptor getSqlTypeDescriptor(); + /** * Perform the extraction * @@ -46,7 +52,7 @@ public interface AllowableOutputParameterType extends AllowableParameterType< * Perform the extraction * * @param statement The CallableStatement from which to extract the parameter value(s). - * @param paramNames The parameter names. + * @param paramName The parameter names. * @param session The originating session * * @return The extracted value. @@ -54,5 +60,5 @@ public interface AllowableOutputParameterType extends AllowableParameterType< * @throws SQLException Indicates an issue calling into the CallableStatement * @throws IllegalStateException Thrown if this method is called on instances that return {@code false} for {@link #canDoExtraction} */ - J extract(CallableStatement statement, String[] paramNames, SharedSessionContractImplementor session) throws SQLException; + J extract(CallableStatement statement, String paramName, SharedSessionContractImplementor session) throws SQLException; } diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/internal/FunctionReturnImpl.java b/hibernate-core/src/main/java/org/hibernate/procedure/internal/FunctionReturnImpl.java index c223048354..b29e89d37a 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/internal/FunctionReturnImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/internal/FunctionReturnImpl.java @@ -16,6 +16,8 @@ import org.hibernate.metamodel.model.domain.AllowableParameterType; import org.hibernate.procedure.spi.FunctionReturnImplementor; import org.hibernate.procedure.spi.ProcedureCallImplementor; import org.hibernate.query.QueryParameter; +import org.hibernate.type.descriptor.java.JavaTypeDescriptor; +import org.hibernate.type.descriptor.sql.SqlTypeDescriptor; import org.hibernate.type.spi.TypeConfiguration; /** @@ -34,7 +36,7 @@ public class FunctionReturnImpl implements FunctionReturnImplementor { public FunctionReturnImpl(ProcedureCallImplementor procedureCall, AllowableOutputParameterType ormType) { this.procedureCall = procedureCall; - this.jdbcTypeCode = ormType.getSqlTypeDescriptor().getJdbcTypeCode(); + this.jdbcTypeCode = ormType.getSqlTypeDescriptor().getSqlType(); this.ormType = ormType; } @@ -57,7 +59,7 @@ public class FunctionReturnImpl implements FunctionReturnImplementor { .getDescriptor( getJdbcTypeCode() ); final JavaTypeDescriptor javaTypeMapping = sqlTypeDescriptor .getJdbcRecommendedJavaTypeMapping( typeConfiguration ); - ormType = typeConfiguration.getBasicTypeRegistry().getBasicType( javaTypeMapping.getJavaType() ); + ormType = typeConfiguration.standardBasicTypeForJavaType( javaTypeMapping.getJavaType() ); parameterExtractor = new JdbcCallParameterExtractorImpl( procedureCall.getProcedureName(), null, 0, ormType ); refCursorExtractor = null; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcOperation.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcOperation.java new file mode 100644 index 0000000000..ebf710fa25 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcOperation.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.sql.exec.spi; + +import java.util.List; +import java.util.Set; + +/** + * Unifying contract for any SQL statement we want to execute via JDBC. + * + * @author Steve Ebersole + */ +public interface JdbcOperation { + /** + * Get the SQL command we will be executing through JDBC PreparedStatement + * or CallableStatement + */ + String getSql(); + + /** + * Get the list of parameter binders for the generated PreparedStatement + */ + List getParameterBinders(); + + Set getAffectedTableNames(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcParameter.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcParameter.java new file mode 100644 index 0000000000..86bd70c8c5 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcParameter.java @@ -0,0 +1,16 @@ +/* + * 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.sql.exec.spi; + +import org.hibernate.sql.ast.tree.expression.Expression; + +/** + * @author Steve Ebersole + */ +public interface JdbcParameter extends Expression { + JdbcParameterBinder getParameterBinder(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcParameterBinder.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcParameterBinder.java new file mode 100644 index 0000000000..c1a777b0df --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcParameterBinder.java @@ -0,0 +1,27 @@ +/* + * 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.sql.exec.spi; + +import java.sql.PreparedStatement; +import java.sql.SQLException; + +/** + * Performs parameter value binding to a JDBC PreparedStatement. + * + * @author Steve Ebersole + * @author John O'Hara + */ +public interface JdbcParameterBinder { + /** + * Bind the appropriate value in the JDBC statement + */ + void bindParameterValue( + PreparedStatement statement, + int startPosition, + JdbcParameterBindings jdbcParameterBindings, + ExecutionContext executionContext) throws SQLException; +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcParameterBinding.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcParameterBinding.java new file mode 100644 index 0000000000..c2c7963125 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcParameterBinding.java @@ -0,0 +1,17 @@ +/* + * 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.sql.exec.spi; + +import org.hibernate.sql.SqlExpressableType; + +/** + * @author Steve Ebersole + */ +public interface JdbcParameterBinding { + SqlExpressableType getBindType(); + Object getBindValue(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcParameterBindings.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcParameterBindings.java new file mode 100644 index 0000000000..101232dc21 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcParameterBindings.java @@ -0,0 +1,54 @@ +/* + * 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.sql.exec.spi; + +import java.util.Collection; +import java.util.Collections; +import java.util.function.BiConsumer; + +/** + * Access to all of the externalized JDBC parameter bindings + * + * @apiNote "Externalized" because some JDBC parameter values are + * intrinsically part of the parameter itself and we do not need to + * locate a JdbcParameterBinding. E.g., consider a + * {@link org.hibernate.sql.ast.tree.expression.LiteralParameter} + * which actually encapsulates the actually literal value inside + * itself - to create the binder and actually perform the binding + * is only dependent on the LiteralParameter + * + * @author Steve Ebersole + */ +public interface JdbcParameterBindings { + void addBinding(JdbcParameter parameter, JdbcParameterBinding binding); + + Collection getBindings(); + + JdbcParameterBinding getBinding(JdbcParameter parameter); + + void visitBindings(BiConsumer action); + + JdbcParameterBindings NO_BINDINGS = new JdbcParameterBindings() { + @Override + public void addBinding(JdbcParameter parameter, JdbcParameterBinding binding) { + } + + @Override + public Collection getBindings() { + return Collections.emptyList(); + } + + @Override + public JdbcParameterBinding getBinding(JdbcParameter parameter) { + return null; + } + + @Override + public void visitBindings(BiConsumer action) { + } + }; +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcParameters.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcParameters.java new file mode 100644 index 0000000000..09535ad294 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcParameters.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.sql.exec.spi; + +import java.util.Collection; +import java.util.Set; +import java.util.function.Consumer; + +/** + * The collection + * @author Steve Ebersole + */ +public interface JdbcParameters { + void addParameter(JdbcParameter parameter); + void addParameters(Collection parameters); + + Set getJdbcParameters(); + + default void visitJdbcParameters(Consumer jdbcParameterAction) { + for ( JdbcParameter jdbcParameter : getJdbcParameters() ) { + jdbcParameterAction.accept( jdbcParameter ); + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/JdbcValuesSourceProcessingState.java b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/JdbcValuesSourceProcessingState.java index 29a6c434e3..99868fd269 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/JdbcValuesSourceProcessingState.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/JdbcValuesSourceProcessingState.java @@ -6,7 +6,6 @@ */ package org.hibernate.sql.results.spi; -import org.hibernate.engine.loading.internal.LoadingCollectionEntry; import org.hibernate.engine.spi.CollectionKey; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.SharedSessionContractImplementor; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/LoadContexts.java b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/LoadContexts.java new file mode 100644 index 0000000000..d140a02a0d --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/LoadContexts.java @@ -0,0 +1,87 @@ +/* + * 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.sql.results.spi; + +import java.sql.ResultSet; + +import org.hibernate.engine.spi.CollectionKey; +import org.hibernate.engine.spi.EntityKey; +import org.hibernate.engine.spi.PersistenceContext; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.internal.CoreLogging; +import org.hibernate.internal.CoreMessageLogger; +import org.hibernate.internal.util.collections.StandardStack; + +/** + * Maps {@link ResultSet result-sets} to specific contextual data related to processing that result set + *

+ * Considering the JDBC-redesign work, would further like this contextual info not mapped separately, but available + * based on the result set being processed. This would also allow maintaining a single mapping as we could reliably + * get notification of the result-set closing... + * + * @author Steve Ebersole + */ +public class LoadContexts { + private static final CoreMessageLogger log = CoreLogging.messageLogger( LoadContexts.class ); + + private final PersistenceContext persistenceContext; + private final StandardStack jdbcValuesSourceProcessingStateStack = new StandardStack<>(); + + public LoadContexts(PersistenceContext persistenceContext) { + this.persistenceContext = persistenceContext; + } + + public void register(JdbcValuesSourceProcessingState state) { + jdbcValuesSourceProcessingStateStack.push( state ); + } + + public void deregister(JdbcValuesSourceProcessingState state) { + final JdbcValuesSourceProcessingState previous = jdbcValuesSourceProcessingStateStack.pop(); + if ( previous != state ) { + throw new IllegalStateException( "Illegal pop() with non-matching JdbcValuesSourceProcessingState" ); + } + } + + public LoadingEntityEntry findLoadingEntityEntry(EntityKey entityKey) { + return jdbcValuesSourceProcessingStateStack.findCurrentFirst( + state -> state.findLoadingEntityLocally( entityKey ) + ); + } + + public LoadingCollectionEntry findLoadingCollectionEntry(CollectionKey collectionKey) { + return jdbcValuesSourceProcessingStateStack.findCurrentFirst( + state -> state.findLoadingCollectionLocally( collectionKey ) + ); + } + + /** + * Retrieves the persistence context to which this is bound. + * + * @return The persistence context to which this is bound. + */ + public PersistenceContext getPersistenceContext() { + return persistenceContext; + } + + private SharedSessionContractImplementor getSession() { + return getPersistenceContext().getSession(); + } + + + /** + * Release internal state associated with *all* result sets. + *

+ * This is intended as a "failsafe" process to make sure we get everything + * cleaned up and released. + */ + public void cleanup() { + if ( ! jdbcValuesSourceProcessingStateStack.isEmpty() ) { + log.debugf( "LoadContexts still contained JdbcValuesSourceProcessingState registrations on cleanup" ); + } + jdbcValuesSourceProcessingStateStack.clear(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/LoadingCollectionEntry.java b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/LoadingCollectionEntry.java new file mode 100644 index 0000000000..da1e6d4ddb --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/LoadingCollectionEntry.java @@ -0,0 +1,93 @@ +/* + * 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.sql.results.spi; + +import org.hibernate.collection.spi.PersistentCollection; +import org.hibernate.engine.spi.CollectionEntry; +import org.hibernate.engine.spi.PersistenceContext; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.metamodel.model.mapping.PersistentCollectionDescriptor; +import org.hibernate.metamodel.model.mapping.internal.PersistentArrayDescriptorImpl; +import org.hibernate.sql.exec.spi.ExecutionContext; + +/** + * Represents a collection currently being loaded. + * + * @author Steve Ebersole + */ +public class LoadingCollectionEntry { + private final PersistentCollectionDescriptor collectionDescriptor; + private final CollectionInitializer initializer; + private final Object key; + private final PersistentCollection collectionInstance; + + public LoadingCollectionEntry( + PersistentCollectionDescriptor collectionDescriptor, + CollectionInitializer initializer, + Object key, + PersistentCollection collectionInstance) { + this.collectionDescriptor = collectionDescriptor; + this.initializer = initializer; + this.key = key; + this.collectionInstance = collectionInstance; + + collectionInstance.beforeInitialize( -1, getCollectionDescriptor() ); + collectionInstance.beginRead(); + } + + public PersistentCollectionDescriptor getCollectionDescriptor() { + return collectionDescriptor; + } + + /** + * Access to the initializer that is responsible for initializing this collection + */ + public CollectionInitializer getInitializer() { + return initializer; + } + + public Object getKey() { + return key; + } + + public PersistentCollection getCollectionInstance() { + return collectionInstance; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "(" + getCollectionDescriptor().getNavigableRole().getFullPath() + "#" + getKey() + ")"; + } + + public void finishLoading(ExecutionContext executionContext) { + collectionInstance.endRead(); + + final SharedSessionContractImplementor session = executionContext.getSession(); + final PersistenceContext persistenceContext = session.getPersistenceContext(); + + CollectionEntry collectionEntry = persistenceContext.getCollectionEntry( collectionInstance ); + + if ( collectionEntry == null ) { + collectionEntry = persistenceContext.addInitializedCollection( + getCollectionDescriptor(), + getCollectionInstance(), + getKey() + ); + } + else { + collectionEntry.postInitialize( collectionInstance ); + } + + if ( getCollectionDescriptor() instanceof PersistentArrayDescriptorImpl ) { + persistenceContext.addCollectionHolder( collectionInstance ); + } + + + // todo (6.0) : there is other logic still needing to be implemented here. caching, etc + // see org.hibernate.engine.loading.internal.CollectionLoadContext#endLoadingCollection in 5.x + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/type/AbstractStandardBasicType.java b/hibernate-core/src/main/java/org/hibernate/type/AbstractStandardBasicType.java index a2826c4f1b..10199390bd 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/AbstractStandardBasicType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/AbstractStandardBasicType.java @@ -386,10 +386,10 @@ public abstract class AbstractStandardBasicType } @Override - public T extract(CallableStatement statement, String[] paramNames, final SharedSessionContractImplementor session) throws SQLException { + public T extract(CallableStatement statement, String paramName, final SharedSessionContractImplementor session) throws SQLException { return remapSqlTypeDescriptor( session ).getExtractor( javaTypeDescriptor ).extract( statement, - paramNames, + paramName, session ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/ComponentType.java b/hibernate-core/src/main/java/org/hibernate/type/ComponentType.java index 1daea61edf..55e2aa06fa 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/ComponentType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/ComponentType.java @@ -808,7 +808,7 @@ public class ComponentType extends AbstractType implements CompositeType, Proced } @Override - public Object extract(CallableStatement statement, String[] paramNames, SharedSessionContractImplementor session) + public Object extract(CallableStatement statement, String paramName, SharedSessionContractImplementor session) throws SQLException { // for this form to work all sub-property spans must be one (1)... @@ -816,7 +816,7 @@ public class ComponentType extends AbstractType implements CompositeType, Proced int indx = 0; boolean notNull = false; - for ( String paramName : paramNames ) { + for ( String paramName : paramName ) { // we know this cast is safe from canDoExtraction final ProcedureParameterExtractionAware propertyType = (ProcedureParameterExtractionAware) propertyTypes[indx]; final Object value = propertyType.extract( statement, new String[] {paramName}, session ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/CustomType.java b/hibernate-core/src/main/java/org/hibernate/type/CustomType.java index e8c0c36dcb..2c4b696147 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/CustomType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/CustomType.java @@ -326,10 +326,10 @@ public class CustomType } @Override - public Object extract(CallableStatement statement, String[] paramNames, SharedSessionContractImplementor session) + public Object extract(CallableStatement statement, String paramName, SharedSessionContractImplementor session) throws SQLException { if ( canDoExtraction() ) { - return ((ProcedureParameterExtractionAware) getUserType() ).extract( statement, paramNames, session ); + return ((ProcedureParameterExtractionAware) getUserType() ).extract( statement, paramName, session ); } else { throw new UnsupportedOperationException(