From ec97277b9c23abe307180662f043dffe3c6fce1c Mon Sep 17 00:00:00 2001 From: Gavin King Date: Fri, 13 Jun 2025 15:03:58 +0200 Subject: [PATCH 1/2] add two @apiNotes about the role of Type also, Type is definitely not @Internal --- .../src/main/java/org/hibernate/Interceptor.java | 4 ++++ .../src/main/java/org/hibernate/type/Type.java | 10 ++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/Interceptor.java b/hibernate-core/src/main/java/org/hibernate/Interceptor.java index dad428c2dfac..7f266d9d7053 100644 --- a/hibernate-core/src/main/java/org/hibernate/Interceptor.java +++ b/hibernate-core/src/main/java/org/hibernate/Interceptor.java @@ -47,6 +47,10 @@ * However, JPA callbacks do not provide the ability to access the previous * value of an updated property in a {@code @PreUpdate} callback, and do not * provide a well-defined way to intercept changes to collections. + *

+ * Note that this API exposes the interface {@link Type}, which in modern + * versions of Hibernate is considered an SPI. This is unfortunate, and might + * change in the future, but is bearable for now. * * @see SessionBuilder#interceptor(Interceptor) * @see SharedSessionBuilder#interceptor() diff --git a/hibernate-core/src/main/java/org/hibernate/type/Type.java b/hibernate-core/src/main/java/org/hibernate/type/Type.java index 9e60a82c7c00..34353ee33300 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/Type.java +++ b/hibernate-core/src/main/java/org/hibernate/type/Type.java @@ -10,7 +10,6 @@ import java.util.Map; import org.hibernate.HibernateException; -import org.hibernate.Internal; import org.hibernate.MappingException; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; @@ -32,10 +31,17 @@ * An implementation of this interface must certainly be thread-safe. Ideally, it should also be * immutable. * + * @apiNote + * This interface—which has existed since the very earliest days of Hibernate—used + * to be an important API. In modern versions of Hibernate its role has evolved, and it is no + * longer considered an API, though it's still a very important SPI. Application programs should + * avoid direct use of this interface where possible. In many cases, + * {@link org.hibernate.type.descriptor.java.JavaType} or + * {@link org.hibernate.type.descriptor.jdbc.JdbcType} may be used instead. + * * @author Gavin King * @author Steve Ebersole */ -@Internal public interface Type extends Serializable { /** * Return true if the implementation is castable to {@link AssociationType}. This does not From a45b608d31d2d1b037f3a8b48d5d7701a508ec88 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Fri, 13 Jun 2025 15:04:25 +0200 Subject: [PATCH 2/2] minor code cleanups around JdbcParameterBindingsImpl --- .../sql/exec/internal/JdbcCallImpl.java | 31 ++++--- .../JdbcCallRefCursorExtractorImpl.java | 8 +- .../internal/JdbcParameterBindingsImpl.java | 89 ++++++++----------- 3 files changed, 57 insertions(+), 71 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcCallImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcCallImpl.java index f4bec9f30cbe..265a55166593 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcCallImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcCallImpl.java @@ -5,7 +5,6 @@ package org.hibernate.sql.exec.internal; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; @@ -23,6 +22,10 @@ import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducer; +import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; +import static java.util.Collections.unmodifiableList; + /** * Models the actual call, allowing iterative building of the parts. * @@ -42,17 +45,17 @@ public JdbcCallImpl(Builder builder) { builder.callableName, builder.functionReturn, builder.parameterRegistrations == null - ? Collections.emptyList() - : Collections.unmodifiableList( builder.parameterRegistrations ), + ? emptyList() + : unmodifiableList( builder.parameterRegistrations ), builder.parameterBinders == null - ? Collections.emptyList() - : Collections.unmodifiableList( builder.parameterBinders ), + ? emptyList() + : unmodifiableList( builder.parameterBinders ), builder.parameterExtractors == null - ? Collections.emptyList() - : Collections.unmodifiableList( builder.parameterExtractors ), + ? emptyList() + : unmodifiableList( builder.parameterExtractors ), builder.refCursorExtractors == null - ? Collections.emptyList() - : Collections.unmodifiableList( builder.refCursorExtractors ) + ? emptyList() + : unmodifiableList( builder.refCursorExtractors ) ); } @@ -99,12 +102,12 @@ public JdbcCallFunctionReturn getFunctionReturn() { @Override public List getParameterRegistrations() { - return parameterRegistrations == null ? Collections.emptyList() : parameterRegistrations; + return parameterRegistrations == null ? emptyList() : parameterRegistrations; } @Override public List getParameterBinders() { - return parameterBinders == null ? Collections.emptyList() : parameterBinders; + return parameterBinders == null ? emptyList() : parameterBinders; } @Override @@ -119,7 +122,7 @@ public boolean dependsOnParameterBindings() { @Override public Map getAppliedParameters() { - return Collections.emptyMap(); + return emptyMap(); } @Override @@ -130,12 +133,12 @@ public boolean isCompatibleWith( @Override public List getParameterExtractors() { - return parameterExtractors == null ? Collections.emptyList() : parameterExtractors; + return parameterExtractors == null ? emptyList() : parameterExtractors; } @Override public List getCallRefCursorExtractors() { - return refCursorExtractors == null ? Collections.emptyList() : refCursorExtractors; + return refCursorExtractors == null ? emptyList() : refCursorExtractors; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcCallRefCursorExtractorImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcCallRefCursorExtractorImpl.java index f855e02db08f..8fe5a350f358 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcCallRefCursorExtractorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcCallRefCursorExtractorImpl.java @@ -30,10 +30,10 @@ public JdbcCallRefCursorExtractorImpl( public ResultSet extractResultSet( CallableStatement callableStatement, SharedSessionContractImplementor session) { - final boolean supportsNamedParameters = session.getJdbcServices() - .getJdbcEnvironment() - .getExtractedDatabaseMetaData() - .supportsNamedParameters(); +// final boolean supportsNamedParameters = session.getJdbcServices() +// .getJdbcEnvironment() +// .getExtractedDatabaseMetaData() +// .supportsNamedParameters(); return session.getFactory() .getServiceRegistry() .requireService( RefCursorSupport.class ) diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcParameterBindingsImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcParameterBindingsImpl.java index 121735909106..93560dec8d6d 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcParameterBindingsImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcParameterBindingsImpl.java @@ -5,13 +5,11 @@ package org.hibernate.sql.exec.internal; import java.util.Collection; -import java.util.Collections; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.function.BiConsumer; -import org.hibernate.dialect.Dialect; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.metamodel.mapping.BasicValuedMapping; import org.hibernate.metamodel.mapping.JdbcMapping; @@ -19,7 +17,6 @@ import org.hibernate.query.spi.QueryParameterBinding; import org.hibernate.query.spi.QueryParameterBindings; import org.hibernate.query.spi.QueryParameterImplementor; -import org.hibernate.query.sql.internal.NativeQueryImpl; import org.hibernate.query.sql.spi.ParameterOccurrence; import org.hibernate.sql.ast.tree.expression.JdbcParameter; import org.hibernate.sql.exec.spi.JdbcParameterBinder; @@ -28,6 +25,9 @@ import org.hibernate.type.BasicTypeReference; import org.hibernate.type.descriptor.converter.spi.BasicValueConverter; +import static java.util.Collections.emptyList; +import static org.hibernate.query.sql.internal.NativeQueryImpl.determineBindValueMaxCount; + /** * Standard implementation of JdbcParameterBindings * @@ -50,42 +50,22 @@ public JdbcParameterBindingsImpl( if ( !parameterOccurrences.isEmpty() ) { bindingMap = new IdentityHashMap<>( parameterOccurrences.size() ); - final Dialect dialect = factory.getJdbcServices().getDialect(); final boolean paddingEnabled = factory.getSessionFactoryOptions().inClauseParameterPaddingEnabled(); - final int inExprLimit = dialect.getParameterCountLimit(); + final int inExprLimit = factory.getJdbcServices().getDialect().getParameterCountLimit(); for ( ParameterOccurrence occurrence : parameterOccurrences ) { final QueryParameterImplementor param = occurrence.parameter(); final QueryParameterBinding binding = queryParameterBindings.getBinding( param ); - final JdbcMapping jdbcMapping; - - final BindableType type = determineParamType( param, binding ); - if ( type == null ) { - jdbcMapping = factory.getTypeConfiguration().getBasicTypeForJavaType( Object.class ); - } - else if ( type instanceof BasicTypeReference basicTypeReference ) { - jdbcMapping = - factory.getTypeConfiguration().getBasicTypeRegistry() - .resolve( basicTypeReference ); - } - else if ( type instanceof BasicValuedMapping basicValuedMapping ) { - jdbcMapping = basicValuedMapping.getJdbcMapping(); - } - else { - throw new IllegalArgumentException( "Could not resolve NativeQuery parameter type : `" + param + "`"); - } + final JdbcMapping jdbcMapping = jdbcMapping( factory, param, binding ); final BasicValueConverter valueConverter = jdbcMapping == null ? null : jdbcMapping.getValueConverter(); if ( binding.isMultiValued() ) { final Collection bindValues = binding.getBindValues(); final int bindValueCount = bindValues.size(); - final int bindValueMaxCount = NativeQueryImpl.determineBindValueMaxCount( - paddingEnabled, - inExprLimit, - bindValueCount - ); + final int bindValueMaxCount = + determineBindValueMaxCount( paddingEnabled, inExprLimit, bindValueCount ); Object lastBindValue = null; if ( valueConverter != null ) { for ( Object bindValue : bindValues ) { @@ -119,31 +99,38 @@ else if ( type instanceof BasicValuedMapping basicValuedMapping ) { } } else { - final Object bindValue; - if ( valueConverter != null && binding.getBindValue() != null ) { - bindValue = valueConverter.toRelationalValue( binding.getBindValue() ); - } - else { - bindValue = binding.getBindValue(); - } - + final Object bindValue = + valueConverter != null && binding.getBindValue() != null + ? valueConverter.toRelationalValue( binding.getBindValue() ) + : binding.getBindValue(); final JdbcParameterImpl jdbcParameter = new JdbcParameterImpl( jdbcMapping ); jdbcParameterBinders.add( jdbcParameter ); - addBinding( - jdbcParameter, - new JdbcParameterBindingImpl( jdbcMapping, bindValue ) - ); + addBinding( jdbcParameter, new JdbcParameterBindingImpl( jdbcMapping, bindValue ) ); } } } } - private BindableType determineParamType(QueryParameterImplementor param, QueryParameterBinding binding) { - BindableType type = binding.getBindType(); + private JdbcMapping jdbcMapping(SessionFactoryImplementor factory, QueryParameterImplementor param, QueryParameterBinding binding) { + final BindableType type = determineParamType( param, binding ); if ( type == null ) { - type = param.getHibernateType(); + return factory.getTypeConfiguration().getBasicTypeForJavaType( Object.class ); } - return type; + else if ( type instanceof BasicTypeReference basicTypeReference ) { + return factory.getTypeConfiguration().getBasicTypeRegistry() + .resolve( basicTypeReference ); + } + else if ( type instanceof BasicValuedMapping basicValuedMapping ) { + return basicValuedMapping.getJdbcMapping(); + } + else { + throw new IllegalArgumentException( "Could not resolve NativeQuery parameter type : `" + param + "`"); + } + } + + private BindableType determineParamType(QueryParameterImplementor param, QueryParameterBinding binding) { + final BindableType type = binding.getBindType(); + return type == null ? param.getHibernateType() : type; } @Override @@ -157,24 +144,20 @@ public void addBinding(JdbcParameter parameter, JdbcParameterBinding binding) { @Override public Collection getBindings() { - return bindingMap == null ? Collections.emptyList() : bindingMap.values(); + return bindingMap == null ? emptyList() : bindingMap.values(); } @Override public JdbcParameterBinding getBinding(JdbcParameter parameter) { - if ( bindingMap == null ) { - return null; - } - return bindingMap.get( parameter ); + return bindingMap == null ? null : bindingMap.get( parameter ); } @Override public void visitBindings(BiConsumer action) { - if ( bindingMap == null ) { - return; - } - for ( Map.Entry entry : bindingMap.entrySet() ) { - action.accept( entry.getKey(), entry.getValue() ); + if ( bindingMap != null ) { + for ( var entry : bindingMap.entrySet() ) { + action.accept( entry.getKey(), entry.getValue() ); + } } }