Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
*/
package org.hibernate.boot.model.internal;

import java.lang.reflect.ParameterizedType;
import java.util.HashMap;

import org.hibernate.annotations.Parameter;
Expand All @@ -17,7 +16,7 @@
import org.hibernate.resource.beans.spi.ManagedBean;
import org.hibernate.type.BasicType;
import org.hibernate.type.CustomType;
import org.hibernate.type.descriptor.converter.internal.JpaAttributeConverterImpl;
import org.hibernate.type.descriptor.converter.spi.JpaAttributeConverter;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
import org.hibernate.type.descriptor.jdbc.JdbcType;
Expand All @@ -28,18 +27,17 @@

import jakarta.persistence.AttributeConverter;

import static org.hibernate.internal.util.GenericsHelper.extractClass;
import static org.hibernate.internal.util.GenericsHelper.extractParameterizedType;
import static org.hibernate.internal.util.collections.CollectionHelper.mapOfSize;
import static org.hibernate.type.descriptor.converter.internal.ConverterHelper.createJpaAttributeConverter;

/**
* @author Steve Ebersole
*/
public class AnnotationHelper {
public static HashMap<String, String> extractParameterMap(Parameter[] parameters) {
final HashMap<String,String> paramMap = mapOfSize( parameters.length );
for ( int i = 0; i < parameters.length; i++ ) {
paramMap.put( parameters[i].name(), parameters[i].value() );
for ( Parameter parameter : parameters ) {
paramMap.put( parameter.name(), parameter.value() );
}
return paramMap;
}
Expand All @@ -58,33 +56,21 @@ public static <X,Y> JdbcMapping resolveAttributeConverter(
MetadataBuildingContext context) {
final BootstrapContext bootstrapContext = context.getBootstrapContext();
final TypeConfiguration typeConfiguration = bootstrapContext.getTypeConfiguration();

final var bean = bootstrapContext.getManagedBeanRegistry().getBean( type );

final ParameterizedType converterParameterizedType = extractParameterizedType( bean.getBeanClass() );
final Class<?> domainJavaClass = extractClass( converterParameterizedType.getActualTypeArguments()[0] );
final Class<?> relationalJavaClass = extractClass( converterParameterizedType.getActualTypeArguments()[1] );

@SuppressWarnings("unchecked")
final var castBean = (ManagedBean<? extends AttributeConverter<X,Y>>) bean;
final JavaTypeRegistry registry = typeConfiguration.getJavaTypeRegistry();
final JavaType<X> domainJtd = registry.resolveDescriptor( domainJavaClass );
final JavaType<Y> relationalJtd = registry.resolveDescriptor( relationalJavaClass );

final JpaAttributeConverterImpl<X,Y> valueConverter =
new JpaAttributeConverterImpl<>(
(ManagedBean<? extends AttributeConverter<X,Y>>) bean,
registry.resolveDescriptor( bean.getBeanClass() ),
domainJtd,
relationalJtd
);
final JpaAttributeConverter<X, Y> valueConverter = createJpaAttributeConverter( castBean, registry );
return new ConvertedBasicTypeImpl<>(
ConverterDescriptor.TYPE_NAME_PREFIX
+ valueConverter.getConverterJavaType().getTypeName(),
String.format(
"BasicType adapter for AttributeConverter<%s,%s>",
domainJtd.getTypeName(),
relationalJtd.getTypeName()
valueConverter.getDomainJavaType().getTypeName(),
valueConverter.getRelationalJavaType().getTypeName()
),
relationalJtd.getRecommendedJdbcType( typeConfiguration.getCurrentBaseSqlTypeIndicators() ),
registry.<Y>resolveDescriptor( valueConverter.getRelationalJavaType().getJavaType() )
.getRecommendedJdbcType( typeConfiguration.getCurrentBaseSqlTypeIndicators() ),
valueConverter
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public JdbcType getJdbcType() {
@Override @SuppressWarnings("unchecked")
public BasicValueConverter<J,T> getValueConverter() {
return updatedType == null
? jdbcMapping.getValueConverter()
? (BasicValueConverter<J, T>) jdbcMapping.getValueConverter()
: (BasicValueConverter<J, T>) updatedType.getValueConverter();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,25 +102,41 @@ default JavaType<?> getJdbcJavaType() {
* or <code>null</code> if there is no conversion.
*/
@Incubating
default BasicValueConverter getValueConverter() {
default BasicValueConverter<?,?> getValueConverter() {
return null;
}

//TODO: would it be better to just give JdbcMapping a
// noop converter by default, instead of having
// to deal with null here?
@SuppressWarnings({"rawtypes", "unchecked"})
default Object convertToRelationalValue(Object value) {
BasicValueConverter valueConverter = getValueConverter();
return valueConverter == null ? value : valueConverter.toRelationalValue( value );
default <T> Object convertToRelationalValue(T value) {
final var converter = getValueConverter();
if ( converter == null ) {
return value;
}
else {
assert value == null
|| converter.getDomainJavaType().isInstance( value );
@SuppressWarnings( "unchecked" ) // safe, we just checked
final var valueConverter = (BasicValueConverter<T,?>) converter;
return valueConverter.toRelationalValue( value );
}
}

default Object convertToDomainValue(Object value) {
BasicValueConverter valueConverter = getValueConverter();
return valueConverter == null ? value : valueConverter.toDomainValue( value );
default <T> Object convertToDomainValue(T value) {
var converter = getValueConverter();
if ( converter == null ) {
return value;
}
else {
assert value == null
|| converter.getRelationalJavaType().isInstance( value );
@SuppressWarnings( "unchecked" ) // safe, we just checked
final var valueConverter = (BasicValueConverter<?, T>) converter;
return valueConverter.toDomainValue( value );
}
}


@Override
default int getJdbcTypeCount() {
return 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -871,7 +871,7 @@ protected Object[] getAttributeValues(Object compositeInstance) {
? getValue( compositeInstance, i )
: null;
}
results[i] = compositeInstance.getClass().getName();
results[i] = compositeInstance.getClass();
return results;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public DomainResult<T> createDomainResult(
final BasicValueConverter<T, ?> converter;
if ( jdbcMapping != null ) {
jdbcJavaType = (JavaType<T>) jdbcMapping.getJdbcJavaType();
converter = jdbcMapping.getValueConverter();
converter = (BasicValueConverter<T, ?>) jdbcMapping.getValueConverter();
}
else if ( type != null ) {
jdbcJavaType = type.getExpressibleJavaType();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ public BasicResult(
jdbcValuesArrayPosition,
resultVariable,
jdbcMapping.getJavaTypeDescriptor(),
jdbcMapping.getValueConverter(),
(BasicValueConverter<T,?>)
jdbcMapping.getValueConverter(),
navigablePath,
coerceResultType,
unwrapRowProcessingState
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ default MappingType getMappedType() {

@Override
default JavaType<T> getJavaTypeDescriptor() {
return this.getMappedJavaType();
return getMappedJavaType();
}

@Override
Expand Down
49 changes: 24 additions & 25 deletions hibernate-core/src/main/java/org/hibernate/type/CustomType.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import java.util.Arrays;
import java.util.Map;

import jakarta.persistence.AttributeConverter;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.cache.MutableCacheKeyBuilder;
Expand All @@ -33,6 +34,7 @@
import org.hibernate.usertype.UserVersionType;

import static org.hibernate.internal.util.collections.ArrayHelper.EMPTY_STRING_ARRAY;
import static org.hibernate.type.descriptor.converter.internal.ConverterHelper.createValueConverter;

/**
* Adapts {@link UserType} to the generic {@link Type} interface, in order
Expand Down Expand Up @@ -62,22 +64,25 @@ public class CustomType<J>
private final ValueBinder<J> valueBinder;
private final JdbcLiteralFormatter<J> jdbcLiteralFormatter;

private final BasicValueConverter<J, ?> converter;

public CustomType(UserType<J> userType, TypeConfiguration typeConfiguration) throws MappingException {
this( userType, EMPTY_STRING_ARRAY, typeConfiguration );
}

public CustomType(UserType<J> userType, String[] registrationKeys, TypeConfiguration typeConfiguration) {
this.userType = userType;
this.registrationKeys = registrationKeys;
name = userType.getClass().getName();

mappedJavaType = getMappedJavaType( userType );

final BasicValueConverter<J, Object> converter = userType.getValueConverter();
if ( converter != null ) {
final AttributeConverter<J, ?> valueConverter = userType.getValueConverter();
if ( valueConverter != null ) {
converter = createValueConverter( valueConverter, typeConfiguration.getJavaTypeRegistry() );
// When an explicit value converter is given,
// we configure the custom type to use that instead of adapters that delegate to UserType.
// This is necessary to support selecting a column with multiple domain type representations.
jdbcType = userType.getJdbcType( typeConfiguration );
jdbcType = typeConfiguration.getJdbcTypeRegistry().getDescriptor( userType.getSqlType() );
jdbcJavaType = converter.getRelationalJavaType();
//noinspection unchecked
valueExtractor = (ValueExtractor<J>) jdbcType.getExtractor( jdbcJavaType );
Expand All @@ -93,16 +98,17 @@ public CustomType(UserType<J> userType, String[] registrationKeys, TypeConfigura
valueExtractor = jdbcType.getExtractor( mappedJavaType );
valueBinder = jdbcType.getBinder( mappedJavaType );
jdbcLiteralFormatter =
userType instanceof EnhancedUserType ? jdbcType.getJdbcLiteralFormatter( mappedJavaType ) : null;
userType instanceof EnhancedUserType
? jdbcType.getJdbcLiteralFormatter( mappedJavaType )
: null;
converter = null;
}

this.registrationKeys = registrationKeys;
}

private JavaType<J> getMappedJavaType(UserType<J> userType) {
return userType instanceof UserVersionType<J> userVersionType
? new UserTypeVersionJavaTypeWrapper<>( userVersionType )
: new UserTypeJavaTypeWrapper<>( userType );
? new UserTypeVersionJavaTypeWrapper<>( userVersionType, this )
: new UserTypeJavaTypeWrapper<>( userType, this );
}

public UserType<J> getUserType() {
Expand Down Expand Up @@ -166,11 +172,9 @@ public Object assemble(Serializable cached, SharedSessionContractImplementor ses
// we have to handle the fact that it could produce a null value,
// in which case we will try to use a converter for assembling,
// or if that doesn't exist, simply use the relational value as is
if ( assembled == null && cached != null ) {
final BasicValueConverter<J, Object> converter = getUserType().getValueConverter();
return converter == null ? cached : converter.toDomainValue( cached );
}
return assembled;
return assembled == null && cached != null
? convertToDomainValue( cached )
: assembled;
}

@Override
Expand All @@ -189,20 +193,15 @@ private Serializable disassembleForCache(Object value) {
// we have to handle the fact that it could produce a null value,
// in which case we will try to use a converter for disassembling,
// or if that doesn't exist, simply use the domain value as is
if ( disassembled == null ){
final BasicValueConverter<J, Object> converter = getUserType().getValueConverter();
return converter == null ? disassembled : (Serializable) converter.toRelationalValue( (J) value );
}
else {
return disassembled;
}
return disassembled == null
? (Serializable) convertToRelationalValue( (J) value )
: disassembled;
}

@Override
public Object disassemble(Object value, SharedSessionContractImplementor session) {
// Use the value converter if available for conversion to the jdbc representation
final BasicValueConverter<J, Object> converter = getUserType().getValueConverter();
return converter == null ? value : converter.toRelationalValue( (J) value );
return convertToRelationalValue( (J) value );
}

@Override
Expand Down Expand Up @@ -395,8 +394,8 @@ public JavaType<?> getJdbcJavaType() {
}

@Override
public BasicValueConverter<J, Object> getValueConverter() {
return userType.getValueConverter();
public BasicValueConverter<J, ?> getValueConverter() {
return converter;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.type.descriptor.converter.internal;

import jakarta.persistence.AttributeConverter;
import jakarta.persistence.PersistenceException;
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
import org.hibernate.type.descriptor.java.JavaType;

/**
* @author Gavin King
* @since 7.0
*/
public class AttributeConverterWrapper<O,R> implements BasicValueConverter<O,R> {
private final AttributeConverter<O,R> converter;
private final JavaType<? extends AttributeConverter<O, R>> converterJtd;
private final JavaType<O> domainJtd;
private final JavaType<R> jdbcJtd;

public AttributeConverterWrapper(
AttributeConverter<O, R> converter,
JavaType<? extends AttributeConverter<O,R>> converterJtd,
JavaType<O> domainJtd,
JavaType<R> jdbcJtd) {
this.converter = converter;
this.converterJtd = converterJtd;
this.domainJtd = domainJtd;
this.jdbcJtd = jdbcJtd;
}

@Override
public O toDomainValue(R relationalForm) {
try {
return converter.convertToEntityAttribute( relationalForm );
}
catch (PersistenceException pe) {
throw pe;
}
catch (RuntimeException re) {
throw new PersistenceException( "Error attempting to apply AttributeConverter", re );
}
}

@Override
public R toRelationalValue(O domainForm) {
try {
return converter.convertToDatabaseColumn( domainForm );
}
catch (PersistenceException pe) {
throw pe;
}
catch (RuntimeException re) {
throw new PersistenceException( "Error attempting to apply AttributeConverter: " + re.getMessage(), re );
}
}

@Override
public JavaType<O> getDomainJavaType() {
return domainJtd;
}

@Override
public JavaType<R> getRelationalJavaType() {
return jdbcJtd;
}

@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}

AttributeConverterWrapper<?, ?> that = (AttributeConverterWrapper<?, ?>) o;

if ( !converter.equals( that.converter ) ) {
return false;
}
if ( !converterJtd.equals( that.converterJtd ) ) {
return false;
}
if ( !domainJtd.equals( that.domainJtd ) ) {
return false;
}
return jdbcJtd.equals( that.jdbcJtd );
}

@Override
public int hashCode() {
int result = converter.hashCode();
result = 31 * result + converterJtd.hashCode();
result = 31 * result + domainJtd.hashCode();
result = 31 * result + jdbcJtd.hashCode();
return result;
}
}
Loading