mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-02-17 00:24:57 +00:00
HHH-9060 - JPA 2.1 ContructorResult binding in metamodel
This commit is contained in:
parent
8309071f3c
commit
8945e9e590
@ -36,13 +36,14 @@
|
|||||||
import org.hibernate.MappingException;
|
import org.hibernate.MappingException;
|
||||||
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
||||||
import org.hibernate.engine.ResultSetMappingDefinition;
|
import org.hibernate.engine.ResultSetMappingDefinition;
|
||||||
|
import org.hibernate.engine.query.spi.sql.NativeSQLQueryConstructorReturn;
|
||||||
import org.hibernate.engine.query.spi.sql.NativeSQLQueryRootReturn;
|
import org.hibernate.engine.query.spi.sql.NativeSQLQueryRootReturn;
|
||||||
import org.hibernate.engine.query.spi.sql.NativeSQLQueryScalarReturn;
|
import org.hibernate.engine.query.spi.sql.NativeSQLQueryScalarReturn;
|
||||||
|
import org.hibernate.internal.CoreLogging;
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
import org.hibernate.internal.util.StringHelper;
|
import org.hibernate.internal.util.StringHelper;
|
||||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||||
import org.hibernate.metamodel.source.internal.annotations.AnnotationBindingContext;
|
import org.hibernate.metamodel.source.internal.annotations.AnnotationBindingContext;
|
||||||
import org.hibernate.metamodel.source.internal.annotations.util.JPADotNames;
|
|
||||||
import org.hibernate.metamodel.source.internal.annotations.util.JandexHelper;
|
import org.hibernate.metamodel.source.internal.annotations.util.JandexHelper;
|
||||||
import org.hibernate.metamodel.spi.binding.AttributeBinding;
|
import org.hibernate.metamodel.spi.binding.AttributeBinding;
|
||||||
import org.hibernate.metamodel.spi.binding.CompositeAttributeBinding;
|
import org.hibernate.metamodel.spi.binding.CompositeAttributeBinding;
|
||||||
@ -52,105 +53,144 @@
|
|||||||
import org.hibernate.metamodel.spi.binding.SingularAttributeBinding;
|
import org.hibernate.metamodel.spi.binding.SingularAttributeBinding;
|
||||||
|
|
||||||
import org.jboss.jandex.AnnotationInstance;
|
import org.jboss.jandex.AnnotationInstance;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.jandex.AnnotationValue;
|
||||||
|
|
||||||
|
import static org.hibernate.metamodel.source.internal.annotations.util.JPADotNames.SQL_RESULT_SET_MAPPING;
|
||||||
|
import static org.hibernate.metamodel.source.internal.annotations.util.JPADotNames.SQL_RESULT_SET_MAPPINGS;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Binds <ul>
|
* Handles processing of SQL ResultSet mappings as defined via
|
||||||
* <li>{@link javax.persistence.SqlResultSetMapping}</li>
|
* {@link javax.persistence.SqlResultSetMapping} and
|
||||||
* <li>{@link javax.persistence.SqlResultSetMappings}</li>
|
* {@link javax.persistence.SqlResultSetMappings} annotations, including
|
||||||
* <li>{@link javax.persistence.EntityResult}</li>
|
* their related annotations:<ul>
|
||||||
* <li>{@link javax.persistence.FieldResult}</li>
|
* <li>
|
||||||
* <li>{@link javax.persistence.ColumnResult}</li>
|
* {@link javax.persistence.EntityResult} (and
|
||||||
|
* {@link javax.persistence.FieldResult})
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* {@link javax.persistence.ColumnResult}
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* {@link javax.persistence.ConstructorResult}
|
||||||
|
* </li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @author Strong Liu <stliu@hibernate.org>
|
* @author Strong Liu
|
||||||
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class SqlResultSetProcessor {
|
public class SqlResultSetProcessor {
|
||||||
private static final CoreMessageLogger LOG = Logger.getMessageLogger(
|
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( QueryProcessor.class );
|
||||||
CoreMessageLogger.class,
|
|
||||||
QueryProcessor.class.getName()
|
|
||||||
);
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disallow direct instantiation.
|
||||||
|
*/
|
||||||
private SqlResultSetProcessor() {
|
private SqlResultSetProcessor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void bind(final AnnotationBindingContext bindingContext) {
|
/**
|
||||||
Collection<AnnotationInstance> annotations = bindingContext.getIndex()
|
* Main entry point into processing SQL ResultSet mappings
|
||||||
.getAnnotations( JPADotNames.SQL_RESULT_SET_MAPPING );
|
*
|
||||||
for ( final AnnotationInstance sqlResultSetMappingAnnotationInstance : annotations ) {
|
* @param bindingContext The binding context
|
||||||
bindSqlResultSetMapping( bindingContext, sqlResultSetMappingAnnotationInstance );
|
*/
|
||||||
|
public static void bind(AnnotationBindingContext bindingContext) {
|
||||||
|
// singular form
|
||||||
|
{
|
||||||
|
final Collection<AnnotationInstance> sqlResultSetMappingAnnotations =
|
||||||
|
bindingContext.getIndex().getAnnotations( SQL_RESULT_SET_MAPPING );
|
||||||
|
for ( final AnnotationInstance sqlResultSetMappingAnnotation : sqlResultSetMappingAnnotations ) {
|
||||||
|
bindSqlResultSetMapping( sqlResultSetMappingAnnotation, bindingContext );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
annotations = bindingContext.getIndex().getAnnotations( JPADotNames.SQL_RESULT_SET_MAPPINGS );
|
// plural form
|
||||||
for ( final AnnotationInstance sqlResultSetMappingsAnnotationInstance : annotations ) {
|
{
|
||||||
for ( AnnotationInstance annotationInstance : JandexHelper.getValue(
|
final Collection<AnnotationInstance> sqlResultSetMappingsAnnotations =
|
||||||
sqlResultSetMappingsAnnotationInstance,
|
bindingContext.getIndex().getAnnotations( SQL_RESULT_SET_MAPPINGS );
|
||||||
"value",
|
for ( final AnnotationInstance sqlResultSetMappingsAnnotationInstance : sqlResultSetMappingsAnnotations ) {
|
||||||
AnnotationInstance[].class,
|
final AnnotationInstance[] sqlResultSetMappingAnnotations = JandexHelper.extractAnnotationsValue(
|
||||||
bindingContext.getBuildingOptions().getServiceRegistry().getService( ClassLoaderService.class )
|
sqlResultSetMappingsAnnotationInstance,
|
||||||
) ) {
|
"value"
|
||||||
bindSqlResultSetMapping( bindingContext, annotationInstance );
|
);
|
||||||
|
for ( AnnotationInstance sqlResultSetMappingAnnotation : sqlResultSetMappingAnnotations ) {
|
||||||
|
bindSqlResultSetMapping( sqlResultSetMappingAnnotation, bindingContext );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int entityAliasIndex = 0;
|
private static int entityAliasIndex = 0;
|
||||||
|
|
||||||
private static void bindSqlResultSetMapping(final AnnotationBindingContext bindingContext, final AnnotationInstance annotation) {
|
private static void bindSqlResultSetMapping(AnnotationInstance annotation, AnnotationBindingContext bindingContext) {
|
||||||
final ClassLoaderService classLoaderService = bindingContext.getBuildingOptions().getServiceRegistry().getService( ClassLoaderService.class );
|
|
||||||
entityAliasIndex = 0;
|
entityAliasIndex = 0;
|
||||||
final String name = JandexHelper.getValue( annotation, "name", String.class, classLoaderService );
|
|
||||||
|
// `name` is required...
|
||||||
|
final String name = annotation.value( "name" ).asString();
|
||||||
LOG.debugf( "Binding @SqlResultSetMapping(name=%s)", name );
|
LOG.debugf( "Binding @SqlResultSetMapping(name=%s)", name );
|
||||||
final ResultSetMappingDefinition definition = new ResultSetMappingDefinition( name );
|
final ResultSetMappingDefinition definition = new ResultSetMappingDefinition( name );
|
||||||
for ( final AnnotationInstance entityResult : JandexHelper.getValue(
|
|
||||||
|
final AnnotationInstance[] entityResults = JandexHelper.extractAnnotationsValue(
|
||||||
annotation,
|
annotation,
|
||||||
"entities",
|
"entities"
|
||||||
AnnotationInstance[].class,
|
);
|
||||||
classLoaderService
|
if ( entityResults != null && entityResults.length > 0 ) {
|
||||||
) ) {
|
for ( AnnotationInstance entityResult : entityResults ) {
|
||||||
bindEntityResult( bindingContext, entityResult, definition );
|
bindEntityResult( entityResult, definition, bindingContext );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for ( final AnnotationInstance columnResult : JandexHelper.getValue(
|
|
||||||
|
final AnnotationInstance[] columnResults = JandexHelper.extractAnnotationsValue(
|
||||||
annotation,
|
annotation,
|
||||||
"columns",
|
"columns"
|
||||||
AnnotationInstance[].class,
|
);
|
||||||
classLoaderService
|
if ( columnResults != null && columnResults.length > 0 ) {
|
||||||
) ) {
|
for ( AnnotationInstance columnResult : columnResults ) {
|
||||||
bindColumnResult( bindingContext, columnResult, definition );
|
bindColumnResult( columnResult, definition, bindingContext );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final AnnotationInstance[] constructorResults = JandexHelper.extractAnnotationsValue(
|
||||||
|
annotation,
|
||||||
|
"classes"
|
||||||
|
);
|
||||||
|
if ( constructorResults != null && constructorResults.length > 0 ) {
|
||||||
|
for ( AnnotationInstance constructorResult : constructorResults ) {
|
||||||
|
bindConstructorResult( constructorResult, definition, bindingContext );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bindingContext.getMetadataCollector().addResultSetMapping( definition );
|
bindingContext.getMetadataCollector().addResultSetMapping( definition );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void bindEntityResult(
|
private static void bindEntityResult(
|
||||||
final AnnotationBindingContext bindingContext,
|
|
||||||
final AnnotationInstance entityResult,
|
final AnnotationInstance entityResult,
|
||||||
final ResultSetMappingDefinition definition) {
|
final ResultSetMappingDefinition definition,
|
||||||
final ClassLoaderService classLoaderService = bindingContext.getBuildingOptions().getServiceRegistry().getService( ClassLoaderService.class );
|
final AnnotationBindingContext bindingContext) {
|
||||||
final String className = JandexHelper.getValue( entityResult, "entityClass", String.class, classLoaderService );
|
final String className = entityResult.value( "entityClass" ).asString();
|
||||||
final EntityBinding targetEntityBinding = bindingContext.getMetadataCollector().getEntityBinding( className );
|
final EntityBinding targetEntityBinding = bindingContext.getMetadataCollector().getEntityBinding( className );
|
||||||
if ( targetEntityBinding == null ) {
|
if ( targetEntityBinding == null ) {
|
||||||
throw new MappingException(
|
throw new MappingException(
|
||||||
String.format(
|
String.format(
|
||||||
"Entity[%s] not found in SqlResultMapping[%s]",
|
"Entity [%s] not found in SqlResultMapping [%s]",
|
||||||
className,
|
className,
|
||||||
definition.getName()
|
definition.getName()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final String discriminatorColumn = JandexHelper.getValue( entityResult, "discriminatorColumn", String.class,
|
|
||||||
classLoaderService );
|
|
||||||
|
|
||||||
final Map<String, String[]> propertyResults = new HashMap<String, String[]>();
|
final Map<String, String[]> propertyResults = new HashMap<String, String[]>();
|
||||||
|
|
||||||
|
final AnnotationValue discriminatorColumnValue = entityResult.value( "discriminatorColumn" );
|
||||||
|
if ( discriminatorColumnValue != null ) {
|
||||||
|
final String discriminatorColumn = discriminatorColumnValue.asString();
|
||||||
|
if ( StringHelper.isNotEmpty( discriminatorColumn ) ) {
|
||||||
|
propertyResults.put(
|
||||||
|
"class",
|
||||||
|
new String[] { normalize( discriminatorColumn, bindingContext ) }
|
||||||
|
);
|
||||||
|
|
||||||
if ( StringHelper.isNotEmpty( discriminatorColumn ) ) {
|
}
|
||||||
final String quotingNormalizedName = bindingContext.getMetadataCollector()
|
|
||||||
.getObjectNameNormalizer()
|
|
||||||
.normalizeIdentifierQuoting( discriminatorColumn );
|
|
||||||
propertyResults.put( "class", new String[] { quotingNormalizedName } );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<FieldResult> fieldResultList = reorderFieldResult(
|
List<FieldResult> fieldResultList = reorderFieldResult(
|
||||||
bindingContext,
|
bindingContext,
|
||||||
entityResult,
|
entityResult,
|
||||||
@ -171,24 +211,29 @@ private static void bindEntityResult(
|
|||||||
definition.addQueryReturn( result );
|
definition.addQueryReturn( result );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String normalize(String name, AnnotationBindingContext bindingContext) {
|
||||||
|
return bindingContext.getMetadataCollector()
|
||||||
|
.getObjectNameNormalizer()
|
||||||
|
.normalizeIdentifierQuoting( name );
|
||||||
|
}
|
||||||
|
|
||||||
//todo see org.hibernate.cfg.annotations.ResultsetMappingSecondPass#getSubPropertyIterator
|
//todo see org.hibernate.cfg.annotations.ResultsetMappingSecondPass#getSubPropertyIterator
|
||||||
private static List<FieldResult> reorderFieldResult(
|
private static List<FieldResult> reorderFieldResult(
|
||||||
AnnotationBindingContext bindingContext,
|
AnnotationBindingContext bindingContext,
|
||||||
AnnotationInstance entityResult,
|
AnnotationInstance entityResult,
|
||||||
EntityBinding entityBinding,
|
EntityBinding entityBinding,
|
||||||
String resultSetMappingDefinitionName) {
|
String resultSetMappingDefinitionName) {
|
||||||
final ClassLoaderService classLoaderService = bindingContext.getBuildingOptions().getServiceRegistry().getService( ClassLoaderService.class );
|
final AnnotationInstance[] fieldResultAnnotationInstances = JandexHelper.extractAnnotationsValue(
|
||||||
final AnnotationInstance[] fieldResultAnnotationInstances = JandexHelper.getValue(
|
|
||||||
entityResult,
|
entityResult,
|
||||||
"fields",
|
"fields"
|
||||||
AnnotationInstance[].class,
|
|
||||||
classLoaderService
|
|
||||||
);
|
);
|
||||||
List<FieldResult> results = new ArrayList<FieldResult>( fieldResultAnnotationInstances.length );
|
|
||||||
List<String> propertyNames = new ArrayList<String>();
|
final List<FieldResult> results = new ArrayList<FieldResult>( fieldResultAnnotationInstances.length );
|
||||||
|
final List<String> propertyNames = new ArrayList<String>();
|
||||||
final Set<String> uniqueReturnProperty = new HashSet<String>();
|
final Set<String> uniqueReturnProperty = new HashSet<String>();
|
||||||
|
|
||||||
for ( final AnnotationInstance fieldResult : fieldResultAnnotationInstances ) {
|
for ( final AnnotationInstance fieldResult : fieldResultAnnotationInstances ) {
|
||||||
final String name = JandexHelper.getValue( fieldResult, "name", String.class, classLoaderService );
|
final String name = fieldResult.value( "name" ).asString();
|
||||||
if ( !uniqueReturnProperty.add( name ) ) {
|
if ( !uniqueReturnProperty.add( name ) ) {
|
||||||
throw new MappingException(
|
throw new MappingException(
|
||||||
"duplicate @FieldResult for property " + name
|
"duplicate @FieldResult for property " + name
|
||||||
@ -198,12 +243,13 @@ private static List<FieldResult> reorderFieldResult(
|
|||||||
}
|
}
|
||||||
if ( "class".equals( name ) ) {
|
if ( "class".equals( name ) ) {
|
||||||
throw new MappingException(
|
throw new MappingException(
|
||||||
"class is not a valid property name to use in a @FieldResult, use @EntityResult(discriminatorColumn) instead"
|
"class is not a valid property name to use in a @FieldResult, " +
|
||||||
|
"use @EntityResult(discriminatorColumn) instead"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
final String column = JandexHelper.getValue( fieldResult, "column", String.class,
|
|
||||||
classLoaderService );
|
final String column = fieldResult.value( "column" ).asString();
|
||||||
final String quotingNormalizedColumnName = normalize( bindingContext, column );
|
final String quotingNormalizedColumnName = normalize( column, bindingContext );
|
||||||
if ( name.contains( "." ) ) {
|
if ( name.contains( "." ) ) {
|
||||||
int dotIndex = name.lastIndexOf( '.' );
|
int dotIndex = name.lastIndexOf( '.' );
|
||||||
String reducedName = name.substring( 0, dotIndex );
|
String reducedName = name.substring( 0, dotIndex );
|
||||||
@ -326,19 +372,44 @@ private static void insert(String key, String value, Map<String, String[]> map)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void bindColumnResult(final AnnotationBindingContext bindingContext,
|
private static void bindColumnResult(
|
||||||
final AnnotationInstance columnResult,
|
AnnotationInstance columnResult,
|
||||||
final ResultSetMappingDefinition definition) {
|
ResultSetMappingDefinition definition,
|
||||||
final String name = JandexHelper.getValue( columnResult, "name", String.class,
|
AnnotationBindingContext bindingContext) {
|
||||||
bindingContext.getBuildingOptions().getServiceRegistry().getService( ClassLoaderService.class ) );
|
definition.addQueryReturn( extractColumnResult( columnResult, bindingContext ) );
|
||||||
final String normalizedName = normalize( bindingContext, name );
|
|
||||||
//todo TYPE
|
|
||||||
definition.addQueryReturn( new NativeSQLQueryScalarReturn( normalizedName, null ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String normalize(final AnnotationBindingContext bindingContext, String name) {
|
private static NativeSQLQueryScalarReturn extractColumnResult(
|
||||||
return bindingContext.getMetadataCollector()
|
AnnotationInstance columnResult,
|
||||||
.getObjectNameNormalizer()
|
AnnotationBindingContext bindingContext) {
|
||||||
.normalizeIdentifierQuoting( name );
|
// `name` is required
|
||||||
|
final String name = columnResult.value( "name" ).asString();
|
||||||
|
final String normalizedName = normalize( name, bindingContext );
|
||||||
|
//todo TYPE
|
||||||
|
return new NativeSQLQueryScalarReturn( normalizedName, null );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void bindConstructorResult(
|
||||||
|
AnnotationInstance constructorResult,
|
||||||
|
ResultSetMappingDefinition definition,
|
||||||
|
AnnotationBindingContext bindingContext) {
|
||||||
|
final Class classToConstruct = bindingContext.getServiceRegistry()
|
||||||
|
.getService( ClassLoaderService.class )
|
||||||
|
.classForName( constructorResult.value( "targetClass" ).asString() );
|
||||||
|
|
||||||
|
final List<NativeSQLQueryScalarReturn> columns = new ArrayList<NativeSQLQueryScalarReturn>();
|
||||||
|
final AnnotationInstance[] columnResults = JandexHelper.extractAnnotationsValue(
|
||||||
|
constructorResult,
|
||||||
|
"columns"
|
||||||
|
);
|
||||||
|
if ( columnResults != null && columnResults.length > 0 ) {
|
||||||
|
for ( AnnotationInstance columnResult : columnResults ) {
|
||||||
|
columns.add( extractColumnResult( columnResult, bindingContext ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
definition.addQueryReturn(
|
||||||
|
new NativeSQLQueryConstructorReturn( classToConstruct, columns )
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,6 @@
|
|||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
@FailureExpectedWithNewMetamodel( jiraKey = "HHH-9060" )
|
|
||||||
public class ConstructorResultNativeQueryTest extends BaseEntityManagerFunctionalTestCase {
|
public class ConstructorResultNativeQueryTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
@Entity( name = "Person" )
|
@Entity( name = "Person" )
|
||||||
@SqlResultSetMappings(
|
@SqlResultSetMappings(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user