Introduce TupleType for modelling structural types
This commit is contained in:
parent
1a3629a571
commit
b4a82f0854
|
@ -43,9 +43,7 @@ import org.hibernate.userguide.model.Phone;
|
|||
import org.hibernate.userguide.model.PhoneType;
|
||||
import org.hibernate.userguide.model.WireTransferPayment;
|
||||
|
||||
import org.hibernate.testing.DialectChecks;
|
||||
import org.hibernate.testing.RequiresDialect;
|
||||
import org.hibernate.testing.RequiresDialectFeature;
|
||||
import org.hibernate.testing.SkipForDialect;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
@ -2285,7 +2283,6 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
|
|||
|
||||
|
||||
@Test
|
||||
@RequiresDialectFeature(DialectChecks.SupportRowValueConstructorSyntaxInInList.class)
|
||||
public void test_hql_in_predicate_example_6() {
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
|
|
|
@ -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.metamodel.model.domain;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.query.sqm.SqmExpressable;
|
||||
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||
|
||||
/**
|
||||
* Describes any structural type without a direct java type representation.
|
||||
*
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public interface TupleType<J> extends SqmExpressable<J> {
|
||||
|
||||
int componentCount();
|
||||
String getComponentName(int index);
|
||||
List<String> getComponentNames();
|
||||
|
||||
SqmExpressable<?> get(int index);
|
||||
SqmExpressable<?> get(String componentName);
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* 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.metamodel.model.domain.internal;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.metamodel.mapping.MappingModelExpressable;
|
||||
import org.hibernate.metamodel.model.domain.AllowableFunctionReturnType;
|
||||
import org.hibernate.metamodel.model.domain.AllowableParameterType;
|
||||
import org.hibernate.metamodel.model.domain.TupleType;
|
||||
import org.hibernate.query.sqm.SqmExpressable;
|
||||
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||
import org.hibernate.type.descriptor.java.ObjectArrayTypeDescriptor;
|
||||
|
||||
/**
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class ArrayTupleType implements TupleType<Object[]>, AllowableParameterType<Object[]>, AllowableFunctionReturnType<Object[]>,
|
||||
MappingModelExpressable {
|
||||
|
||||
private final ObjectArrayTypeDescriptor javaTypeDescriptor;
|
||||
private final SqmExpressable<?>[] components;
|
||||
|
||||
public ArrayTupleType(SqmExpressable<?>[] components) {
|
||||
this.components = components;
|
||||
this.javaTypeDescriptor = new ObjectArrayTypeDescriptor( getTypeDescriptors( components ) );
|
||||
}
|
||||
|
||||
private static JavaTypeDescriptor<?>[] getTypeDescriptors(SqmExpressable<?>[] components) {
|
||||
final JavaTypeDescriptor<?>[] typeDescriptors = new JavaTypeDescriptor<?>[components.length];
|
||||
for ( int i = 0; i < components.length; i++ ) {
|
||||
typeDescriptors[i] = components[i].getExpressableJavaTypeDescriptor();
|
||||
}
|
||||
return typeDescriptors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int componentCount() {
|
||||
return components.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getComponentName(int index) {
|
||||
throw new UnsupportedOperationException( "Array tuple has no component names" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getComponentNames() {
|
||||
throw new UnsupportedOperationException( "Array tuple has no component names" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmExpressable<?> get(int index) {
|
||||
return components[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmExpressable<?> get(String componentName) {
|
||||
throw new UnsupportedOperationException( "Array tuple has no component names" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaTypeDescriptor<Object[]> getExpressableJavaTypeDescriptor() {
|
||||
return javaTypeDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PersistenceType getPersistenceType() {
|
||||
return PersistenceType.EMBEDDABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<Object[]> getJavaType() {
|
||||
return getExpressableJavaTypeDescriptor().getJavaTypeClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ArrayTupleType" + Arrays.toString( components );
|
||||
}
|
||||
}
|
|
@ -55,6 +55,7 @@ import org.hibernate.metamodel.model.domain.EntityDomainType;
|
|||
import org.hibernate.metamodel.model.domain.JpaMetamodel;
|
||||
import org.hibernate.metamodel.model.domain.ManagedDomainType;
|
||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
import org.hibernate.metamodel.model.domain.TupleType;
|
||||
import org.hibernate.metamodel.spi.EntityRepresentationStrategy;
|
||||
import org.hibernate.metamodel.spi.MetamodelImplementor;
|
||||
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
||||
|
@ -151,6 +152,7 @@ public class MappingMetamodelImpl implements MappingMetamodel, MetamodelImplemen
|
|||
private final TypeConfiguration typeConfiguration;
|
||||
|
||||
private final Map<String, String[]> implementorsCache = new ConcurrentHashMap<>();
|
||||
private final Map<TupleType<?>, MappingModelExpressable<?>> tupleTypeCache = new ConcurrentHashMap<>();
|
||||
|
||||
public MappingMetamodelImpl(SessionFactoryImplementor sessionFactory, TypeConfiguration typeConfiguration) {
|
||||
this.sessionFactory = sessionFactory;
|
||||
|
@ -751,6 +753,26 @@ public class MappingMetamodelImpl implements MappingMetamodel, MetamodelImplemen
|
|||
return getEntityDescriptor( ( (EntityDomainType<?>) sqmExpressable ).getHibernateEntityName() );
|
||||
}
|
||||
|
||||
if ( sqmExpressable instanceof TupleType<?> ) {
|
||||
final MappingModelExpressable<?> mappingModelExpressable = tupleTypeCache.get( sqmExpressable );
|
||||
if ( mappingModelExpressable != null ) {
|
||||
return mappingModelExpressable;
|
||||
}
|
||||
final TupleType<?> tupleType = (TupleType<?>) sqmExpressable;
|
||||
final MappingModelExpressable<?>[] components = new MappingModelExpressable<?>[tupleType.componentCount()];
|
||||
for ( int i = 0; i < components.length; i++ ) {
|
||||
components[i] = resolveMappingExpressable( tupleType.get( i ), tableGroupLocator );
|
||||
}
|
||||
final MappingModelExpressable createdMappingModelExpressable = new TupleMappingModelExpressable( components );
|
||||
final MappingModelExpressable<?> existingMappingModelExpressable = tupleTypeCache.putIfAbsent(
|
||||
tupleType,
|
||||
createdMappingModelExpressable
|
||||
);
|
||||
return existingMappingModelExpressable == null
|
||||
? createdMappingModelExpressable
|
||||
: existingMappingModelExpressable;
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException( "Cannot determine proper mapping model expressable for " + sqmExpressable );
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* 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.metamodel.model.domain.internal;
|
||||
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.mapping.IndexedConsumer;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.MappingModelExpressable;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
|
||||
/**
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class TupleMappingModelExpressable implements MappingModelExpressable {
|
||||
|
||||
private final MappingModelExpressable<Object>[] components;
|
||||
|
||||
public TupleMappingModelExpressable(MappingModelExpressable<?>[] components) {
|
||||
this.components = (MappingModelExpressable<Object>[]) components;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int forEachJdbcType(int offset, IndexedConsumer<JdbcMapping> action) {
|
||||
int span = 0;
|
||||
for ( int i = 0; i < components.length; i++ ) {
|
||||
span += components[i].forEachJdbcType( offset + span, action );
|
||||
}
|
||||
return span;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object disassemble(Object value, SharedSessionContractImplementor session) {
|
||||
final Object[] disassembled = new Object[components.length];
|
||||
final Object[] array = (Object[]) value;
|
||||
for ( int i = 0; i < components.length; i++ ) {
|
||||
disassembled[i] = components[i].disassemble( array[i], session );
|
||||
}
|
||||
return disassembled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int forEachDisassembledJdbcValue(
|
||||
Object value,
|
||||
Clause clause,
|
||||
int offset,
|
||||
JdbcValuesConsumer valuesConsumer,
|
||||
SharedSessionContractImplementor session) {
|
||||
final Object[] values = (Object[]) value;
|
||||
int span = 0;
|
||||
for ( int i = 0; i < components.length; i++ ) {
|
||||
span += components[i].forEachDisassembledJdbcValue( values[i], clause, span + offset, valuesConsumer, session );
|
||||
}
|
||||
return span;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int forEachJdbcValue(
|
||||
Object value,
|
||||
Clause clause,
|
||||
int offset,
|
||||
JdbcValuesConsumer valuesConsumer,
|
||||
SharedSessionContractImplementor session) {
|
||||
final Object[] values = (Object[]) value;
|
||||
int span = 0;
|
||||
for ( int i = 0; i < components.length; i++ ) {
|
||||
span += components[i].forEachDisassembledJdbcValue(
|
||||
components[i].disassemble( values[i], session ),
|
||||
clause,
|
||||
span + offset,
|
||||
valuesConsumer,
|
||||
session
|
||||
);
|
||||
}
|
||||
return span;
|
||||
}
|
||||
}
|
|
@ -2025,7 +2025,9 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
final HqlParser.ExplicitTupleInListContext tupleExpressionListContext = (HqlParser.ExplicitTupleInListContext) inListContext;
|
||||
final int size = tupleExpressionListContext.getChildCount();
|
||||
final int estimatedSize = size >> 1;
|
||||
final boolean isEnum = testExpression.getJavaType().isEnum();
|
||||
final Class<?> testExpressionJavaType = testExpression.getJavaType();
|
||||
final boolean isEnum = testExpressionJavaType != null && testExpressionJavaType.isEnum();
|
||||
// Multi-valued bindings are only allowed if there is a single list item, hence size 3 (LP, RP and param)
|
||||
parameterDeclarationContextStack.push( () -> size == 3 );
|
||||
try {
|
||||
final List<SqmExpression<?>> listExpressions = new ArrayList<>( estimatedSize );
|
||||
|
@ -2039,7 +2041,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
resolveEnumShorthandLiteral(
|
||||
expressionContext,
|
||||
possibleEnumValues,
|
||||
testExpression.getJavaType()
|
||||
testExpressionJavaType
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ import org.hibernate.query.sqm.NodeBuilder;
|
|||
import org.hibernate.query.sqm.SemanticQueryWalker;
|
||||
import org.hibernate.query.sqm.SqmExpressable;
|
||||
import org.hibernate.query.sqm.tree.select.SqmJpaCompoundSelection;
|
||||
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||
|
||||
/**
|
||||
* Models a tuple of values, generally defined as a series of values
|
||||
|
@ -38,45 +37,28 @@ public class SqmTuple<T>
|
|||
}
|
||||
|
||||
public SqmTuple(NodeBuilder nodeBuilder, SqmExpressable<T> type, SqmExpression<?>... groupedExpressions) {
|
||||
this( Arrays.asList( groupedExpressions ), nodeBuilder );
|
||||
applyInferableType( type );
|
||||
this( Arrays.asList( groupedExpressions ), type, nodeBuilder );
|
||||
}
|
||||
|
||||
public SqmTuple(List<SqmExpression<?>> groupedExpressions, NodeBuilder nodeBuilder) {
|
||||
super( null, nodeBuilder );
|
||||
this( groupedExpressions, null, nodeBuilder );
|
||||
}
|
||||
|
||||
public SqmTuple(List<SqmExpression<?>> groupedExpressions, SqmExpressable<T> type, NodeBuilder nodeBuilder) {
|
||||
super( type, nodeBuilder );
|
||||
if ( groupedExpressions.isEmpty() ) {
|
||||
throw new QueryException( "tuple grouping cannot be constructed over zero expressions" );
|
||||
}
|
||||
this.groupedExpressions = groupedExpressions;
|
||||
}
|
||||
|
||||
public SqmTuple(List<SqmExpression<?>> groupedExpressions, SqmExpressable<T> type, NodeBuilder nodeBuilder) {
|
||||
this( groupedExpressions, nodeBuilder );
|
||||
applyInferableType( type );
|
||||
if ( type == null ) {
|
||||
setExpressableType( nodeBuilder.getTypeConfiguration().resolveTupleType( groupedExpressions ) );
|
||||
}
|
||||
}
|
||||
|
||||
public List<SqmExpression<?>> getGroupedExpressions() {
|
||||
return groupedExpressions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmExpressable<T> getNodeType() {
|
||||
final SqmExpressable<T> expressableType = super.getNodeType();
|
||||
if ( expressableType != null ) {
|
||||
return expressableType;
|
||||
}
|
||||
|
||||
for ( SqmExpression groupedExpression : groupedExpressions ) {
|
||||
//noinspection unchecked
|
||||
final SqmExpressable<T> groupedExpressionExpressableType = groupedExpression.getNodeType();
|
||||
if ( groupedExpressionExpressableType != null ) {
|
||||
return groupedExpressionExpressableType;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> X accept(SemanticQueryWalker<X> walker) {
|
||||
return walker.visitTuple( this );
|
||||
|
@ -98,11 +80,6 @@ public class SqmTuple<T>
|
|||
return toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaTypeDescriptor<T> getJavaTypeDescriptor() {
|
||||
return getNodeType().getExpressableJavaTypeDescriptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCompoundSelection() {
|
||||
return true;
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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.type.descriptor.java;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* Comparator for component arrays.
|
||||
*
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class ComponentArrayComparator implements Comparator<Object[]> {
|
||||
|
||||
private final JavaTypeDescriptor<Object>[] components;
|
||||
|
||||
public ComponentArrayComparator(JavaTypeDescriptor<Object>[] components) {
|
||||
this.components = components;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(Object[] o1, Object[] o2) {
|
||||
for ( int i = 0; i < components.length; i++ ) {
|
||||
final int cmp = components[i].getComparator().compare( o1[i], o2[i] );
|
||||
if ( cmp != 0 ) {
|
||||
return cmp;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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.type.descriptor.java;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.hibernate.SharedSessionContract;
|
||||
|
||||
/**
|
||||
* Mutability plan for component based arrays.
|
||||
*
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class ComponentArrayMutabilityPlan implements MutabilityPlan<Object[]> {
|
||||
|
||||
private final JavaTypeDescriptor<Object>[] components;
|
||||
|
||||
public ComponentArrayMutabilityPlan(JavaTypeDescriptor<Object>[] components) {
|
||||
this.components = components;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMutable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable disassemble(Object[] value, SharedSessionContract session) {
|
||||
return (Serializable) deepCopy( value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] assemble(Serializable cached, SharedSessionContract session) {
|
||||
return deepCopy( (Object[]) cached );
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Object[] deepCopy(Object[] value) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
if ( value.length != components.length ) {
|
||||
throw new IllegalArgumentException(
|
||||
"Value does not have the expected size " + components.length + ": " + Arrays.toString( value )
|
||||
);
|
||||
}
|
||||
final Object[] copy = new Object[value.length];
|
||||
for ( int i = 0; i < components.length; i++ ) {
|
||||
copy[i] = components[i].getMutabilityPlan().deepCopy( value[i] );
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* 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.type.descriptor.java;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
|
||||
/**
|
||||
* Descriptor for {@code Object[]} handling, usually used for tuples.
|
||||
*
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class ObjectArrayTypeDescriptor extends AbstractClassTypeDescriptor<Object[]> {
|
||||
|
||||
private final JavaTypeDescriptor<Object>[] components;
|
||||
private final Comparator<Object[]> comparator;
|
||||
|
||||
public ObjectArrayTypeDescriptor(JavaTypeDescriptor<?>[] components) {
|
||||
super( Object[].class, ImmutableMutabilityPlan.INSTANCE );
|
||||
this.components = (JavaTypeDescriptor<Object>[]) components;
|
||||
this.comparator = new ComponentArrayComparator( this.components );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(Object[] value) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append( '(' );
|
||||
sb.append( components[0].toString( value[0] ) );
|
||||
for ( int i = 1; i < components.length; i++ ) {
|
||||
sb.append( ", " );
|
||||
sb.append( components[i].toString( value[i] ) );
|
||||
}
|
||||
sb.append( ')' );
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areEqual(Object[] one, Object[] another) {
|
||||
if ( one == another ) {
|
||||
return true;
|
||||
}
|
||||
if ( one != null && another != null && one.length == another.length ) {
|
||||
for ( int i = 0; i < components.length; i++ ) {
|
||||
if ( !components[i].areEqual( one[i], another[i] ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int extractHashCode(Object[] objects) {
|
||||
int hashCode = 1;
|
||||
for ( int i = 0; i < objects.length; i++ ) {
|
||||
hashCode = 31 * hashCode + components[i].extractHashCode( objects[i] );
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Comparator<Object[]> getComparator() {
|
||||
return comparator;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
@Override
|
||||
public <X> X unwrap(Object[] value, Class<X> type, WrapperOptions options) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
if ( Object[].class.isAssignableFrom( type ) ) {
|
||||
return (X) value;
|
||||
}
|
||||
throw unknownUnwrap( type );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> Object[] wrap(X value, WrapperOptions options) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
if ( Object[].class.isInstance( value ) ) {
|
||||
return (Object[]) value;
|
||||
}
|
||||
throw unknownWrap( value.getClass() );
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@ import java.time.LocalDateTime;
|
|||
import java.time.LocalTime;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
@ -24,6 +25,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
|
@ -42,13 +44,16 @@ import org.hibernate.metamodel.mapping.BasicValuedMapping;
|
|||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
||||
import org.hibernate.metamodel.mapping.MappingModelExpressable;
|
||||
import org.hibernate.metamodel.model.domain.internal.ArrayTupleType;
|
||||
import org.hibernate.metamodel.model.domain.internal.MappingMetamodelImpl;
|
||||
import org.hibernate.query.BinaryArithmeticOperator;
|
||||
import org.hibernate.query.internal.QueryHelper;
|
||||
import org.hibernate.query.sqm.SqmExpressable;
|
||||
import org.hibernate.query.sqm.tree.SqmTypedNode;
|
||||
import org.hibernate.service.ServiceRegistry;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.BasicTypeRegistry;
|
||||
import org.hibernate.type.JavaObjectType;
|
||||
import org.hibernate.type.SingleColumnType;
|
||||
import org.hibernate.type.StandardBasicTypes;
|
||||
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||
|
@ -466,6 +471,37 @@ public class TypeConfiguration implements SessionFactoryObserver, Serializable {
|
|||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
private final ConcurrentMap<ArrayCacheKey, ArrayTupleType> arrayTuples = new ConcurrentHashMap<>();
|
||||
|
||||
public SqmExpressable<?> resolveTupleType(List<? extends SqmTypedNode<?>> typedNodes) {
|
||||
final SqmExpressable<?>[] components = new SqmExpressable<?>[typedNodes.size()];
|
||||
for ( int i = 0; i < typedNodes.size(); i++ ) {
|
||||
final SqmExpressable<?> sqmExpressable = typedNodes.get( i ).getNodeType();
|
||||
components[i] = sqmExpressable == null ? JavaObjectType.INSTANCE : sqmExpressable;
|
||||
}
|
||||
return arrayTuples.computeIfAbsent(
|
||||
new ArrayCacheKey( components ),
|
||||
key -> new ArrayTupleType( key.components )
|
||||
);
|
||||
}
|
||||
|
||||
private static class ArrayCacheKey {
|
||||
final SqmExpressable<?>[] components;
|
||||
|
||||
public ArrayCacheKey(SqmExpressable<?>[] components) {
|
||||
this.components = components;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return Arrays.equals( components, ((ArrayCacheKey) o).components );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode( components );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QueryHelper#highestPrecedenceType2
|
||||
|
|
Loading…
Reference in New Issue