HHH-6491 Binding @SqlResultSetMapping

This commit is contained in:
Strong Liu 2012-07-05 01:58:52 +08:00
parent bc3964ba98
commit 4a6ea053cc
6 changed files with 175 additions and 92 deletions

View File

@ -253,8 +253,7 @@ public class ResultsetMappingSecondPass implements QuerySecondPass {
}
private static int getIndexOfFirstMatchingProperty(List<String> propertyNames, String follower) {
int propertySize = propertyNames.size();
for (int propIndex = 0; propIndex < propertySize; propIndex++) {
for (int propIndex = 0, propertySize = propertyNames.size(); propIndex < propertySize; propIndex++) {
if ( ( propertyNames.get( propIndex ) ).startsWith( follower ) ) {
return propIndex;
}

View File

@ -45,34 +45,9 @@ public class NamedQueryDefinition implements Serializable {
private final Integer fetchSize;
private final FlushMode flushMode;
private final Map parameterTypes;
private CacheMode cacheMode;
private boolean readOnly;
private String comment;
// kept for backward compatibility until after the 3.1beta5 release of HA
// TODO: is this still needed?
public NamedQueryDefinition(
String query,
boolean cacheable,
String cacheRegion,
Integer timeout,
Integer fetchSize,
FlushMode flushMode,
Map parameterTypes) {
this(
null,
query,
cacheable,
cacheRegion,
timeout,
fetchSize,
flushMode,
null,
false,
null,
parameterTypes
);
}
private final CacheMode cacheMode;
private final boolean readOnly;
private final String comment;
public NamedQueryDefinition(
String name,

View File

@ -178,8 +178,9 @@ public class AnnotationMetadataSourceProcessorImpl implements MetadataSourceProc
public void processMappingDependentMetadata() {
TableProcessor.bind( bindingContext );
FetchProfileProcessor.bind( bindingContext );
QueryProcessor.bind( bindingContext );
SqlResultSetProcessor.bind( bindingContext );
QueryProcessor.bind( bindingContext );
}
private Index parseAndUpdateIndex(List<JaxbRoot<JaxbEntityMappings>> mappings, Index annotationIndex) {

View File

@ -38,6 +38,7 @@ import org.hibernate.AnnotationException;
import org.hibernate.CacheMode;
import org.hibernate.FlushMode;
import org.hibernate.LockMode;
import org.hibernate.MappingException;
import org.hibernate.annotations.QueryHints;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.NotYetImplementedException;
@ -221,6 +222,16 @@ public class QueryProcessor {
boolean callable = getBoolean( hints, QueryHints.CALLABLE, name );
NamedSQLQueryDefinition def;
if ( StringHelper.isNotEmpty( resultSetMapping ) ) {
boolean resultSetMappingExists = metadata.getResultSetMappingDefinitions().containsKey( resultSetMapping );
if ( !resultSetMappingExists ) {
throw new MappingException(
String.format(
"Named SQL Query [%s] is referencing an non-existed result set mapping [%s] ",
name,
resultSetMapping
)
);
}
def = new NamedSQLQueryDefinition(
name,
query, resultSetMapping, null, cacheable,

View File

@ -23,6 +23,7 @@
*/
package org.hibernate.metamodel.internal.source.annotations.global;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
@ -39,8 +40,10 @@ import org.hibernate.MappingException;
import org.hibernate.engine.ResultSetMappingDefinition;
import org.hibernate.engine.query.spi.sql.NativeSQLQueryRootReturn;
import org.hibernate.engine.query.spi.sql.NativeSQLQueryScalarReturn;
import org.hibernate.id.EntityIdentifierNature;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.internal.source.annotations.AnnotationBindingContext;
import org.hibernate.metamodel.internal.source.annotations.util.JPADotNames;
import org.hibernate.metamodel.internal.source.annotations.util.JandexHelper;
@ -49,6 +52,7 @@ import org.hibernate.metamodel.spi.binding.CompositeAttributeBinding;
import org.hibernate.metamodel.spi.binding.EntityBinding;
import org.hibernate.metamodel.spi.binding.ManyToOneAttributeBinding;
import org.hibernate.metamodel.spi.binding.SingularAssociationAttributeBinding;
import org.hibernate.metamodel.spi.binding.SingularNonAssociationAttributeBinding;
/**
* Binds <ul>
@ -152,21 +156,15 @@ public class SqlResultSetProcessor {
);
propertyResults.put( "class", new String[] { quotingNormalizedName } );
}
final Set<String> uniqueReturnProperty = new HashSet<String>();
for ( final AnnotationInstance fieldResult : JandexHelper.getValue(
entityResult,
"fields",
AnnotationInstance[].class
) ) {
bindFieldResult(
List<FieldResult> fieldResultList = reorderFieldResult(
bindingContext,
entityResult,
targetEntityBinding,
fieldResult,
uniqueReturnProperty,
propertyResults,
definition
definition.getName()
);
for ( final FieldResult fieldResult : fieldResultList ) {
insert( StringHelper.root( fieldResult.name ), fieldResult.column, propertyResults );
}
final NativeSQLQueryRootReturn result = new NativeSQLQueryRootReturn(
@ -178,32 +176,46 @@ public class SqlResultSetProcessor {
definition.addQueryReturn( result );
}
private static void bindFieldResult(final AnnotationBindingContext bindingContext,
final EntityBinding entityBinding,
final AnnotationInstance fieldResult,
final Set<String> uniqueReturnProperty,
final Map<String, String[]> propertyResults,
final ResultSetMappingDefinition definition) {
//todo see org.hibernate.cfg.annotations.ResultsetMappingSecondPass#getSubPropertyIterator
private static List<FieldResult> reorderFieldResult(AnnotationBindingContext bindingContext,
AnnotationInstance entityResult,
EntityBinding entityBinding,
String resultSetMappingDefinitionName) {
final AnnotationInstance[] fieldResultAnnotationInstances = JandexHelper.getValue(
entityResult,
"fields",
AnnotationInstance[].class
);
List<FieldResult> results = new ArrayList<FieldResult>( fieldResultAnnotationInstances.length );
List<String> propertyNames = new ArrayList<String>();
final Set<String> uniqueReturnProperty = new HashSet<String>();
for ( final AnnotationInstance fieldResult : fieldResultAnnotationInstances ) {
final String name = JandexHelper.getValue( fieldResult, "name", String.class );
checkFieldNameisNotClass( name );
checkFieldNameUnique( entityBinding, uniqueReturnProperty, definition, name );
if ( !uniqueReturnProperty.add( name ) ) {
throw new MappingException(
"duplicate @FieldResult for property " + name +
" on @Entity " + entityBinding.getEntity()
.getName() + " in " + resultSetMappingDefinitionName
);
}
if ( "class".equals( name ) ) {
throw new MappingException(
"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 );
final String quotingNormalizedColumnName = normalize( bindingContext, column );
if ( name.contains( "." ) ) {
int dotIndex = name.lastIndexOf( '.' );
String reducedName = name.substring( 0, dotIndex );
AttributeBinding attributeBinding = entityBinding.locateAttributeBinding( reducedName );
Iterable<? extends AttributeBinding> attributeBindings = null;
if ( CompositeAttributeBinding.class.isInstance( attributeBinding ) ) {
CompositeAttributeBinding compositeAttributeBinding = CompositeAttributeBinding.class.cast(
attributeBinding
);
boolean hasFollowers = false;
Iterable<AttributeBinding> attributeBindings = compositeAttributeBinding.attributeBindings();
for ( final AttributeBinding ab : attributeBindings ) {
ab.getAttribute().getName();
}
attributeBindings = compositeAttributeBinding.attributeBindings();
}
else if ( ManyToOneAttributeBinding.class.isInstance( attributeBinding ) ) {
ManyToOneAttributeBinding manyToOneAttributeBinding = ManyToOneAttributeBinding.class.cast(
@ -211,33 +223,97 @@ public class SqlResultSetProcessor {
);
EntityBinding referencedEntityBinding = manyToOneAttributeBinding.getReferencedEntityBinding();
Set<SingularAssociationAttributeBinding> referencingAttributeBindings = manyToOneAttributeBinding.getEntityReferencingAttributeBindings();
//todo see org.hibernate.cfg.annotations.ResultsetMappingSecondPass#getSubPropertyIterator
if ( CollectionHelper.isNotEmpty( referencingAttributeBindings ) ) {
attributeBindings = referencingAttributeBindings;
}
else {
// EntityIdentifierNature entityIdentifierNature= referencedEntityBinding.getHierarchyDetails().getEntityIdentifier().getNature();
// switch ( entityIdentifierNature ) {
// case SIMPLE:
// throw new MappingException(
// "dotted notation reference neither a component nor a many/one to one"
// );
// case AGGREGATED_COMPOSITE:
// case COMPOSITE:
// referencedEntityBinding.getHierarchyDetails().getEntityIdentifier().isSingleAttribute();//.isIdentifierMapper();
// }
//todo check if this logic is correct
SingularNonAssociationAttributeBinding identifierAttributeBinding = referencedEntityBinding.getHierarchyDetails()
.getEntityIdentifier()
.getAttributeBinding();
if ( CompositeAttributeBinding.class.isInstance( identifierAttributeBinding ) ) {
attributeBindings = CompositeAttributeBinding.class.cast( identifierAttributeBinding )
.attributeBindings();
}
else {
throw new MappingException(
"dotted notation reference neither a component nor a many/one to one"
);
}
}
}
else {
throw new MappingException( "dotted notation reference neither a component nor a many/one to one" );
}
List<String> followers = getFollowers( attributeBindings, reducedName, name );
int index = results.size();
int followersSize = followers.size();
for ( int loop = 0; loop < followersSize; loop++ ) {
String follower = followers.get( loop );
int currentIndex = getIndexOfFirstMatchingProperty( propertyNames, follower );
index = currentIndex != -1 && currentIndex < index ? currentIndex : index;
}
propertyNames.add( index, name );
results.add( index, new FieldResult( name, quotingNormalizedColumnName ) );
}
else {
propertyNames.add( name );
results.add( new FieldResult( name, quotingNormalizedColumnName ) );
}
insert( StringHelper.root( name ), quotingNormalizedColumnName, propertyResults );
}
return results;
}
private static void checkFieldNameUnique(EntityBinding entityBinding, Set<String> uniqueReturnProperty, ResultSetMappingDefinition definition, String name) {
if ( !uniqueReturnProperty.add( name ) ) {
throw new MappingException(
"duplicate @FieldResult for property " + name +
" on @Entity " + entityBinding.getEntity().getName() + " in " + definition.getName()
);
private static int getIndexOfFirstMatchingProperty(List<String> propertyNames, String follower) {
int propertySize = propertyNames.size();
for ( int propIndex = 0; propIndex < propertySize; propIndex++ ) {
if ( ( propertyNames.get( propIndex ) ).startsWith( follower ) ) {
return propIndex;
}
}
return -1;
}
private static class FieldResult {
String name;
String column;
private FieldResult(String column, String name) {
this.column = column;
this.name = name;
}
}
private static void checkFieldNameisNotClass(String name) {
if ( "class".equals( name ) ) {
throw new MappingException(
"class is not a valid property name to use in a @FieldResult, use @EntityResult(discriminatorColumn) instead"
);
private static List<String> getFollowers(Iterable<? extends AttributeBinding> attributeBindings, String reducedName, String name) {
boolean hasFollowers = false;
List<String> followers = new ArrayList<String>();
for ( final AttributeBinding attributeBinding : attributeBindings ) {
String currentPropertyName = attributeBinding.getAttribute().getName();
String currentName = reducedName + '.' + currentPropertyName;
if ( hasFollowers ) {
followers.add( currentName );
}
if ( name.equals( currentName ) ) {
hasFollowers = true;
}
}
return followers;
}
private static void insert(String key, String value, Map<String, String[]> map) {
if ( map.containsKey( key ) ) {

View File

@ -23,19 +23,23 @@
*/
package org.hibernate.metamodel.internal.source.hbm;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.hibernate.engine.ResultSetMappingDefinition;
import org.hibernate.internal.jaxb.Origin;
import org.hibernate.internal.jaxb.mapping.hbm.EntityElement;
import org.hibernate.internal.jaxb.mapping.hbm.JaxbFetchProfileElement;
import org.hibernate.internal.jaxb.mapping.hbm.JaxbHibernateMapping;
import org.hibernate.internal.jaxb.mapping.hbm.JaxbHibernateMapping.JaxbImport;
import org.hibernate.internal.jaxb.mapping.hbm.JaxbQueryElement;
import org.hibernate.internal.jaxb.mapping.hbm.JaxbResultsetElement;
import org.hibernate.internal.jaxb.mapping.hbm.JaxbSqlQueryElement;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.Value;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.spi.binding.FetchProfile;
import org.hibernate.metamodel.spi.relational.AuxiliaryDatabaseObject;
import org.hibernate.metamodel.spi.relational.BasicAuxiliaryDatabaseObjectImpl;
@ -217,11 +221,28 @@ public class HibernateMappingProcessor {
}
private void processResultSetMappings() {
if ( mappingRoot().getResultset() == null ) {
List<JaxbResultsetElement> resultsetElements = new ArrayList<JaxbResultsetElement>();
if ( CollectionHelper.isNotEmpty( mappingRoot().getResultset() ) ) {
resultsetElements.addAll( mappingRoot().getResultset() );
}
for ( Object obj : mappingRoot().getClazzOrSubclassOrJoinedSubclass() ) {
EntityElement element = EntityElement.class.cast( obj );
if ( CollectionHelper.isNotEmpty( element.getResultset() ) ) {
resultsetElements.addAll( element.getResultset() );
}
}
if ( resultsetElements.isEmpty() ) {
return;
}
for(final JaxbResultsetElement element : resultsetElements){
bindResultSetMappingDefinitions( element );
}
// bindResultSetMappingDefinitions( element, null, mappings );
}
private void bindResultSetMappingDefinitions(JaxbResultsetElement element) {
ResultSetMappingDefinition definition = new ResultSetMappingDefinition( element.getName() );
metadata.addResultSetMapping( definition );
}
private void processNamedQueries() {