parent
671250afa6
commit
5f7c139f7e
|
@ -15,6 +15,9 @@ Document in release-notes:
|
|||
* `NativeQuery#addScalar(Class)`
|
||||
* `NativeQuery#addScalar(Class,AttributeConverter)`
|
||||
* `NativeQuery#addScalar(Class,Class<AttributeConverter>)`
|
||||
* `NativeQuery#addAttributeResult(String,Class,String)`
|
||||
* `NativeQuery#addAttributeResult(String,String,String)`
|
||||
* `NativeQuery#addAttributeResult(String,Attribute)`
|
||||
|
||||
|
||||
== Changes
|
||||
|
|
|
@ -536,7 +536,12 @@ public class MappingMetamodelImpl implements MappingMetamodel, MetamodelImplemen
|
|||
|
||||
@Override
|
||||
public String getImportedName(String name) {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
// we have to go back through TypeConfiguration / SessionFactory to get to the JpaMetamodel :(
|
||||
final String qualifiedName = typeConfiguration.getSessionFactory()
|
||||
.getRuntimeMetamodels()
|
||||
.getJpaMetamodel()
|
||||
.qualifyImportableName( name );
|
||||
return qualifiedName == null ? name : qualifiedName;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -19,6 +19,7 @@ import javax.persistence.FlushModeType;
|
|||
import javax.persistence.LockModeType;
|
||||
import javax.persistence.Parameter;
|
||||
import javax.persistence.TemporalType;
|
||||
import javax.persistence.metamodel.SingularAttribute;
|
||||
|
||||
import org.hibernate.CacheMode;
|
||||
import org.hibernate.FlushMode;
|
||||
|
@ -131,6 +132,41 @@ public interface NativeQuery<T> extends Query<T>, SynchronizeableQuery {
|
|||
*/
|
||||
<C> NativeQuery<T> addScalar(String columnAlias, Class<C> relationalJavaType, Class<? extends AttributeConverter<?,C>> converter);
|
||||
|
||||
/**
|
||||
* Defines a result based on a specified attribute. Differs from adding a scalar in that
|
||||
* any conversions or other semantics defined on the attribute are automatically applied
|
||||
* to the mapping
|
||||
*
|
||||
* @return {@code this}, for method chaining
|
||||
*
|
||||
* @since 6.0
|
||||
*/
|
||||
NativeQuery<T> addAttributeResult(String columnAlias, Class<?> entityJavaType, String attributePath);
|
||||
|
||||
/**
|
||||
* Defines a result based on a specified attribute. Differs from adding a scalar in that
|
||||
* any conversions or other semantics defined on the attribute are automatically applied
|
||||
* to the mapping
|
||||
*
|
||||
* @return {@code this}, for method chaining
|
||||
*
|
||||
* @since 6.0
|
||||
*/
|
||||
NativeQuery<T> addAttributeResult(String columnAlias, String entityName, String attributePath);
|
||||
|
||||
/**
|
||||
* Defines a result based on a specified attribute. Differs from adding a scalar in that
|
||||
* any conversions or other semantics defined on the attribute are automatically applied
|
||||
* to the mapping.
|
||||
*
|
||||
* This form accepts the JPA Attribute mapping describing the attribute
|
||||
*
|
||||
* @return {@code this}, for method chaining
|
||||
*
|
||||
* @since 6.0
|
||||
*/
|
||||
NativeQuery<T> addAttributeResult(String columnAlias, SingularAttribute<?,?> attribute);
|
||||
|
||||
/**
|
||||
* Add a new root return mapping, returning a {@link RootReturn} to allow
|
||||
* further definition.
|
||||
|
|
|
@ -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.query.results;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.BasicValuedSingularAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||
import org.hibernate.sql.results.graph.DomainResult;
|
||||
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||
import org.hibernate.sql.results.graph.Fetch;
|
||||
import org.hibernate.sql.results.graph.FetchParent;
|
||||
import org.hibernate.sql.results.graph.basic.BasicResult;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class AttributeResultBuilder implements ResultBuilder {
|
||||
private final BasicValuedSingularAttributeMapping attributeMapping;
|
||||
private final String columnAlias;
|
||||
private final String entityName;
|
||||
private final String attributePath;
|
||||
|
||||
public AttributeResultBuilder(
|
||||
SingularAttributeMapping attributeMapping,
|
||||
String columnAlias,
|
||||
String entityName,
|
||||
String attributePath) {
|
||||
final boolean allowable = attributeMapping instanceof BasicValuedSingularAttributeMapping;
|
||||
if ( !allowable ) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
Locale.ROOT,
|
||||
"Specified attribute [%s.%s] must be basic: %s",
|
||||
entityName,
|
||||
attributePath,
|
||||
attributeMapping
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
this.attributeMapping = (BasicValuedSingularAttributeMapping) attributeMapping;
|
||||
this.columnAlias = columnAlias;
|
||||
this.entityName = entityName;
|
||||
this.attributePath = attributePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResult<?> buildReturn(
|
||||
JdbcValuesMetadata jdbcResultsMetadata,
|
||||
BiFunction<String, String, LegacyFetchBuilder> legacyFetchResolver,
|
||||
Consumer<SqlSelection> sqlSelectionConsumer,
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
final int resultSetPosition = jdbcResultsMetadata.resolveColumnPosition( columnAlias );
|
||||
final int valuesArrayPosition = resultSetPosition - 1;
|
||||
|
||||
final SqlSelectionImpl sqlSelection = new SqlSelectionImpl( valuesArrayPosition, attributeMapping );
|
||||
sqlSelectionConsumer.accept( sqlSelection );
|
||||
|
||||
return new BasicResult<>(
|
||||
valuesArrayPosition,
|
||||
columnAlias,
|
||||
attributeMapping.getJavaTypeDescriptor(),
|
||||
attributeMapping.getValueConverter()
|
||||
);
|
||||
}
|
||||
}
|
|
@ -6,10 +6,18 @@
|
|||
*/
|
||||
package org.hibernate.query.results;
|
||||
|
||||
import java.util.Locale;
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.metamodel.EntityType;
|
||||
import javax.persistence.metamodel.SingularAttribute;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.RuntimeMetamodels;
|
||||
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||
|
||||
|
@ -64,10 +72,92 @@ public class Builders {
|
|||
throw new NotYetImplementedFor6Exception();
|
||||
}
|
||||
|
||||
public static ResultBuilder attributeResult(
|
||||
String columnAlias,
|
||||
String entityName,
|
||||
String attributePath,
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
if ( attributePath.contains( "." ) ) {
|
||||
throw new NotYetImplementedFor6Exception(
|
||||
"Support for defining a NativeQuery attribute result based on a composite path is not yet implemented"
|
||||
);
|
||||
}
|
||||
|
||||
final RuntimeMetamodels runtimeMetamodels = sessionFactory.getRuntimeMetamodels();
|
||||
final String fullEntityName = runtimeMetamodels.getMappingMetamodel().getImportedName( entityName );
|
||||
final EntityPersister entityMapping = runtimeMetamodels.getMappingMetamodel().findEntityDescriptor( fullEntityName );
|
||||
if ( entityMapping == null ) {
|
||||
throw new IllegalArgumentException( "Could not locate entity mapping : " + fullEntityName );
|
||||
}
|
||||
|
||||
final AttributeMapping attributeMapping = entityMapping.findAttributeMapping( attributePath );
|
||||
if ( attributeMapping == null ) {
|
||||
throw new IllegalArgumentException( "Could not locate attribute mapping : " + fullEntityName + "." + attributePath );
|
||||
}
|
||||
|
||||
if ( attributeMapping instanceof SingularAttributeMapping ) {
|
||||
final SingularAttributeMapping singularAttributeMapping = (SingularAttributeMapping) attributeMapping;
|
||||
return new AttributeResultBuilder( singularAttributeMapping, columnAlias, fullEntityName, attributePath );
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
Locale.ROOT,
|
||||
"Specified attribute mapping [%s.%s] not a basic attribute: %s",
|
||||
fullEntityName,
|
||||
attributePath,
|
||||
attributeMapping
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static ResultBuilder attributeResult(String columnAlias, SingularAttribute<?, ?> attribute) {
|
||||
if ( ! ( attribute.getDeclaringType() instanceof EntityType ) ) {
|
||||
throw new NotYetImplementedFor6Exception(
|
||||
"Support for defining a NativeQuery attribute result based on a composite path is not yet implemented"
|
||||
);
|
||||
}
|
||||
|
||||
throw new NotYetImplementedFor6Exception();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a EntityResultBuilder allowing for further configuring of the mapping.
|
||||
*
|
||||
* @param tableAlias
|
||||
* @param entityName
|
||||
* @return
|
||||
*/
|
||||
public static EntityResultBuilder entity(String tableAlias, String entityName) {
|
||||
throw new NotYetImplementedFor6Exception( );
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a EntityResultBuilder that does not allow any further configuring of the mapping.
|
||||
*
|
||||
* @see org.hibernate.query.NativeQuery#addEntity(Class)
|
||||
* @see org.hibernate.query.NativeQuery#addEntity(String)
|
||||
* @see org.hibernate.query.NativeQuery#addEntity(String, Class)
|
||||
* @see org.hibernate.query.NativeQuery#addEntity(String, String)
|
||||
*/
|
||||
public static CalculatedEntityResultBuilder entityCalculated(String tableAlias, String entityName) {
|
||||
return entityCalculated( tableAlias, entityName, null );
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a EntityResultBuilder that does not allow any further configuring of the mapping.
|
||||
*
|
||||
* @see #entityCalculated(String, String)
|
||||
* @see org.hibernate.query.NativeQuery#addEntity(String, Class, LockMode)
|
||||
* @see org.hibernate.query.NativeQuery#addEntity(String, String, LockMode)
|
||||
*/
|
||||
public static CalculatedEntityResultBuilder entityCalculated(
|
||||
String tableAlias,
|
||||
String entityName,
|
||||
LockMode explicitLockMode) {
|
||||
return new CalculatedEntityResultBuilder( tableAlias, entityName, explicitLockMode );
|
||||
}
|
||||
|
||||
public static LegacyFetchBuilder fetch(String tableAlias, String ownerTableAlias, String joinPropertyName) {
|
||||
throw new NotYetImplementedFor6Exception( );
|
||||
}
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||
import org.hibernate.sql.results.graph.DomainResult;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
|
||||
|
||||
/**
|
||||
* An entity DomainResult builder for cases when Hibernate implicitly
|
||||
* calculates the mapping
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class CalculatedEntityResultBuilder implements ResultBuilder {
|
||||
private final String tableAlias;
|
||||
private final String entityName;
|
||||
private final LockMode explicitLockMode;
|
||||
|
||||
public CalculatedEntityResultBuilder(
|
||||
String tableAlias,
|
||||
String entityName,
|
||||
LockMode explicitLockMode) {
|
||||
this.tableAlias = tableAlias;
|
||||
this.entityName = entityName;
|
||||
this.explicitLockMode = explicitLockMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResult<?> buildReturn(
|
||||
JdbcValuesMetadata jdbcResultsMetadata,
|
||||
BiFunction<String, String, LegacyFetchBuilder> legacyFetchResolver,
|
||||
Consumer<SqlSelection> sqlSelectionConsumer,
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@ import javax.persistence.LockModeType;
|
|||
import javax.persistence.Parameter;
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.persistence.TemporalType;
|
||||
import javax.persistence.metamodel.SingularAttribute;
|
||||
|
||||
import org.hibernate.CacheMode;
|
||||
import org.hibernate.FlushMode;
|
||||
|
@ -511,6 +512,30 @@ public class NativeQueryImpl<R>
|
|||
return registerBuilder( Builders.scalar( columnAlias, relationalJavaType, converter, getSessionFactory() ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public NativeQueryImplementor<R> addAttributeResult(
|
||||
String columnAlias,
|
||||
Class<?> entityJavaType,
|
||||
String attributePath) {
|
||||
return addAttributeResult( columnAlias, entityJavaType.getName(), attributePath );
|
||||
}
|
||||
|
||||
@Override
|
||||
public NativeQueryImplementor<R> addAttributeResult(
|
||||
String columnAlias,
|
||||
String entityName,
|
||||
String attributePath) {
|
||||
registerBuilder( Builders.attributeResult( columnAlias, entityName, attributePath, getSessionFactory() ) );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NativeQueryImplementor<R> addAttributeResult(
|
||||
String columnAlias,
|
||||
SingularAttribute<?, ?> attribute) {
|
||||
registerBuilder( Builders.attributeResult( columnAlias, attribute ) );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityResultBuilder addRoot(String tableAlias, String entityName) {
|
||||
|
@ -534,13 +559,13 @@ public class NativeQueryImpl<R>
|
|||
|
||||
@Override
|
||||
public NativeQueryImplementor<R> addEntity(String tableAlias, String entityName) {
|
||||
addRoot( tableAlias, entityName );
|
||||
registerBuilder( Builders.entityCalculated( tableAlias, entityName ) );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NativeQueryImplementor<R> addEntity(String tableAlias, String entityName, LockMode lockMode) {
|
||||
addRoot( tableAlias, entityName ).setLockMode( lockMode );
|
||||
registerBuilder( Builders.entityCalculated( tableAlias, entityName, lockMode ) );
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import javax.persistence.FlushModeType;
|
|||
import javax.persistence.LockModeType;
|
||||
import javax.persistence.Parameter;
|
||||
import javax.persistence.TemporalType;
|
||||
import javax.persistence.metamodel.SingularAttribute;
|
||||
|
||||
import org.hibernate.CacheMode;
|
||||
import org.hibernate.FlushMode;
|
||||
|
@ -65,6 +66,15 @@ public interface NativeQueryImplementor<R> extends QueryImplementor<R>, NativeQu
|
|||
@Override
|
||||
<C> NativeQueryImplementor<R> addScalar(String columnAlias, Class<C> relationalJavaType, Class<? extends AttributeConverter<?,C>> converter);
|
||||
|
||||
@Override
|
||||
NativeQueryImplementor<R> addAttributeResult(String columnAlias, Class<?> entityJavaType, String attributePath);
|
||||
|
||||
@Override
|
||||
NativeQueryImplementor<R> addAttributeResult(String columnAlias, String entityName, String attributePath);
|
||||
|
||||
@Override
|
||||
NativeQueryImplementor<R> addAttributeResult(String columnAlias, SingularAttribute<?, ?> attribute);
|
||||
|
||||
@Override
|
||||
EntityResultBuilder addRoot(String tableAlias, String entityName);
|
||||
|
||||
|
|
|
@ -83,7 +83,6 @@ public class EntityGraphNativeQueryTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@FailureExpected( reason = "Uses an implicit entity/root return, which is not yet implemented" )
|
||||
void testNativeQueryLoadGraph(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
|
@ -115,7 +114,6 @@ public class EntityGraphNativeQueryTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@FailureExpected( reason = "Uses an implicit entity/root return, which is not yet implemented" )
|
||||
void testNativeQueryFetchGraph(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
|
|
|
@ -17,13 +17,11 @@ import org.hibernate.metamodel.mapping.ModelPart;
|
|||
import org.hibernate.metamodel.mapping.internal.BasicValuedSingularAttributeMapping;
|
||||
import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
|
||||
import org.hibernate.metamodel.model.convert.spi.JpaAttributeConverter;
|
||||
import org.hibernate.orm.test.metamodel.mapping.SmokeTests;
|
||||
import org.hibernate.query.sql.spi.NativeQueryImplementor;
|
||||
|
||||
import org.hibernate.testing.orm.domain.StandardDomainModel;
|
||||
import org.hibernate.testing.orm.domain.gambit.EntityOfBasics;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.DomainModelScope;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
|
@ -47,7 +45,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
|
|||
standardModels = StandardDomainModel.GAMBIT
|
||||
)
|
||||
@SessionFactory
|
||||
public class NativeQueryScalarTests {
|
||||
public class NativeQueryResultBuilderTests {
|
||||
public static final String STRING_VALUE = "a string value";
|
||||
public static final String URL_STRING = "http://hibernate.org";
|
||||
|
||||
|
@ -220,6 +218,31 @@ public class NativeQueryScalarTests {
|
|||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertedAttributeBasedBuilder(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final NativeQueryImplementor qry = session.createNativeQuery(
|
||||
"select converted_gender from EntityOfBasics"
|
||||
);
|
||||
|
||||
qry.addAttributeResult(
|
||||
"converted_gender",
|
||||
"EntityOfBasics",
|
||||
"convertedGender"
|
||||
);
|
||||
|
||||
final List results = qry.list();
|
||||
assertThat( results.size(), is( 1 ) );
|
||||
|
||||
final Object result = results.get( 0 );
|
||||
assertThat( result, instanceOf( EntityOfBasics.Gender.class ) );
|
||||
|
||||
assertThat( result, is( EntityOfBasics.Gender.OTHER ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@BeforeAll
|
||||
public void verifyModel(SessionFactoryScope scope) {
|
||||
final EntityMappingType entityDescriptor = scope.getSessionFactory()
|
Loading…
Reference in New Issue