6 - SQM based on JPA type system

- further work on `org.hibernate.query` (especially `NamedQueryRepository` and friends)
- initial work on `org.hibernate.sql.exec`
- initial work on `org.hibernate.sql.results`
- SemanticPathPart handling
- NamedQueryMemento
- work on ProcedureCall
- continued work on `org.hibernate.sql.exec`
- continued work on `org.hibernate.sql.results`
This commit is contained in:
Steve Ebersole 2019-06-04 10:28:24 -05:00 committed by Andrea Boriero
parent 638c217e8a
commit d6428c5b43
15 changed files with 373 additions and 14 deletions

View File

@ -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):

View File

@ -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<J> 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<J> 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<J> 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;
}

View File

@ -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;
}

View File

@ -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<JdbcParameterBinder> getParameterBinders();
Set<String> getAffectedTableNames();
}

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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<JdbcParameterBinding> getBindings();
JdbcParameterBinding getBinding(JdbcParameter parameter);
void visitBindings(BiConsumer<JdbcParameter, JdbcParameterBinding> action);
JdbcParameterBindings NO_BINDINGS = new JdbcParameterBindings() {
@Override
public void addBinding(JdbcParameter parameter, JdbcParameterBinding binding) {
}
@Override
public Collection<JdbcParameterBinding> getBindings() {
return Collections.emptyList();
}
@Override
public JdbcParameterBinding getBinding(JdbcParameter parameter) {
return null;
}
@Override
public void visitBindings(BiConsumer<JdbcParameter, JdbcParameterBinding> action) {
}
};
}

View File

@ -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<JdbcParameter> parameters);
Set<JdbcParameter> getJdbcParameters();
default void visitJdbcParameters(Consumer<JdbcParameter> jdbcParameterAction) {
for ( JdbcParameter jdbcParameter : getJdbcParameters() ) {
jdbcParameterAction.accept( jdbcParameter );
}
}
}

View File

@ -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;

View File

@ -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
* <p/>
* 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<JdbcValuesSourceProcessingState> 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.
* <p/>
* 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();
}
}

View File

@ -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
}
}

View File

@ -386,10 +386,10 @@ public abstract class AbstractStandardBasicType<T>
}
@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
);
}

View File

@ -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 );

View File

@ -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(