Skip to content

Commit

Permalink
HHH-16040 New coercing assembler when types are different from expected
Browse files Browse the repository at this point in the history
  • Loading branch information
mbladel authored and beikov committed Feb 6, 2023
1 parent 29077e6 commit 1064577
Show file tree
Hide file tree
Showing 17 changed files with 375 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@ public Fetch generateFetch(
// Lazy property. A valuesArrayPosition of -1 will lead to
// returning a domain result assembler that returns LazyPropertyInitializer.UNFETCHED_PROPERTY
final EntityMappingType containingEntityMapping = findContainingEntityMapping();
boolean coerceResultType = false;
if ( fetchTiming == FetchTiming.DELAYED
&& !( fetchParent instanceof EmbeddableResultGraphNode )
&& containingEntityMapping.getEntityPersister().getPropertyLaziness()[getStateArrayPosition()] ) {
Expand All @@ -375,6 +376,10 @@ public Fetch generateFetch(

final SqlSelection sqlSelection = resolveSqlSelection( fetchablePath, tableGroup, true, fetchParent, creationState );
valuesArrayPosition = sqlSelection.getValuesArrayPosition();
if ( sqlSelection.getExpressionType() != null) {
// if the expression type is different that the expected type coerce the value
coerceResultType = sqlSelection.getExpressionType().getSingleJdbcMapping().getJdbcJavaType() != getJdbcMapping().getJdbcJavaType();
}
}

return new BasicFetch<>(
Expand All @@ -383,7 +388,8 @@ public Fetch generateFetch(
fetchablePath,
this,
fetchTiming,
creationState
creationState,
coerceResultType
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.model.domain.NavigableRole;
Expand Down Expand Up @@ -399,13 +400,16 @@ public Fetch generateFetch(
assert tableGroup != null;

final SqlSelection sqlSelection = resolveSqlSelection( fetchablePath, tableGroup, false, fetchParent, creationState );
final JdbcMappingContainer selectionType = sqlSelection.getExpressionType();
return new BasicFetch<>(
sqlSelection.getValuesArrayPosition(),
fetchParent,
fetchablePath,
this,
FetchTiming.IMMEDIATE,
creationState
creationState,
// if the expression type is different that the expected type coerce the value
selectionType != null && selectionType.getSingleJdbcMapping().getJdbcJavaType() != getJdbcMapping().getJdbcJavaType()
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.ModelPart;
Expand Down Expand Up @@ -316,17 +317,21 @@ private <T> DomainResult<T> createDomainResult(
);
}

final JavaType<?> javaType = selectableMapping.getJdbcMapping().getJdbcJavaType();
final SqlSelection sqlSelection = sqlExpressionResolver.resolveSqlSelection(
sqlExpressionResolver.resolveSqlExpression( tableReference, selectableMapping ),
selectableMapping.getJdbcMapping().getJdbcJavaType(),
javaType,
fetchParent,
sqlAstCreationState.getCreationContext().getSessionFactory().getTypeConfiguration()
);

final JdbcMappingContainer selectionType = sqlSelection.getExpressionType();
return new BasicResult<>(
sqlSelection.getValuesArrayPosition(),
null,
selectableMapping.getJdbcMapping()
selectableMapping.getJdbcMapping(),
// if the expression type is different that the expected type coerce the value
selectionType != null && selectionType.getSingleJdbcMapping().getJdbcJavaType() != javaType
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,28 @@ public BasicFetch(
);
}

public BasicFetch(
int valuesArrayPosition,
FetchParent fetchParent,
NavigablePath fetchablePath,
BasicValuedModelPart valuedMapping,
FetchTiming fetchTiming,
DomainResultCreationState creationState,
boolean coerceResultType) {
//noinspection unchecked
this(
valuesArrayPosition,
fetchParent,
fetchablePath,
valuedMapping,
(BasicValueConverter<T, ?>) valuedMapping.getJdbcMapping().getValueConverter(),
fetchTiming,
true,
creationState,
coerceResultType
);
}

public BasicFetch(
int valuesArrayPosition,
FetchParent fetchParent,
Expand Down Expand Up @@ -84,6 +106,29 @@ public BasicFetch(
FetchTiming fetchTiming,
boolean canBasicPartFetchBeDelayed,
DomainResultCreationState creationState) {
this(
valuesArrayPosition,
fetchParent,
fetchablePath,
valuedMapping,
valueConverter,
fetchTiming,
canBasicPartFetchBeDelayed,
creationState,
false
);
}

public BasicFetch(
int valuesArrayPosition,
FetchParent fetchParent,
NavigablePath fetchablePath,
BasicValuedModelPart valuedMapping,
BasicValueConverter<T, ?> valueConverter,
FetchTiming fetchTiming,
boolean canBasicPartFetchBeDelayed,
DomainResultCreationState creationState,
boolean coerceResultType) {
this.navigablePath = fetchablePath;

this.fetchParent = fetchParent;
Expand All @@ -100,11 +145,12 @@ public BasicFetch(
}
}
else {
this.assembler = new BasicResultAssembler<>(
valuesArrayPosition,
javaType,
valueConverter
);
if (coerceResultType) {
this.assembler = new CoercingResultAssembler<>( valuesArrayPosition, javaType, valueConverter );
}
else {
this.assembler = new BasicResultAssembler<>( valuesArrayPosition, javaType, valueConverter );
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,22 @@ public BasicResult(
);
}

public BasicResult(
int jdbcValuesArrayPosition,
String resultVariable,
JdbcMapping jdbcMapping,
boolean coerceResultType) {
//noinspection unchecked
this(
jdbcValuesArrayPosition,
resultVariable,
jdbcMapping.getJavaTypeDescriptor(),
jdbcMapping.getValueConverter(),
null,
coerceResultType
);
}

public BasicResult(
int jdbcValuesArrayPosition,
String resultVariable,
Expand Down Expand Up @@ -85,11 +101,26 @@ public BasicResult(
JavaType<T> javaType,
BasicValueConverter<T,?> valueConverter,
NavigablePath navigablePath) {
this( valuesArrayPosition, resultVariable, javaType, valueConverter, navigablePath, false );
}

public BasicResult(
int valuesArrayPosition,
String resultVariable,
JavaType<T> javaType,
BasicValueConverter<T,?> valueConverter,
NavigablePath navigablePath,
boolean coerceResultType) {
this.resultVariable = resultVariable;
this.javaType = javaType;
this.navigablePath = navigablePath;

this.assembler = new BasicResultAssembler<>( valuesArrayPosition, javaType, valueConverter );
if ( coerceResultType ) {
this.assembler = new CoercingResultAssembler<>( valuesArrayPosition, javaType, valueConverter );
}
else {
this.assembler = new BasicResultAssembler<>( valuesArrayPosition, javaType, valueConverter );
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ public static <X> BasicResultAssembler<X> from(SqlSelection selection, JavaType<
return new BasicResultAssembler<>( selection.getValuesArrayPosition(), javaType );
}

private final int valuesArrayPosition;
private final JavaType<J> assembledJavaType;
protected final int valuesArrayPosition;
protected final JavaType<J> assembledJavaType;
private final BasicValueConverter<J,?> valueConverter;

public BasicResultAssembler(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* 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.graph.basic;

import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
import org.hibernate.type.descriptor.java.JavaType;

/**
* A {@link BasicResultAssembler} which does type coercion to handle cases
* where the expression type and the expected result {@link JavaType} are different
* (e.g. same column mapped with differently typed properties).
*
* @author Marco Belladelli
*/
public class CoercingResultAssembler<J> extends BasicResultAssembler<J> {
public CoercingResultAssembler(
int valuesArrayPosition,
JavaType<J> assembledJavaType,
BasicValueConverter<J, ?> valueConverter) {
super( valuesArrayPosition, assembledJavaType, valueConverter );
}

/**
* Access to the row value, coerced to expected type
*/
@Override
public Object extractRawValue(RowProcessingState rowProcessingState) {
return assembledJavaType.coerce(
rowProcessingState.getJdbcValue( valuesArrayPosition ),
rowProcessingState.getSession()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,9 @@ public <X> char[] wrap(X value, WrapperOptions options) {
}
throw unknownWrap( value.getClass() );
}

@Override
public <X> char[] coerce(X value, CoercionContext coercionContext) {
return wrap( value, null );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ public <X> String wrap(X value, WrapperOptions options) {
if (value instanceof String) {
return (String) value;
}
if (value instanceof char[]) {
return new String( (char[]) value );
}
if (value instanceof Reader) {
return DataHelper.extractString( (Reader) value );
}
Expand All @@ -113,4 +116,8 @@ public boolean isWider(JavaType<?> javaType) {
}
}

@Override
public <X> String coerce(X value, CoercionContext coercionContext) {
return wrap( value, null );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import jakarta.persistence.criteria.Root;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;

/**
* @author Marco Belladelli
Expand All @@ -65,7 +66,7 @@ public void setUp(EntityManagerFactoryScope scope) {
term.setLength( 4 );
term.setLanguage( language );
term.setAnyProperty( stringProperty );
term.setSynonyms( new ArrayList<>( List.of( "ciao" ) ) );
term.setSynonyms( new ArrayList<>() );
term.setEmbeddableProperty( new EmbeddableType( "ciao" ) );
Linkage linkage = new Linkage();
linkage.setTerm( term );
Expand Down Expand Up @@ -101,14 +102,7 @@ public void testTreatEntityValue(EntityManagerFactoryScope scope) {

@Test
public void testTreatPluralValue(EntityManagerFactoryScope scope) {
scope.inTransaction( entityManager -> {
try {
testCriteriaTreat( entityManager, "synonyms", List.of( "ciao" ) );
}
catch (Exception e) {
assertEquals( UnsupportedOperationException.class, e.getClass() );
}
} );
scope.inTransaction( entityManager -> testCriteriaTreat( entityManager, "synonyms", null, true ) );
}

@Test
Expand All @@ -129,13 +123,17 @@ public void testTreatAnyValue(EntityManagerFactoryScope scope) {
}

private void testCriteriaTreat(EntityManager entityManager, String property, Object value) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Linkage> criteria = cb.createQuery( Linkage.class );
Root<Linkage> root = criteria.from( Linkage.class );
Path<LocalTerm> asLocalTerm = cb.treat( root.get( "term" ), LocalTerm.class );
Predicate predicate;
if ( value instanceof Collection<?> ) {
predicate = asLocalTerm.get( property ).in( value );
testCriteriaTreat( entityManager, property, value, false );
}

private void testCriteriaTreat(EntityManager entityManager, String property, Object value, boolean plural) {
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<Linkage> criteria = cb.createQuery( Linkage.class );
final Root<Linkage> root = criteria.from( Linkage.class );
final Path<LocalTerm> asLocalTerm = cb.treat( root.get( "term" ), LocalTerm.class );
final Predicate predicate;
if ( plural ) {
predicate = cb.isEmpty( asLocalTerm.get( property ) );
}
else {
predicate = cb.equal( asLocalTerm.get( property ), value );
Expand Down

0 comments on commit 1064577

Please sign in to comment.