HHH-13082 - Support specifying an AttributeConverter class as a @ColumnResult#type
This commit is contained in:
parent
bd784b6e90
commit
38fa2aef75
|
@ -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.boot.model.convert.internal;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
|
||||
import org.hibernate.internal.util.GenericsHelper;
|
||||
|
||||
import jakarta.persistence.AttributeConverter;
|
||||
|
||||
/**
|
||||
* Helpers related to handling converters
|
||||
*/
|
||||
public class ConverterHelper {
|
||||
public static ParameterizedType extractAttributeConverterParameterizedType(Class<? extends AttributeConverter<?,?>> base) {
|
||||
return GenericsHelper.extractParameterizedType( base );
|
||||
}
|
||||
}
|
|
@ -8,20 +8,18 @@ package org.hibernate.cfg;
|
|||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.persistence.AttributeConverter;
|
||||
import jakarta.persistence.Converter;
|
||||
|
||||
import org.hibernate.AnnotationException;
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.boot.AttributeConverterInfo;
|
||||
import org.hibernate.boot.model.convert.internal.ConverterHelper;
|
||||
import org.hibernate.boot.model.convert.internal.InstanceBasedConverterDescriptor;
|
||||
import org.hibernate.boot.model.convert.spi.ConverterDescriptor;
|
||||
import org.hibernate.boot.spi.MetadataBuildingContext;
|
||||
import org.hibernate.internal.util.GenericsHelper;
|
||||
|
||||
/**
|
||||
* Externalized representation of an AttributeConverter
|
||||
|
@ -121,7 +119,7 @@ public class AttributeConverterDefinition implements AttributeConverterInfo {
|
|||
this.autoApply = autoApply;
|
||||
|
||||
final Class attributeConverterClass = attributeConverter.getClass();
|
||||
final ParameterizedType attributeConverterSignature = extractAttributeConverterParameterizedType( attributeConverterClass );
|
||||
final ParameterizedType attributeConverterSignature = ConverterHelper.extractAttributeConverterParameterizedType( attributeConverterClass );
|
||||
if ( attributeConverterSignature == null ) {
|
||||
throw new AssertionFailure(
|
||||
"Could not extract ParameterizedType representation of AttributeConverter definition " +
|
||||
|
@ -142,7 +140,7 @@ public class AttributeConverterDefinition implements AttributeConverterInfo {
|
|||
+ "] specified more than 2 parameterized types"
|
||||
);
|
||||
}
|
||||
entityAttributeType = extractClass( attributeConverterSignature.getActualTypeArguments()[0] );
|
||||
entityAttributeType = GenericsHelper.extractClass( attributeConverterSignature.getActualTypeArguments()[0] );
|
||||
if ( entityAttributeType == null ) {
|
||||
throw new AnnotationException(
|
||||
"Could not determine 'entity attribute' type from given AttributeConverter [" +
|
||||
|
@ -150,7 +148,7 @@ public class AttributeConverterDefinition implements AttributeConverterInfo {
|
|||
);
|
||||
}
|
||||
|
||||
databaseColumnType = extractClass(attributeConverterSignature.getActualTypeArguments()[1]);
|
||||
databaseColumnType = GenericsHelper.extractClass(attributeConverterSignature.getActualTypeArguments()[1]);
|
||||
if ( databaseColumnType == null ) {
|
||||
throw new AnnotationException(
|
||||
"Could not determine 'database column' type from given AttributeConverter [" +
|
||||
|
@ -159,77 +157,6 @@ public class AttributeConverterDefinition implements AttributeConverterInfo {
|
|||
}
|
||||
}
|
||||
|
||||
private ParameterizedType extractAttributeConverterParameterizedType(Type base) {
|
||||
if ( base != null ) {
|
||||
Class clazz = extractClass( base );
|
||||
List<Type> types = new ArrayList<>();
|
||||
types.add( clazz.getGenericSuperclass() );
|
||||
types.addAll( Arrays.asList( clazz.getGenericInterfaces() ) );
|
||||
for ( Type type : types ) {
|
||||
type = resolveType( type, base );
|
||||
if ( ParameterizedType.class.isInstance( type ) ) {
|
||||
final ParameterizedType parameterizedType = (ParameterizedType) type;
|
||||
if ( AttributeConverter.class.equals( parameterizedType.getRawType() ) ) {
|
||||
return parameterizedType;
|
||||
}
|
||||
}
|
||||
ParameterizedType parameterizedType = extractAttributeConverterParameterizedType( type );
|
||||
if ( parameterizedType != null ) {
|
||||
return parameterizedType;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Type resolveType(Type target, Type context) {
|
||||
if ( target instanceof ParameterizedType ) {
|
||||
return resolveParameterizedType( (ParameterizedType) target, context );
|
||||
}
|
||||
else if ( target instanceof TypeVariable ) {
|
||||
return resolveTypeVariable( (TypeVariable) target, (ParameterizedType) context );
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
private static ParameterizedType resolveParameterizedType(final ParameterizedType parameterizedType, Type context) {
|
||||
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
|
||||
|
||||
final Type[] resolvedTypeArguments = new Type[actualTypeArguments.length];
|
||||
for ( int idx = 0; idx < actualTypeArguments.length; idx++ ) {
|
||||
resolvedTypeArguments[idx] = resolveType( actualTypeArguments[idx], context );
|
||||
}
|
||||
return new ParameterizedType() {
|
||||
|
||||
@Override
|
||||
public Type[] getActualTypeArguments() {
|
||||
return resolvedTypeArguments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getRawType() {
|
||||
return parameterizedType.getRawType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getOwnerType() {
|
||||
return parameterizedType.getOwnerType();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
private static Type resolveTypeVariable(TypeVariable typeVariable, ParameterizedType context) {
|
||||
Class clazz = extractClass( context.getRawType() );
|
||||
TypeVariable[] typeParameters = clazz.getTypeParameters();
|
||||
for ( int idx = 0; idx < typeParameters.length; idx++ ) {
|
||||
if ( typeVariable.getName().equals( typeParameters[idx].getName() ) ) {
|
||||
return resolveType( context.getActualTypeArguments()[idx], context );
|
||||
}
|
||||
}
|
||||
return typeVariable;
|
||||
}
|
||||
|
||||
public AttributeConverter getAttributeConverter() {
|
||||
return attributeConverter;
|
||||
}
|
||||
|
@ -246,16 +173,6 @@ public class AttributeConverterDefinition implements AttributeConverterInfo {
|
|||
return databaseColumnType;
|
||||
}
|
||||
|
||||
private static Class extractClass(Type type) {
|
||||
if ( type instanceof Class ) {
|
||||
return (Class) type;
|
||||
}
|
||||
else if ( type instanceof ParameterizedType ) {
|
||||
return extractClass( ( (ParameterizedType) type ).getRawType() );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AttributeConverter> getConverterClass() {
|
||||
return attributeConverter.getClass();
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* 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.internal.util;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.persistence.AttributeConverter;
|
||||
|
||||
public class GenericsHelper {
|
||||
public static ParameterizedType extractParameterizedType(Type base) {
|
||||
if ( base == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Class<?> clazz = extractClass( base );
|
||||
if ( clazz == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final List<Type> types = new ArrayList<>();
|
||||
types.add( clazz.getGenericSuperclass() );
|
||||
types.addAll( Arrays.asList( clazz.getGenericInterfaces() ) );
|
||||
|
||||
for ( Type type : types ) {
|
||||
type = resolveType( type, base );
|
||||
if ( type instanceof ParameterizedType ) {
|
||||
final ParameterizedType parameterizedType = (ParameterizedType) type;
|
||||
if ( AttributeConverter.class.equals( parameterizedType.getRawType() ) ) {
|
||||
return parameterizedType;
|
||||
}
|
||||
}
|
||||
|
||||
final ParameterizedType parameterizedType = extractParameterizedType( type );
|
||||
if ( parameterizedType != null ) {
|
||||
return parameterizedType;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Type resolveTypeVariable(TypeVariable<?> typeVariable, ParameterizedType context) {
|
||||
final Class<?> clazz = extractClass( context.getRawType() );
|
||||
if ( clazz == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final TypeVariable<?>[] typeParameters = clazz.getTypeParameters();
|
||||
for ( int idx = 0; idx < typeParameters.length; idx++ ) {
|
||||
if ( typeVariable.getName().equals( typeParameters[idx].getName() ) ) {
|
||||
return resolveType( context.getActualTypeArguments()[idx], context );
|
||||
}
|
||||
}
|
||||
|
||||
return typeVariable;
|
||||
}
|
||||
|
||||
public static Class<?> extractClass(Type type) {
|
||||
if ( type instanceof Class ) {
|
||||
return (Class<?>) type;
|
||||
}
|
||||
else if ( type instanceof ParameterizedType ) {
|
||||
return extractClass( ( (ParameterizedType) type ).getRawType() );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Type resolveType(Type target, Type context) {
|
||||
if ( target instanceof ParameterizedType ) {
|
||||
return resolveParameterizedType( (ParameterizedType) target, context );
|
||||
}
|
||||
else if ( target instanceof TypeVariable ) {
|
||||
return resolveTypeVariable( (TypeVariable<?>) target, (ParameterizedType) context );
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
private static ParameterizedType resolveParameterizedType(final ParameterizedType parameterizedType, Type context) {
|
||||
final Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
|
||||
|
||||
final Type[] resolvedTypeArguments = new Type[actualTypeArguments.length];
|
||||
for ( int idx = 0; idx < actualTypeArguments.length; idx++ ) {
|
||||
resolvedTypeArguments[idx] = resolveType( actualTypeArguments[idx], context );
|
||||
}
|
||||
return new ParameterizedType() {
|
||||
|
||||
@Override
|
||||
public Type[] getActualTypeArguments() {
|
||||
return resolvedTypeArguments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getRawType() {
|
||||
return parameterizedType.getRawType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getOwnerType() {
|
||||
return parameterizedType.getOwnerType();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
}
|
|
@ -6,22 +6,30 @@
|
|||
*/
|
||||
package org.hibernate.query.internal;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.function.Consumer;
|
||||
import jakarta.persistence.ColumnResult;
|
||||
|
||||
import org.hibernate.boot.model.convert.internal.ConverterHelper;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.BasicValuedMapping;
|
||||
import org.hibernate.query.named.ResultMementoBasic;
|
||||
import org.hibernate.query.results.ResultBuilderBasicValued;
|
||||
import org.hibernate.query.results.complete.CompleteResultBuilderBasicValuedConverted;
|
||||
import org.hibernate.query.results.complete.CompleteResultBuilderBasicValuedStandard;
|
||||
import org.hibernate.resource.beans.spi.ManagedBean;
|
||||
import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.CustomType;
|
||||
import org.hibernate.type.descriptor.java.BasicJavaType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
import org.hibernate.usertype.UserType;
|
||||
|
||||
import jakarta.persistence.AttributeConverter;
|
||||
import jakarta.persistence.ColumnResult;
|
||||
|
||||
/**
|
||||
* Implementation of ResultMappingMemento for scalar (basic) results.
|
||||
*
|
||||
|
@ -50,11 +58,9 @@ import org.hibernate.usertype.UserType;
|
|||
* @author Steve Ebersole
|
||||
*/
|
||||
public class ResultMementoBasicStandard implements ResultMementoBasic {
|
||||
|
||||
public final String explicitColumnName;
|
||||
|
||||
private final BasicType<?> explicitType;
|
||||
private final JavaType<?> explicitJavaTypeDescriptor;
|
||||
private final ResultBuilderBasicValued builder;
|
||||
|
||||
/**
|
||||
* Creation of ScalarResultMappingMemento for JPA descriptor
|
||||
|
@ -64,21 +70,42 @@ public class ResultMementoBasicStandard implements ResultMementoBasic {
|
|||
ResultSetMappingResolutionContext context) {
|
||||
this.explicitColumnName = definition.name();
|
||||
|
||||
BasicType resolvedBasicType = null;
|
||||
|
||||
final Class<?> definedType = definition.type();
|
||||
if ( void.class == definedType ) {
|
||||
explicitJavaTypeDescriptor = null;
|
||||
}
|
||||
else {
|
||||
final SessionFactoryImplementor sessionFactory = context.getSessionFactory();
|
||||
final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration();
|
||||
|
||||
// first see if this is a registered BasicType...
|
||||
final BasicType<Object> registeredBasicType = typeConfiguration.getBasicTypeRegistry()
|
||||
.getRegisteredType( definition.type().getName() );
|
||||
final Class<?> definedType = definition.type();
|
||||
|
||||
if ( void.class == definedType ) {
|
||||
builder = new CompleteResultBuilderBasicValuedStandard( explicitColumnName, null, null );
|
||||
}
|
||||
else if ( AttributeConverter.class.isAssignableFrom( definedType ) ) {
|
||||
final Class<? extends AttributeConverter<?, ?>> converterClass = (Class<? extends AttributeConverter<?, ?>>) definedType;
|
||||
final ManagedBean<? extends AttributeConverter<?,?>> converterBean = sessionFactory.getServiceRegistry()
|
||||
.getService( ManagedBeanRegistry.class )
|
||||
.getBean( converterClass );
|
||||
final JavaType<? extends AttributeConverter<?,?>> converterJtd = typeConfiguration
|
||||
.getJavaTypeDescriptorRegistry()
|
||||
.getDescriptor( converterClass );
|
||||
|
||||
final ParameterizedType parameterizedType = ConverterHelper.extractAttributeConverterParameterizedType( converterBean.getBeanClass() );
|
||||
|
||||
builder = new CompleteResultBuilderBasicValuedConverted(
|
||||
explicitColumnName,
|
||||
converterBean,
|
||||
converterJtd,
|
||||
determineDomainJavaType( parameterizedType, typeConfiguration.getJavaTypeDescriptorRegistry() ),
|
||||
resolveUnderlyingMapping( parameterizedType, typeConfiguration )
|
||||
);
|
||||
}
|
||||
else {
|
||||
final BasicType<?> explicitType;
|
||||
final JavaType<?> explicitJavaTypeDescriptor;
|
||||
|
||||
// see if this is a registered BasicType...
|
||||
final BasicType<Object> registeredBasicType = typeConfiguration.getBasicTypeRegistry().getRegisteredType( definition.type().getName() );
|
||||
if ( registeredBasicType != null ) {
|
||||
this.explicitJavaTypeDescriptor = registeredBasicType.getJavaTypeDescriptor();
|
||||
explicitType = registeredBasicType;
|
||||
explicitJavaTypeDescriptor = registeredBasicType.getJavaTypeDescriptor();
|
||||
}
|
||||
else {
|
||||
final JavaTypeRegistry jtdRegistry = typeConfiguration.getJavaTypeDescriptorRegistry();
|
||||
|
@ -86,22 +113,40 @@ public class ResultMementoBasicStandard implements ResultMementoBasic {
|
|||
final ManagedBeanRegistry beanRegistry = sessionFactory.getServiceRegistry().getService( ManagedBeanRegistry.class );
|
||||
if ( BasicType.class.isAssignableFrom( registeredJtd.getJavaTypeClass() ) ) {
|
||||
final ManagedBean<BasicType<?>> typeBean = (ManagedBean) beanRegistry.getBean( registeredJtd.getJavaTypeClass() );
|
||||
resolvedBasicType = typeBean.getBeanInstance();
|
||||
this.explicitJavaTypeDescriptor = resolvedBasicType.getJavaTypeDescriptor();
|
||||
explicitType = typeBean.getBeanInstance();
|
||||
explicitJavaTypeDescriptor = explicitType.getJavaTypeDescriptor();
|
||||
}
|
||||
else if ( UserType.class.isAssignableFrom( registeredJtd.getJavaTypeClass() ) ) {
|
||||
final ManagedBean<UserType<?>> userTypeBean = (ManagedBean) beanRegistry.getBean( registeredJtd.getJavaTypeClass() );
|
||||
// todo (6.0) : is this the best approach? or should we keep a Class<? extends UserType> -> CustomType mapping somewhere?
|
||||
resolvedBasicType = new CustomType<>( (UserType<Object>) userTypeBean.getBeanInstance(), sessionFactory.getTypeConfiguration() );
|
||||
this.explicitJavaTypeDescriptor = resolvedBasicType.getJavaTypeDescriptor();
|
||||
explicitType = new CustomType<>( (UserType<Object>) userTypeBean.getBeanInstance(), typeConfiguration );
|
||||
explicitJavaTypeDescriptor = explicitType.getJavaTypeDescriptor();
|
||||
}
|
||||
else {
|
||||
this.explicitJavaTypeDescriptor = jtdRegistry.getDescriptor( definition.type() );
|
||||
}
|
||||
explicitType = null;
|
||||
explicitJavaTypeDescriptor = jtdRegistry.getDescriptor( definition.type() );
|
||||
}
|
||||
}
|
||||
|
||||
explicitType = resolvedBasicType;
|
||||
builder = new CompleteResultBuilderBasicValuedStandard( explicitColumnName, explicitType, explicitJavaTypeDescriptor );
|
||||
}
|
||||
}
|
||||
|
||||
private BasicJavaType<?> determineDomainJavaType(
|
||||
ParameterizedType parameterizedType,
|
||||
JavaTypeRegistry jtdRegistry) {
|
||||
final Type[] typeParameters = parameterizedType.getActualTypeArguments();
|
||||
final Type domainTypeType = typeParameters[ 0 ];
|
||||
final Class<?> domainClass = (Class<?>) domainTypeType;
|
||||
|
||||
return (BasicJavaType<?>) jtdRegistry.getDescriptor( domainClass );
|
||||
}
|
||||
|
||||
private BasicValuedMapping resolveUnderlyingMapping(
|
||||
ParameterizedType parameterizedType,
|
||||
TypeConfiguration typeConfiguration) {
|
||||
final Type[] typeParameters = parameterizedType.getActualTypeArguments();
|
||||
return typeConfiguration.standardBasicTypeForJavaType( (Class) typeParameters[ 1 ] );
|
||||
}
|
||||
|
||||
public ResultMementoBasicStandard(
|
||||
|
@ -109,16 +154,19 @@ public class ResultMementoBasicStandard implements ResultMementoBasic {
|
|||
BasicType<?> explicitType,
|
||||
ResultSetMappingResolutionContext context) {
|
||||
this.explicitColumnName = explicitColumnName;
|
||||
this.explicitType = explicitType;
|
||||
this.explicitJavaTypeDescriptor = explicitType != null
|
||||
this.builder = new CompleteResultBuilderBasicValuedStandard(
|
||||
explicitColumnName,
|
||||
explicitType,
|
||||
explicitType != null
|
||||
? explicitType.getJavaTypeDescriptor()
|
||||
: null;
|
||||
: null
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultBuilderBasicValued resolve(
|
||||
Consumer<String> querySpaceConsumer,
|
||||
ResultSetMappingResolutionContext context) {
|
||||
return new CompleteResultBuilderBasicValuedStandard( explicitColumnName, explicitType, explicitJavaTypeDescriptor );
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* 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.query.results.complete;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.BasicValuedMapping;
|
||||
import org.hibernate.metamodel.model.convert.internal.JpaAttributeConverterImpl;
|
||||
import org.hibernate.query.results.DomainResultCreationStateImpl;
|
||||
import org.hibernate.query.results.ResultsHelper;
|
||||
import org.hibernate.query.results.SqlSelectionImpl;
|
||||
import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy;
|
||||
import org.hibernate.resource.beans.spi.ManagedBean;
|
||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||
import org.hibernate.sql.results.graph.basic.BasicResult;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
|
||||
import org.hibernate.type.descriptor.java.BasicJavaType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
||||
import jakarta.persistence.AttributeConverter;
|
||||
|
||||
import static org.hibernate.query.results.ResultsHelper.impl;
|
||||
|
||||
/**
|
||||
* ResultBuilder for scalar results defined via:<ul>
|
||||
* <li>JPA {@link jakarta.persistence.ColumnResult}</li>
|
||||
* <li>`<return-scalar/>` as part of a `<resultset/>` stanza in `hbm.xml`</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class CompleteResultBuilderBasicValuedConverted<O,R> implements CompleteResultBuilderBasicValued {
|
||||
private final String explicitColumnName;
|
||||
private final ManagedBean<? extends AttributeConverter<O, R>> converterBean;
|
||||
private final JavaType<? extends AttributeConverter<O, R>> converterJtd;
|
||||
private final BasicJavaType<O> domainJavaType;
|
||||
private final BasicValuedMapping underlyingMapping;
|
||||
|
||||
public CompleteResultBuilderBasicValuedConverted(
|
||||
String explicitColumnName,
|
||||
ManagedBean<? extends AttributeConverter<O, R>> converterBean,
|
||||
JavaType<? extends AttributeConverter<O, R>> converterJtd,
|
||||
BasicJavaType<O> domainJavaType,
|
||||
BasicValuedMapping underlyingMapping) {
|
||||
this.explicitColumnName = explicitColumnName;
|
||||
this.converterBean = converterBean;
|
||||
this.converterJtd = converterJtd;
|
||||
this.domainJavaType = domainJavaType;
|
||||
this.underlyingMapping = underlyingMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getJavaType() {
|
||||
return domainJavaType.getJavaTypeClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BasicResult<?> buildResult(
|
||||
JdbcValuesMetadata jdbcResultsMetadata,
|
||||
int resultPosition,
|
||||
BiFunction<String, String, DynamicFetchBuilderLegacy> legacyFetchResolver,
|
||||
DomainResultCreationState domainResultCreationState) {
|
||||
final DomainResultCreationStateImpl creationStateImpl = impl( domainResultCreationState );
|
||||
final SessionFactoryImplementor sessionFactory = creationStateImpl.getSessionFactory();
|
||||
|
||||
final String columnName;
|
||||
if ( explicitColumnName != null ) {
|
||||
columnName = explicitColumnName;
|
||||
}
|
||||
else {
|
||||
columnName = jdbcResultsMetadata.resolveColumnName( resultPosition + 1 );
|
||||
}
|
||||
|
||||
|
||||
// final int jdbcPosition;
|
||||
// if ( explicitColumnName != null ) {
|
||||
// jdbcPosition = jdbcResultsMetadata.resolveColumnPosition( explicitColumnName );
|
||||
// }
|
||||
// else {
|
||||
// jdbcPosition = resultPosition + 1;
|
||||
// }
|
||||
//
|
||||
// final BasicValuedMapping basicType;
|
||||
// if ( explicitType != null ) {
|
||||
// basicType = explicitType;
|
||||
// }
|
||||
// else {
|
||||
// basicType = jdbcResultsMetadata.resolveType( jdbcPosition, explicitJavaTypeDescriptor );
|
||||
// }
|
||||
//
|
||||
// final SqlSelection sqlSelection = creationStateImpl.resolveSqlSelection(
|
||||
// creationStateImpl.resolveSqlExpression(
|
||||
// columnName,
|
||||
// processingState -> {
|
||||
// final int valuesArrayPosition = ResultsHelper.jdbcPositionToValuesArrayPosition( jdbcPosition );
|
||||
// return new SqlSelectionImpl( valuesArrayPosition, basicType );
|
||||
// }
|
||||
// ),
|
||||
// basicType.getExpressableJavaTypeDescriptor(),
|
||||
// sessionFactory.getTypeConfiguration()
|
||||
// );
|
||||
|
||||
|
||||
final SqlSelection sqlSelection = creationStateImpl.resolveSqlSelection(
|
||||
creationStateImpl.resolveSqlExpression(
|
||||
columnName,
|
||||
processingState -> {
|
||||
final int jdbcPosition;
|
||||
if ( explicitColumnName != null ) {
|
||||
jdbcPosition = jdbcResultsMetadata.resolveColumnPosition( explicitColumnName );
|
||||
}
|
||||
else {
|
||||
jdbcPosition = resultPosition + 1;
|
||||
}
|
||||
|
||||
final int valuesArrayPosition = ResultsHelper.jdbcPositionToValuesArrayPosition( jdbcPosition );
|
||||
return new SqlSelectionImpl( valuesArrayPosition, underlyingMapping );
|
||||
}
|
||||
),
|
||||
domainJavaType,
|
||||
sessionFactory.getTypeConfiguration()
|
||||
);
|
||||
|
||||
final JpaAttributeConverterImpl<O,R> valueConverter = new JpaAttributeConverterImpl<>(
|
||||
converterBean,
|
||||
converterJtd,
|
||||
domainJavaType,
|
||||
underlyingMapping.getJdbcMapping().getJavaTypeDescriptor()
|
||||
);
|
||||
|
||||
return new BasicResult<>(
|
||||
sqlSelection.getValuesArrayPosition(),
|
||||
columnName,
|
||||
domainJavaType,
|
||||
valueConverter
|
||||
);
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ import java.util.function.BiFunction;
|
|||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.BasicValuedMapping;
|
||||
import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
|
||||
import org.hibernate.query.results.DomainResultCreationStateImpl;
|
||||
import org.hibernate.query.results.ResultsHelper;
|
||||
import org.hibernate.query.results.SqlSelectionImpl;
|
||||
|
|
|
@ -6,18 +6,23 @@
|
|||
*/
|
||||
package org.hibernate.orm.test.mapping.converted.converter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.persistence.AttributeConverter;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.ColumnResult;
|
||||
import jakarta.persistence.Convert;
|
||||
import jakarta.persistence.Converter;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.persistence.Embedded;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.SqlResultSetMapping;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.query.sql.spi.NativeQueryImplementor;
|
||||
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.After;
|
||||
|
@ -25,6 +30,7 @@ import org.junit.Before;
|
|||
import org.junit.Test;
|
||||
|
||||
import static junit.framework.Assert.assertNotNull;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
/**
|
||||
|
@ -56,6 +62,20 @@ public class QueryTest extends BaseNonConfigCoreFunctionalTestCase {
|
|||
session.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNativeQueryResult() {
|
||||
inTransaction( (session) -> {
|
||||
final NativeQueryImplementor<Object[]> query = session.createNativeQuery( "select id, salary from EMP", "emp_id_salary" );
|
||||
|
||||
final List<Object[]> results = query.list();
|
||||
assertThat( results ).hasSize( 1 );
|
||||
|
||||
final Object[] values = results.get( 0 );
|
||||
assertThat( values[0] ).isEqualTo( 1 );
|
||||
assertThat( values[1] ).isEqualTo( SALARY );
|
||||
} );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] { Employee.class, SalaryConverter.class };
|
||||
|
@ -81,6 +101,13 @@ public class QueryTest extends BaseNonConfigCoreFunctionalTestCase {
|
|||
|
||||
@Entity( name = "Employee" )
|
||||
@Table( name = "EMP" )
|
||||
@SqlResultSetMapping(
|
||||
name = "emp_id_salary",
|
||||
columns = {
|
||||
@ColumnResult( name = "id" ),
|
||||
@ColumnResult( name = "salary", type = SalaryConverter.class )
|
||||
}
|
||||
)
|
||||
public static class Employee {
|
||||
@Id
|
||||
public Integer id;
|
||||
|
|
Loading…
Reference in New Issue