HHH-4394 - @OrderBy usage on a joined classes (when using join table) produces incorred SQL syntax.

This commit is contained in:
Steve Ebersole 2012-06-15 17:13:42 -05:00
parent 41bac11115
commit d51a0d0c78
41 changed files with 1262 additions and 390 deletions

View File

@ -164,7 +164,7 @@ orderByFragment { trace("orderByFragment"); }
; ;
/** /**
* Reconition rule for what ANSI SQL terms the <tt>sort specification</tt>, which is essentially each thing upon which * Recognition rule for what ANSI SQL terms the <tt>sort specification</tt>, which is essentially each thing upon which
* the results should be sorted. * the results should be sorted.
*/ */
sortSpecification { trace("sortSpecification"); } sortSpecification { trace("sortSpecification"); }
@ -175,7 +175,7 @@ sortSpecification { trace("sortSpecification"); }
; ;
/** /**
* Reconition rule for what ANSI SQL terms the <tt>sort key</tt> which is the expression (column, function, etc) upon * Recognition rule for what ANSI SQL terms the <tt>sort key</tt> which is the expression (column, function, etc) upon
* which to base the sorting. * which to base the sorting.
*/ */
sortKey! { trace("sortKey"); } sortKey! { trace("sortKey"); }
@ -185,7 +185,7 @@ sortKey! { trace("sortKey"); }
; ;
/** /**
* Reconition rule what this grammar recognizes as valid <tt>sort key</tt>. * Recognition rule what this grammar recognizes as valid <tt>sort key</tt>.
*/ */
expression! { trace("expression"); } expression! { trace("expression"); }
: HARD_QUOTE qi:IDENT HARD_QUOTE { : HARD_QUOTE qi:IDENT HARD_QUOTE {
@ -261,7 +261,7 @@ functionParameter { trace("functionParameter"); }
; ;
/** /**
* Reconition rule for what ANSI SQL terms the <tt>collation specification</tt> used to allow specifying that sorting for * Recognition rule for what ANSI SQL terms the <tt>collation specification</tt> used to allow specifying that sorting for
* the given {@link #sortSpecification} be treated within a specific character-set. * the given {@link #sortSpecification} be treated within a specific character-set.
*/ */
collationSpecification! { trace("collationSpecification"); } collationSpecification! { trace("collationSpecification"); }
@ -278,7 +278,7 @@ collationName { trace("collationSpecification"); }
; ;
/** /**
* Reconition rule for what ANSI SQL terms the <tt>ordering specification</tt>; <tt>ASCENDING</tt> or * Recognition rule for what ANSI SQL terms the <tt>ordering specification</tt>; <tt>ASCENDING</tt> or
* <tt>DESCENDING</tt>. * <tt>DESCENDING</tt>.
*/ */
orderingSpecification! { trace("orderingSpecification"); } orderingSpecification! { trace("orderingSpecification"); }

View File

@ -23,14 +23,6 @@
*/ */
package org.hibernate.cfg.annotations; package org.hibernate.cfg.annotations;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import javax.persistence.AttributeOverride; import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides; import javax.persistence.AttributeOverrides;
import javax.persistence.ElementCollection; import javax.persistence.ElementCollection;
@ -43,6 +35,11 @@ import javax.persistence.ManyToMany;
import javax.persistence.MapKey; import javax.persistence.MapKey;
import javax.persistence.MapKeyColumn; import javax.persistence.MapKeyColumn;
import javax.persistence.OneToMany; import javax.persistence.OneToMany;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
@ -110,9 +107,7 @@ import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.ManyToOne; import org.hibernate.mapping.ManyToOne;
import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property; import org.hibernate.mapping.Property;
import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.SimpleValue; import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.SingleTableSubclass;
import org.hibernate.mapping.Table; import org.hibernate.mapping.Table;
import org.hibernate.mapping.TypeDef; import org.hibernate.mapping.TypeDef;
@ -946,192 +941,39 @@ public abstract class CollectionBinder {
} }
} }
private static String buildOrderByClauseFromHql(String hqlOrderBy, PersistentClass associatedClass, String role) { private static String buildOrderByClauseFromHql(String orderByFragment, PersistentClass associatedClass, String role) {
String orderByString = null; if ( orderByFragment != null ) {
if ( hqlOrderBy != null ) { if ( orderByFragment.length() == 0 ) {
List<String> properties = new ArrayList<String>();
List<String> ordering = new ArrayList<String>();
StringBuilder orderByBuffer = new StringBuilder();
if ( hqlOrderBy.length() == 0 ) {
//order by id //order by id
Iterator it = associatedClass.getIdentifier().getColumnIterator(); return "id asc";
while ( it.hasNext() ) {
Selectable col = (Selectable) it.next();
orderByBuffer.append( col.getText() ).append( " asc" ).append( ", " );
}
} }
else { else if ( "desc".equals( orderByFragment ) ) {
StringTokenizer st = new StringTokenizer( hqlOrderBy, " ,", false ); return "id desc";
String currentOrdering = null;
//FIXME make this code decent
while ( st.hasMoreTokens() ) {
String token = st.nextToken();
if ( isNonPropertyToken( token ) ) {
if ( currentOrdering != null ) {
throw new AnnotationException(
"Error while parsing HQL orderBy clause: " + hqlOrderBy
+ " (" + role + ")"
);
}
currentOrdering = token;
}
else {
//Add ordering of the previous
if ( currentOrdering == null ) {
//default ordering
ordering.add( "asc" );
}
else {
ordering.add( currentOrdering );
currentOrdering = null;
}
properties.add( token );
}
}
ordering.remove( 0 ); //first one is the algorithm starter
// add last one ordering
if ( currentOrdering == null ) {
//default ordering
ordering.add( "asc" );
}
else {
ordering.add( currentOrdering );
currentOrdering = null;
}
int index = 0;
for (String property : properties) {
Property p = BinderHelper.findPropertyByName( associatedClass, property );
if ( p == null ) {
throw new AnnotationException(
"property from @OrderBy clause not found: "
+ associatedClass.getEntityName() + "." + property
);
}
PersistentClass pc = p.getPersistentClass();
String table;
if ( pc == null ) {
//we are touching a @IdClass property, the pc is not set
//this means pc == associatedClass
//TODO check whether @ManyToOne @JoinTable in @IdClass used for @OrderBy works: doh!
table = "";
}
else if (pc == associatedClass
|| (associatedClass instanceof SingleTableSubclass && pc
.getMappedClass().isAssignableFrom(
associatedClass.getMappedClass()))) {
table = "";
} else {
table = pc.getTable().getQuotedName() + ".";
}
Iterator propertyColumns = p.getColumnIterator();
while ( propertyColumns.hasNext() ) {
Selectable column = (Selectable) propertyColumns.next();
orderByBuffer.append( table )
.append( column.getText() )
.append( " " )
.append( ordering.get( index ) )
.append( ", " );
}
index++;
}
}
orderByString = orderByBuffer.substring( 0, orderByBuffer.length() - 2 );
}
return orderByString;
}
private static String buildOrderByClauseFromHql(String hqlOrderBy, Component component, String role) {
String orderByString = null;
if ( hqlOrderBy != null ) {
List<String> properties = new ArrayList<String>();
List<String> ordering = new ArrayList<String>();
StringBuilder orderByBuffer = new StringBuilder();
if ( hqlOrderBy.length() == 0 ) {
//TODO : Check that. Maybe order by key for maps
}
else {
StringTokenizer st = new StringTokenizer( hqlOrderBy, " ,", false );
String currentOrdering = null;
//FIXME make this code decent
while ( st.hasMoreTokens() ) {
String token = st.nextToken();
if ( isNonPropertyToken( token ) ) {
if ( currentOrdering != null ) {
throw new AnnotationException(
"Error while parsing HQL orderBy clause: " + hqlOrderBy
+ " (" + role + ")"
);
}
currentOrdering = token;
}
else {
//Add ordering of the previous
if ( currentOrdering == null ) {
//default ordering
ordering.add( "asc" );
}
else {
ordering.add( currentOrdering );
currentOrdering = null;
}
properties.add( token );
}
}
ordering.remove( 0 ); //first one is the algorithm starter
// add last one ordering
if ( currentOrdering == null ) {
//default ordering
ordering.add( "asc" );
}
else {
ordering.add( currentOrdering );
currentOrdering = null;
}
int index = 0;
for (String property : properties) {
Property p = BinderHelper.findPropertyByName( component, property );
if ( p == null ) {
throw new AnnotationException(
"property from @OrderBy clause not found: "
+ role + "." + property
);
}
Iterator propertyColumns = p.getColumnIterator();
while ( propertyColumns.hasNext() ) {
Selectable column = (Selectable) propertyColumns.next();
orderByBuffer.append( column.getText() )
.append( " " )
.append( ordering.get( index ) )
.append( ", " );
}
index++;
}
if ( orderByBuffer.length() >= 2 ) {
orderByString = orderByBuffer.substring( 0, orderByBuffer.length() - 2 );
}
} }
} }
return orderByString; return orderByFragment;
} }
private static boolean isNonPropertyToken(String token) { private static String adjustUserSuppliedValueCollectionOrderingFragment(String orderByFragment) {
if ( " ".equals( token ) ) return true; if ( orderByFragment != null ) {
if ( ",".equals( token ) ) return true; // NOTE: "$element$" is a specially recognized collection property recognized by the collection persister
if ( token.equalsIgnoreCase( "desc" ) ) return true; if ( orderByFragment.length() == 0 ) {
if ( token.equalsIgnoreCase( "asc" ) ) return true; //order by element
return false; return "$element$ asc";
}
else if ( "desc".equals( orderByFragment ) ) {
return "$element$ desc";
}
}
return orderByFragment;
} }
private static SimpleValue buildCollectionKey( private static SimpleValue buildCollectionKey(
Collection collValue, Ejb3JoinColumn[] joinColumns, boolean cascadeDeleteEnabled, Collection collValue,
XProperty property, Mappings mappings Ejb3JoinColumn[] joinColumns,
) { boolean cascadeDeleteEnabled,
XProperty property,
Mappings mappings) {
//binding key reference using column //binding key reference using column
KeyValue keyVal; KeyValue keyVal;
//give a chance to override the referenced property name //give a chance to override the referenced property name
@ -1406,7 +1248,7 @@ public abstract class CollectionBinder {
if ( StringHelper.isNotEmpty( hqlOrderBy ) ) { if ( StringHelper.isNotEmpty( hqlOrderBy ) ) {
String path = collValue.getOwnerEntityName() + "." + joinColumns[0].getPropertyName(); String path = collValue.getOwnerEntityName() + "." + joinColumns[0].getPropertyName();
String orderBy = buildOrderByClauseFromHql( hqlOrderBy, component, path ); String orderBy = adjustUserSuppliedValueCollectionOrderingFragment( hqlOrderBy );
if ( orderBy != null ) { if ( orderBy != null ) {
collValue.setOrderBy( orderBy ); collValue.setOrderBy( orderBy );
} }
@ -1437,6 +1279,10 @@ public abstract class CollectionBinder {
elementBinder.setColumns( elementColumns ); elementBinder.setColumns( elementColumns );
elementBinder.setType( property, elementClass ); elementBinder.setType( property, elementClass );
collValue.setElement( elementBinder.make() ); collValue.setElement( elementBinder.make() );
String orderBy = adjustUserSuppliedValueCollectionOrderingFragment( hqlOrderBy );
if ( orderBy != null ) {
collValue.setOrderBy( orderBy );
}
} }
} }

View File

@ -240,13 +240,22 @@ public class DefaultLoadEventListener extends AbstractLockUpgradeEventListener i
} }
// this class has no proxies (so do a shortcut) // this class has no proxies (so do a shortcut)
if (!persister.hasProxy()) return load(event, persister, keyToLoad, options); if (!persister.hasProxy()) {
return load(event, persister, keyToLoad, options);
}
final PersistenceContext persistenceContext = event.getSession().getPersistenceContext(); final PersistenceContext persistenceContext = event.getSession().getPersistenceContext();
// look for a proxy // look for a proxy
Object proxy = persistenceContext.getProxy(keyToLoad); Object proxy = persistenceContext.getProxy(keyToLoad);
if (proxy != null) return returnNarrowedProxy(event, persister, keyToLoad, options, persistenceContext, proxy); if (proxy != null) {
if (options.isAllowProxyCreation()) return createProxyIfNecessary(event, persister, keyToLoad, options, persistenceContext); return returnNarrowedProxy(event, persister, keyToLoad, options, persistenceContext, proxy);
}
if (options.isAllowProxyCreation()) {
return createProxyIfNecessary(event, persister, keyToLoad, options, persistenceContext);
}
// return a newly loaded object // return a newly loaded object
return load(event, persister, keyToLoad, options); return load(event, persister, keyToLoad, options);
} }

View File

@ -930,7 +930,7 @@ public class JoinWalker {
if ( buf.length()>0 ) buf.setLength( buf.length()-2 ); if ( buf.length()>0 ) buf.setLength( buf.length()-2 );
return buf.toString(); return buf.toString();
} }
/** /**
* Render the where condition for a (batch) load by identifier / collection key * Render the where condition for a (batch) load by identifier / collection key
*/ */

View File

@ -47,6 +47,7 @@ import org.hibernate.cache.spi.entry.StructuredCollectionCacheEntry;
import org.hibernate.cache.spi.entry.StructuredMapCacheEntry; import org.hibernate.cache.spi.entry.StructuredMapCacheEntry;
import org.hibernate.cache.spi.entry.UnstructuredCacheEntry; import org.hibernate.cache.spi.entry.UnstructuredCacheEntry;
import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.batch.internal.BasicBatchKey; import org.hibernate.engine.jdbc.batch.internal.BasicBatchKey;
@ -77,13 +78,21 @@ import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.Table; import org.hibernate.mapping.Table;
import org.hibernate.metadata.CollectionMetadata; import org.hibernate.metadata.CollectionMetadata;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Loadable;
import org.hibernate.persister.entity.PropertyMapping; import org.hibernate.persister.entity.PropertyMapping;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.pretty.MessageHelper; import org.hibernate.pretty.MessageHelper;
import org.hibernate.sql.Alias; import org.hibernate.sql.Alias;
import org.hibernate.sql.SelectFragment; import org.hibernate.sql.SelectFragment;
import org.hibernate.sql.SimpleSelect; import org.hibernate.sql.SimpleSelect;
import org.hibernate.sql.Template; import org.hibernate.sql.Template;
import org.hibernate.sql.ordering.antlr.ColumnMapper; import org.hibernate.sql.ordering.antlr.ColumnMapper;
import org.hibernate.sql.ordering.antlr.ColumnReference;
import org.hibernate.sql.ordering.antlr.FormulaReference;
import org.hibernate.sql.ordering.antlr.OrderByAliasResolver;
import org.hibernate.sql.ordering.antlr.OrderByFragmentParser;
import org.hibernate.sql.ordering.antlr.OrderByTranslation;
import org.hibernate.sql.ordering.antlr.SqlValueReference;
import org.hibernate.type.CollectionType; import org.hibernate.type.CollectionType;
import org.hibernate.type.CompositeType; import org.hibernate.type.CompositeType;
import org.hibernate.type.EntityType; import org.hibernate.type.EntityType;
@ -116,11 +125,16 @@ public abstract class AbstractCollectionPersister
private final String sqlDetectRowByIndexString; private final String sqlDetectRowByIndexString;
private final String sqlDetectRowByElementString; private final String sqlDetectRowByElementString;
protected final String sqlWhereString;
private final String sqlOrderByStringTemplate;
private final String sqlWhereStringTemplate;
private final boolean hasOrder;
protected final boolean hasWhere; protected final boolean hasWhere;
protected final String sqlWhereString;
private final String sqlWhereStringTemplate;
private final boolean hasOrder;
private final OrderByTranslation orderByTranslation;
private final boolean hasManyToManyOrder;
private final OrderByTranslation manyToManyOrderByTranslation;
private final int baseIndex; private final int baseIndex;
private final String nodeName; private final String nodeName;
@ -202,9 +216,6 @@ public abstract class AbstractCollectionPersister
private final String manyToManyWhereString; private final String manyToManyWhereString;
private final String manyToManyWhereTemplate; private final String manyToManyWhereTemplate;
private final boolean hasManyToManyOrder;
private final String manyToManyOrderByTemplate;
// custom sql // custom sql
private final boolean insertCallable; private final boolean insertCallable;
private final boolean updateCallable; private final boolean updateCallable;
@ -549,22 +560,16 @@ public abstract class AbstractCollectionPersister
hasOrder = collection.getOrderBy() != null; hasOrder = collection.getOrderBy() != null;
if ( hasOrder ) { if ( hasOrder ) {
ColumnMapper mapper = new ColumnMapper() { orderByTranslation = Template.translateOrderBy(
public String[] map(String reference) {
return elementPropertyMapping.toColumns( reference );
}
};
sqlOrderByStringTemplate = Template.renderOrderByStringTemplate(
collection.getOrderBy(), collection.getOrderBy(),
mapper, new ColumnMapperImpl(),
factory, factory,
dialect, dialect,
factory.getSqlFunctionRegistry() factory.getSqlFunctionRegistry()
); );
} }
else { else {
sqlOrderByStringTemplate = null; orderByTranslation = null;
} }
// Handle any filters applied to this collection // Handle any filters applied to this collection
@ -581,27 +586,76 @@ public abstract class AbstractCollectionPersister
hasManyToManyOrder = collection.getManyToManyOrdering() != null; hasManyToManyOrder = collection.getManyToManyOrdering() != null;
if ( hasManyToManyOrder ) { if ( hasManyToManyOrder ) {
ColumnMapper mapper = new ColumnMapper() { manyToManyOrderByTranslation = Template.translateOrderBy(
public String[] map(String reference) {
return elementPropertyMapping.toColumns( reference );
}
};
manyToManyOrderByTemplate = Template.renderOrderByStringTemplate(
collection.getManyToManyOrdering(), collection.getManyToManyOrdering(),
mapper, new ColumnMapperImpl(),
factory, factory,
dialect, dialect,
factory.getSqlFunctionRegistry() factory.getSqlFunctionRegistry()
); );
} }
else { else {
manyToManyOrderByTemplate = null; manyToManyOrderByTranslation = null;
} }
initCollectionPropertyMap(); initCollectionPropertyMap();
} }
private class ColumnMapperImpl implements ColumnMapper {
@Override
public SqlValueReference[] map(String reference) {
final String[] columnNames;
final String[] formulaTemplates;
// handle the special "$element$" property name...
if ( "$element$".equals( reference ) ) {
columnNames = elementColumnNames;
formulaTemplates = elementFormulaTemplates;
}
else {
columnNames = elementPropertyMapping.toColumns( reference );
formulaTemplates = formulaTemplates( reference, columnNames.length );
}
final SqlValueReference[] result = new SqlValueReference[ columnNames.length ];
int i = 0;
for ( final String columnName : columnNames ) {
if ( columnName == null ) {
// if the column name is null, it indicates that this index in the property value mapping is
// actually represented by a formula.
final int propertyIndex = elementPersister.getEntityMetamodel().getPropertyIndex( reference );
final String formulaTemplate = formulaTemplates[i];
result[i] = new FormulaReference() {
@Override
public String getFormulaFragment() {
return formulaTemplate;
}
};
}
else {
result[i] = new ColumnReference() {
@Override
public String getColumnName() {
return columnName;
}
};
}
i++;
}
return result;
}
}
private String[] formulaTemplates(String reference, int expectedSize) {
try {
final int propertyIndex = elementPersister.getEntityMetamodel().getPropertyIndex( reference );
return ( (Queryable) elementPersister ).getSubclassPropertyFormulaTemplateClosure()[propertyIndex];
}
catch (Exception e) {
return new String[expectedSize];
}
}
public void postInstantiate() throws MappingException { public void postInstantiate() throws MappingException {
initializer = queryLoaderName == null ? initializer = queryLoaderName == null ?
createCollectionInitializer( LoadQueryInfluencers.NONE ) : createCollectionInitializer( LoadQueryInfluencers.NONE ) :
@ -693,13 +747,13 @@ public abstract class AbstractCollectionPersister
public String getSQLOrderByString(String alias) { public String getSQLOrderByString(String alias) {
return hasOrdering() return hasOrdering()
? StringHelper.replace( sqlOrderByStringTemplate, Template.TEMPLATE, alias ) ? orderByTranslation.injectAliases( new StandardOrderByAliasResolver( alias ) )
: ""; : "";
} }
public String getManyToManyOrderByString(String alias) { public String getManyToManyOrderByString(String alias) {
return hasManyToManyOrdering() return hasManyToManyOrdering()
? StringHelper.replace( manyToManyOrderByTemplate, Template.TEMPLATE, alias ) ? manyToManyOrderByTranslation.injectAliases( new StandardOrderByAliasResolver( alias ) )
: ""; : "";
} }
@ -1856,4 +1910,23 @@ public abstract class AbstractCollectionPersister
public CollectionInitializer getInitializer() { public CollectionInitializer getInitializer() {
return initializer; return initializer;
} }
private class StandardOrderByAliasResolver implements OrderByAliasResolver {
private final String rootAlias;
private StandardOrderByAliasResolver(String rootAlias) {
this.rootAlias = rootAlias;
}
@Override
public String resolveTableAlias(String columnReference) {
if ( elementPersister == null ) {
// we have collection of non-entity elements...
return rootAlias;
}
else {
return ( (Loadable) elementPersister ).getTableAliasForColumn( columnReference, rootAlias );
}
}
}
} }

View File

@ -1969,7 +1969,8 @@ public abstract class AbstractEntityPersister
return propertyDefinedOnSubclass[i]; return propertyDefinedOnSubclass[i];
} }
protected String[][] getSubclassPropertyFormulaTemplateClosure() { @Override
public String[][] getSubclassPropertyFormulaTemplateClosure() {
return subclassPropertyFormulaTemplateClosure; return subclassPropertyFormulaTemplateClosure;
} }
@ -4748,4 +4749,13 @@ public abstract class AbstractEntityPersister
public EntityInstrumentationMetadata getInstrumentationMetadata() { public EntityInstrumentationMetadata getInstrumentationMetadata() {
return entityMetamodel.getInstrumentationMetadata(); return entityMetamodel.getInstrumentationMetadata();
} }
@Override
public String getTableAliasForColumn(String columnName, String rootAlias) {
return generateTableAlias( rootAlias, determineTableNumberForColumn( columnName ) );
}
public int determineTableNumberForColumn(String columnName) {
return 0;
}
} }

View File

@ -840,4 +840,24 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
} }
return super.getSubclassPropertyDeclarer( propertyPath ); return super.getSubclassPropertyDeclarer( propertyPath );
} }
@Override
public int determineTableNumberForColumn(String columnName) {
final String[] subclassColumnNameClosure = getSubclassColumnClosure();
for ( int i = 0, max = subclassColumnNameClosure.length; i < max; i++ ) {
final boolean quoted = subclassColumnNameClosure[i].startsWith( "\"" )
&& subclassColumnNameClosure[i].endsWith( "\"" );
if ( quoted ) {
if ( subclassColumnNameClosure[i].equals( columnName ) ) {
return getSubclassColumnTableNumberClosure()[i];
}
}
else {
if ( subclassColumnNameClosure[i].equalsIgnoreCase( columnName ) ) {
return getSubclassColumnTableNumberClosure()[i];
}
}
}
throw new HibernateException( "Could not locate table which owns column [" + columnName + "] referenced in order-by mapping" );
}
} }

View File

@ -120,4 +120,18 @@ public interface Loadable extends EntityPersister {
* @param fetchProfileName The name of the profile affecting this. * @param fetchProfileName The name of the profile affecting this.
*/ */
public void registerAffectingFetchProfile(String fetchProfileName); public void registerAffectingFetchProfile(String fetchProfileName);
/**
* Given a column name and the root table alias in use for the entity hierarchy, determine the proper table alias
* for the table in that hierarchy that contains said column.
* <p/>
* NOTE : Generally speaking the column is not validated to exist. Most implementations simply return the
* root alias; the exception is {@link JoinedSubclassEntityPersister}
*
* @param columnName The column name
* @param rootAlias The hierarchy root alias
*
* @return The proper table alias for qualifying the given column.
*/
public String getTableAliasForColumn(String columnName, String rootAlias);
} }

View File

@ -170,6 +170,8 @@ public interface Queryable extends Loadable, PropertyMapping, Joinable {
*/ */
public DiscriminatorMetadata getTypeDiscriminatorMetadata(); public DiscriminatorMetadata getTypeDiscriminatorMetadata();
String[][] getSubclassPropertyFormulaTemplateClosure();
public static class Declarer { public static class Declarer {
public static final Declarer CLASS = new Declarer( "class" ); public static final Declarer CLASS = new Declarer( "class" );
public static final Declarer SUBCLASS = new Declarer( "subclass" ); public static final Declarer SUBCLASS = new Declarer( "subclass" );

View File

@ -37,7 +37,10 @@ import org.hibernate.dialect.function.SQLFunctionRegistry;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.sql.ordering.antlr.ColumnMapper; import org.hibernate.sql.ordering.antlr.ColumnMapper;
import org.hibernate.sql.ordering.antlr.OrderByAliasResolver;
import org.hibernate.sql.ordering.antlr.OrderByFragmentTranslator; import org.hibernate.sql.ordering.antlr.OrderByFragmentTranslator;
import org.hibernate.sql.ordering.antlr.OrderByTranslation;
import org.hibernate.sql.ordering.antlr.SqlValueReference;
import org.hibernate.sql.ordering.antlr.TranslationContext; import org.hibernate.sql.ordering.antlr.TranslationContext;
/** /**
@ -624,8 +627,9 @@ public final class Template {
public static class NoOpColumnMapper implements ColumnMapper { public static class NoOpColumnMapper implements ColumnMapper {
public static final NoOpColumnMapper INSTANCE = new NoOpColumnMapper(); public static final NoOpColumnMapper INSTANCE = new NoOpColumnMapper();
public String[] map(String reference) { public SqlValueReference[] map(String reference) {
return new String[] { reference }; // return new String[] { reference };
return null;
} }
} }
@ -639,7 +643,7 @@ public final class Template {
* *
* @return The rendered <tt>ORDER BY</tt> template. * @return The rendered <tt>ORDER BY</tt> template.
* *
* @deprecated Use {@link #renderOrderByStringTemplate(String,ColumnMapper,SessionFactoryImplementor,Dialect,SQLFunctionRegistry)} instead * @deprecated Use {@link #translateOrderBy} instead
*/ */
@Deprecated @Deprecated
public static String renderOrderByStringTemplate( public static String renderOrderByStringTemplate(
@ -655,6 +659,28 @@ public final class Template {
); );
} }
public static String renderOrderByStringTemplate(
String orderByFragment,
final ColumnMapper columnMapper,
final SessionFactoryImplementor sessionFactory,
final Dialect dialect,
final SQLFunctionRegistry functionRegistry) {
return translateOrderBy(
orderByFragment,
columnMapper,
sessionFactory,
dialect,
functionRegistry
).injectAliases( LEGACY_ORDER_BY_ALIAS_RESOLVER );
}
public static OrderByAliasResolver LEGACY_ORDER_BY_ALIAS_RESOLVER = new OrderByAliasResolver() {
@Override
public String resolveTableAlias(String columnReference) {
return TEMPLATE;
}
};
/** /**
* Performs order-by template rendering allowing {@link ColumnMapper column mapping}. An <tt>ORDER BY</tt> template * Performs order-by template rendering allowing {@link ColumnMapper column mapping}. An <tt>ORDER BY</tt> template
* has all column references "qualified" with a placeholder identified by {@link Template#TEMPLATE} which can later * has all column references "qualified" with a placeholder identified by {@link Template#TEMPLATE} which can later
@ -668,7 +694,7 @@ public final class Template {
* *
* @return The rendered <tt>ORDER BY</tt> template. * @return The rendered <tt>ORDER BY</tt> template.
*/ */
public static String renderOrderByStringTemplate( public static OrderByTranslation translateOrderBy(
String orderByFragment, String orderByFragment,
final ColumnMapper columnMapper, final ColumnMapper columnMapper,
final SessionFactoryImplementor sessionFactory, final SessionFactoryImplementor sessionFactory,
@ -692,8 +718,7 @@ public final class Template {
} }
}; };
OrderByFragmentTranslator translator = new OrderByFragmentTranslator( context ); return OrderByFragmentTranslator.translate( context, orderByFragment );
return translator.render( orderByFragment );
} }
private static boolean isNamedParameter(String token) { private static boolean isNamedParameter(String token) {

View File

@ -1,10 +1,10 @@
/* /*
* Hibernate, Relational Persistence for Idiomatic Java * Hibernate, Relational Persistence for Idiomatic Java
* *
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as * Copyright (c) 2008 Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution * indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are * statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC. * distributed under license by Red Hat Inc.
* *
* This copyrighted material is made available to anyone wishing to use, modify, * This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU * copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,11 +20,9 @@
* Free Software Foundation, Inc. * Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*
*/ */
package org.hibernate.sql.ordering.antlr; package org.hibernate.sql.ordering.antlr;
/** /**
* Models a collation specification (<tt>COLLATE</tt> using a specific character-set) within a * Models a collation specification (<tt>COLLATE</tt> using a specific character-set) within a
* {@link SortSpecification}. * {@link SortSpecification}.

View File

@ -1,10 +1,10 @@
/* /*
* Hibernate, Relational Persistence for Idiomatic Java * Hibernate, Relational Persistence for Idiomatic Java
* *
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as * Copyright (c) 2008 Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution * indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are * statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC. * distributed under license by Red Hat Inc.
* *
* This copyrighted material is made available to anyone wishing to use, modify, * This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU * copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,9 +20,9 @@
* Free Software Foundation, Inc. * Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*
*/ */
package org.hibernate.sql.ordering.antlr; package org.hibernate.sql.ordering.antlr;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
/** /**
@ -36,10 +36,11 @@ public interface ColumnMapper {
* *
* @param reference The property reference name. * @param reference The property reference name.
* *
* @return The underlying columns, or null if the property reference is unknown. * @return References to the columns/formulas that define the value mapping for the given property, or null
* if the property reference is unknown.
* *
* @throws HibernateException Generally indicates that the property reference is unkown; interpretation is the * @throws HibernateException Generally indicates that the property reference is unknown; interpretation
* same as a null return. * should be the same as a null return.
*/ */
public String[] map(String reference) throws HibernateException; public SqlValueReference[] map(String reference) throws HibernateException;
} }

View File

@ -0,0 +1,38 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.sql.ordering.antlr;
/**
* Reference to a column name.
*
* @author Steve Ebersole
*/
public interface ColumnReference extends SqlValueReference {
/**
* Retrieve the column name.
*
* @return THe column name
*/
public String getColumnName();
}

View File

@ -1,10 +1,10 @@
/* /*
* Hibernate, Relational Persistence for Idiomatic Java * Hibernate, Relational Persistence for Idiomatic Java
* *
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as * Copyright (c) 2008 Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution * indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are * statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC. * distributed under license by Red Hat Inc.
* *
* This copyrighted material is made available to anyone wishing to use, modify, * This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU * copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,9 +20,9 @@
* Free Software Foundation, Inc. * Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*
*/ */
package org.hibernate.sql.ordering.antlr; package org.hibernate.sql.ordering.antlr;
import antlr.ASTFactory; import antlr.ASTFactory;
/** /**
@ -31,9 +31,7 @@ import antlr.ASTFactory;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class Factory extends ASTFactory implements OrderByTemplateTokenTypes { public class Factory extends ASTFactory implements OrderByTemplateTokenTypes {
/** @Override
* {@inheritDoc}
*/
public Class getASTNodeType(int i) { public Class getASTNodeType(int i) {
switch ( i ) { switch ( i ) {
case ORDER_BY: case ORDER_BY:

View File

@ -0,0 +1,40 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.sql.ordering.antlr;
/**
* Reference to a formula fragment.
*
* @author Steve Ebersole
*/
public interface FormulaReference extends SqlValueReference {
/**
* Retrieve the formula fragment. It is important to note that this is what the persister calls the
* "formula template", which has the $PlaceHolder$ (see {@link org.hibernate.sql.Template#TEMPLATE})
* markers injected.
*
* @return The formula fragment template.
*/
public String getFormulaFragment();
}

View File

@ -1,10 +1,10 @@
/* /*
* Hibernate, Relational Persistence for Idiomatic Java * Hibernate, Relational Persistence for Idiomatic Java
* *
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as * Copyright (c) 2008 Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution * indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are * statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC. * distributed under license by Red Hat Inc.
* *
* This copyrighted material is made available to anyone wishing to use, modify, * This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU * copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,11 +20,9 @@
* Free Software Foundation, Inc. * Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*
*/ */
package org.hibernate.sql.ordering.antlr; package org.hibernate.sql.ordering.antlr;
/** /**
* General contract for AST nodes. * General contract for AST nodes.
* *
@ -48,7 +46,7 @@ public interface Node {
/** /**
* Build the node's representation for use in the resulting rendering. * Build the node's representation for use in the resulting rendering.
* *
* @return The renderable text. * @return The text for use in the translated output.
*/ */
public String getRenderableText(); public String getRenderableText();
} }

View File

@ -1,10 +1,10 @@
/* /*
* Hibernate, Relational Persistence for Idiomatic Java * Hibernate, Relational Persistence for Idiomatic Java
* *
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as * Copyright (c) 2008, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution * indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are * statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC. * distributed under license by Red Hat Inc.
* *
* This copyrighted material is made available to anyone wishing to use, modify, * This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU * copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,27 +20,23 @@
* Free Software Foundation, Inc. * Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*
*/ */
package org.hibernate.sql.ordering.antlr; package org.hibernate.sql.ordering.antlr;
import antlr.CommonAST; import antlr.CommonAST;
/** /**
* Basic implementation of a {@link Node}. * Basic implementation of a {@link Node} briding to the Antlr {@link CommonAST} hierarchy.
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class NodeSupport extends CommonAST implements Node { public class NodeSupport extends CommonAST implements Node {
/** @Override
* {@inheritDoc}
*/
public String getDebugText() { public String getDebugText() {
return getText(); return getText();
} }
/** @Override
* {@inheritDoc}
*/
public String getRenderableText() { public String getRenderableText() {
return getText(); return getText();
} }

View File

@ -0,0 +1,35 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.sql.ordering.antlr;
/**
* Given a column reference, resolve the table alias to apply to the column to qualify it.
*/
public interface OrderByAliasResolver {
/**
* Given a column reference, resolve the table alias to apply to the column to qualify it.
*
*/
public String resolveTableAlias(String columnReference);
}

View File

@ -1,10 +1,10 @@
/* /*
* Hibernate, Relational Persistence for Idiomatic Java * Hibernate, Relational Persistence for Idiomatic Java
* *
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as * Copyright (c) 2008 Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution * indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are * statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC. * distributed under license by Red Hat Inc.
* *
* This copyrighted material is made available to anyone wishing to use, modify, * This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU * copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,11 +20,9 @@
* Free Software Foundation, Inc. * Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*
*/ */
package org.hibernate.sql.ordering.antlr; package org.hibernate.sql.ordering.antlr;
/** /**
* Represents a parsed <tt>order-by</tt> mapping fragment. This holds the tree of all {@link SortSpecification}s. * Represents a parsed <tt>order-by</tt> mapping fragment. This holds the tree of all {@link SortSpecification}s.
* *

View File

@ -1,10 +1,10 @@
/* /*
* Hibernate, Relational Persistence for Idiomatic Java * Hibernate, Relational Persistence for Idiomatic Java
* *
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as * Copyright (c) 2008 Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution * indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are * statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC. * distributed under license by Red Hat Inc.
* *
* This copyrighted material is made available to anyone wishing to use, modify, * This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU * copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,116 +20,99 @@
* Free Software Foundation, Inc. * Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*
*/ */
package org.hibernate.sql.ordering.antlr; package org.hibernate.sql.ordering.antlr;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import antlr.CommonAST; import antlr.CommonAST;
import antlr.TokenStream; import antlr.TokenStream;
import antlr.collections.AST; import antlr.collections.AST;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.hibernate.dialect.function.SQLFunction; import org.hibernate.dialect.function.SQLFunction;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.sql.Template; import org.hibernate.sql.Template;
/** /**
* Extension of the Antlr-generated parser for the purpose of adding our custom parsing behavior. * Extension of the Antlr-generated parser for the purpose of adding our custom parsing behavior
* (semantic analysis, etc).
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class OrderByFragmentParser extends GeneratedOrderByFragmentParser { public class OrderByFragmentParser extends GeneratedOrderByFragmentParser {
private static final Logger LOG = Logger.getLogger(OrderByFragmentParser.class.getName());
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, OrderByFragmentParser.class.getName());
private final TranslationContext context; private final TranslationContext context;
private Set<String> columnReferences = new HashSet<String>();
public OrderByFragmentParser(TokenStream lexer, TranslationContext context) { public OrderByFragmentParser(TokenStream lexer, TranslationContext context) {
super( lexer ); super( lexer );
super.setASTFactory( new Factory() ); super.setASTFactory( new Factory() );
this.context = context; this.context = context;
} }
public Set<String> getColumnReferences() {
// handle trace logging ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ return columnReferences;
private int traceDepth = 0;
@Override
public void traceIn(String ruleName) {
if ( inputState.guessing > 0 ) {
return;
}
String prefix = StringHelper.repeat( '-', (traceDepth++ * 2) ) + "-> ";
LOG.trace(prefix + ruleName);
} }
@Override @Override
public void traceOut(String ruleName) {
if ( inputState.guessing > 0 ) {
return;
}
String prefix = "<-" + StringHelper.repeat( '-', (--traceDepth * 2) ) + " ";
LOG.trace(prefix + ruleName);
}
/**
* {@inheritDoc}
*/
@Override
protected void trace(String msg) {
LOG.trace( msg );
}
/**
* {@inheritDoc}
*/
@Override
protected AST quotedIdentifier(AST ident) { protected AST quotedIdentifier(AST ident) {
return getASTFactory().create( /*
OrderByTemplateTokenTypes.IDENT, * Semantic action used during recognition of quoted identifiers (quoted column names)
Template.TEMPLATE + "." + context.getDialect().quote( '`' + ident.getText() + '`' ) */
); final String columnName = context.getDialect().quote( '`' + ident.getText() + '`' );
columnReferences.add( columnName );
final String marker = '{' + columnName + '}';
return getASTFactory().create( OrderByTemplateTokenTypes.IDENT, marker );
} }
/**
* {@inheritDoc}
*/
@Override @Override
protected AST quotedString(AST ident) { protected AST quotedString(AST ident) {
/*
* Semantic action used during recognition of quoted strings (string literals)
*/
return getASTFactory().create( OrderByTemplateTokenTypes.IDENT, context.getDialect().quote( ident.getText() ) ); return getASTFactory().create( OrderByTemplateTokenTypes.IDENT, context.getDialect().quote( ident.getText() ) );
} }
/**
* {@inheritDoc}
*/
@Override @Override
protected boolean isFunctionName(AST ast) { @SuppressWarnings("SimplifiableIfStatement")
protected boolean isFunctionName(AST ast) {
/*
* Semantic predicate used to determine whether a given AST node represents a function call
*/
AST child = ast.getFirstChild(); AST child = ast.getFirstChild();
// assume it is a function if it has parameters // assume it is a function if it has parameters
if ( child != null && "{param list}".equals( child.getText() ) ) { if ( child != null && "{param list}".equals( child.getText() ) ) {
return true; return true;
} }
// otherwise, in order for this to be a function logically it has to be a function that does not
// have arguments. So try to assert that using the registry of known functions
final SQLFunction function = context.getSqlFunctionRegistry().findSQLFunction( ast.getText() ); final SQLFunction function = context.getSqlFunctionRegistry().findSQLFunction( ast.getText() );
if ( function == null ) { if ( function == null ) {
// no registered function, so we cannot know for certain
return false; return false;
} }
else {
// if function.hasParenthesesIfNoArguments() is true, then assume // if function.hasParenthesesIfNoArguments() is true, then assume the node is not a function
// ast.getText() is not a function. return ! function.hasParenthesesIfNoArguments();
return ! function.hasParenthesesIfNoArguments(); }
} }
/** @SuppressWarnings("unchecked")
* {@inheritDoc}
*/
@Override @Override
protected AST resolveFunction(AST ast) { protected AST resolveFunction(AST ast) {
/*
* Semantic action used during recognition of a *known* function
*/
AST child = ast.getFirstChild(); AST child = ast.getFirstChild();
if ( child != null ) { if ( child != null ) {
assert "{param list}".equals( child.getText() ); assert "{param list}".equals( child.getText() );
@ -143,7 +126,7 @@ public class OrderByFragmentParser extends GeneratedOrderByFragmentParser {
if ( child != null ) { if ( child != null ) {
text += '('; text += '(';
while ( child != null ) { while ( child != null ) {
text += child.getText(); text += resolveFunctionArgument( child );
child = child.getNextSibling(); child = child.getNextSibling();
if ( child != null ) { if ( child != null ) {
text += ", "; text += ", ";
@ -156,7 +139,7 @@ public class OrderByFragmentParser extends GeneratedOrderByFragmentParser {
else { else {
ArrayList expressions = new ArrayList(); ArrayList expressions = new ArrayList();
while ( child != null ) { while ( child != null ) {
expressions.add( child.getText() ); expressions.add( resolveFunctionArgument( child ) );
child = child.getNextSibling(); child = child.getNextSibling();
} }
final String text = function.render( null, expressions, context.getSessionFactory() ); final String text = function.render( null, expressions, context.getSessionFactory() );
@ -164,39 +147,119 @@ public class OrderByFragmentParser extends GeneratedOrderByFragmentParser {
} }
} }
/** private String resolveFunctionArgument(AST argumentNode) {
* {@inheritDoc} final String nodeText = argumentNode.getText();
*/ final String adjustedText;
if ( nodeText.contains( Template.TEMPLATE ) ) {
// we have a SQL order-by fragment
adjustedText = adjustTemplateReferences( nodeText );
}
else if ( nodeText.startsWith( "{" ) && nodeText.endsWith( "}" ) ) {
columnReferences.add( nodeText.substring( 1, nodeText.length() - 1 ) );
return nodeText;
}
else {
adjustedText = nodeText;
// because we did not process the node text, we need to attempt to find any column references
// contained in it.
// NOTE : uses regex for the time being; we should check the performance of this
Pattern pattern = Pattern.compile( "\\{(.*)\\}" );
Matcher matcher = pattern.matcher( adjustedText );
while ( matcher.find() ) {
columnReferences.add( matcher.group( 1 ) );
}
}
return adjustedText;
}
@Override @Override
protected AST resolveIdent(AST ident) { protected AST resolveIdent(AST ident) {
/*
* Semantic action used during recognition of an identifier. This identifier might be a column name, it might
* be a property name.
*/
String text = ident.getText(); String text = ident.getText();
String[] replacements; SqlValueReference[] sqlValueReferences;
try { try {
replacements = context.getColumnMapper().map( text ); sqlValueReferences = context.getColumnMapper().map( text );
} }
catch( Throwable t ) { catch( Throwable t ) {
replacements = null; sqlValueReferences = null;
} }
if ( replacements == null || replacements.length == 0 ) { if ( sqlValueReferences == null || sqlValueReferences.length == 0 ) {
return getASTFactory().create( OrderByTemplateTokenTypes.IDENT, Template.TEMPLATE + "." + text ); return getASTFactory().create( OrderByTemplateTokenTypes.IDENT, makeColumnReference( text ) );
} }
else if ( replacements.length == 1 ) { else if ( sqlValueReferences.length == 1 ) {
return getASTFactory().create( OrderByTemplateTokenTypes.IDENT, Template.TEMPLATE + "." + replacements[0] ); return processSqlValueReference( sqlValueReferences[0] );
} }
else { else {
final AST root = getASTFactory().create( OrderByTemplateTokenTypes.IDENT_LIST, "{ident list}" ); final AST root = getASTFactory().create( OrderByTemplateTokenTypes.IDENT_LIST, "{ident list}" );
for ( int i = 0; i < replacements.length; i++ ) { for ( SqlValueReference sqlValueReference : sqlValueReferences ) {
final String identText = Template.TEMPLATE + '.' + replacements[i]; root.addChild( processSqlValueReference( sqlValueReference ) );
root.addChild( getASTFactory().create( OrderByTemplateTokenTypes.IDENT, identText ) );
} }
return root; return root;
} }
} }
/** private AST processSqlValueReference(SqlValueReference sqlValueReference) {
* {@inheritDoc} if ( ColumnReference.class.isInstance( sqlValueReference ) ) {
*/ final String columnName = ( (ColumnReference) sqlValueReference ).getColumnName();
return getASTFactory().create( OrderByTemplateTokenTypes.IDENT, makeColumnReference( columnName ) );
}
else {
final String formulaFragment = ( (FormulaReference) sqlValueReference ).getFormulaFragment();
// formulas have already been "adjusted" for aliases by appending Template.TEMPLATE to places
// where we believe column references are. Fixing that is beyond scope of this work. But we need
// to re-adjust that to use the order-by expectation of wrapping the column names in curly
// braces (i.e., `{column_name}`).
final String adjustedText = adjustTemplateReferences( formulaFragment );
return getASTFactory().create( OrderByTemplateTokenTypes.IDENT, adjustedText );
}
}
private String makeColumnReference(String text) {
columnReferences.add( text );
return "{" + text + "}";
}
private static final int TEMPLATE_MARKER_LENGTH = Template.TEMPLATE.length();
private String adjustTemplateReferences(String template) {
int templateLength = template.length();
int startPos = template.indexOf( Template.TEMPLATE );
while ( startPos < templateLength ) {
int dotPos = startPos + TEMPLATE_MARKER_LENGTH;
// from here we need to seek the end of the qualified identifier
int pos = dotPos + 1;
while ( pos < templateLength && isValidIdentifierCharacter( template.charAt( pos ) ) ) {
pos++;
}
// At this point we know all 3 points in the template that are needed for replacement.
// Basically we will be replacing the whole match with the bit following the dot, but will wrap
// the replacement in curly braces.
final String columnReference = template.substring( dotPos + 1, pos );
final String replacement = "{" + columnReference + "}";
template = template.replace( template.substring( startPos, pos ), replacement );
columnReferences.add( columnReference );
// prep for the next seek
startPos = ( pos - TEMPLATE_MARKER_LENGTH ) + 1;
templateLength = template.length();
}
return template;
}
private static boolean isValidIdentifierCharacter(char c) {
return Character.isLetter( c )
|| Character.isDigit( c )
|| '_' == c
|| '\"' == c;
}
@Override @Override
protected AST postProcessSortSpecification(AST sortSpec) { protected AST postProcessSortSpecification(AST sortSpec) {
assert SORT_SPEC == sortSpec.getType(); assert SORT_SPEC == sortSpec.getType();
@ -238,4 +301,34 @@ public class OrderByFragmentParser extends GeneratedOrderByFragmentParser {
} }
return ( SortSpecification ) sortSpecification; return ( SortSpecification ) sortSpecification;
} }
// trace logging ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private int traceDepth = 0;
@Override
public void traceIn(String ruleName) {
if ( inputState.guessing > 0 ) {
return;
}
String prefix = StringHelper.repeat( '-', (traceDepth++ * 2) ) + "-> ";
LOG.trace(prefix + ruleName);
}
@Override
public void traceOut(String ruleName) {
if ( inputState.guessing > 0 ) {
return;
}
String prefix = "<-" + StringHelper.repeat( '-', (--traceDepth * 2) ) + " ";
LOG.trace(prefix + ruleName);
}
@Override
protected void trace(String msg) {
LOG.trace( msg );
}
} }

View File

@ -1,10 +1,10 @@
/* /*
* Hibernate, Relational Persistence for Idiomatic Java * Hibernate, Relational Persistence for Idiomatic Java
* *
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as * Copyright (c) 2008 Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution * indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are * statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC. * distributed under license by Red Hat Inc.
* *
* This copyrighted material is made available to anyone wishing to use, modify, * This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU * copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,24 +20,25 @@
* Free Software Foundation, Inc. * Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*
*/ */
package org.hibernate.sql.ordering.antlr; package org.hibernate.sql.ordering.antlr;
import antlr.collections.AST; import antlr.collections.AST;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.hibernate.hql.internal.ast.util.ASTPrinter; import org.hibernate.hql.internal.ast.util.ASTPrinter;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
/** /**
* TODO : javadoc * Extension of the Antlr-generated tree walker for rendering the parsed order-by tree back to String form.
* {@link #out(antlr.collections.AST)} is the sole semantic action here and it is used to utilize our
* split between text (tree debugging text) and "renderable text" (text to use during rendering).
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class OrderByFragmentRenderer extends GeneratedOrderByFragmentRenderer { public class OrderByFragmentRenderer extends GeneratedOrderByFragmentRenderer {
private static final CoreMessageLogger LOG = Logger.getMessageLogger( CoreMessageLogger.class, OrderByFragmentRenderer.class.getName() ); private static final Logger LOG = Logger.getLogger( OrderByFragmentRenderer.class.getName() );
private static final ASTPrinter printer = new ASTPrinter( GeneratedOrderByFragmentRendererTokenTypes.class ); private static final ASTPrinter printer = new ASTPrinter( GeneratedOrderByFragmentRendererTokenTypes.class );
@Override @Override

View File

@ -1,10 +1,10 @@
/* /*
* Hibernate, Relational Persistence for Idiomatic Java * Hibernate, Relational Persistence for Idiomatic Java
* *
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as * Copyright (c) 2008 Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution * indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are * statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC. * distributed under license by Red Hat Inc.
* *
* This copyrighted material is made available to anyone wishing to use, modify, * This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU * copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,41 +20,44 @@
* Free Software Foundation, Inc. * Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*
*/ */
package org.hibernate.sql.ordering.antlr; package org.hibernate.sql.ordering.antlr;
import java.io.StringReader; import java.io.StringReader;
import java.util.Set;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.hql.internal.ast.util.ASTPrinter; import org.hibernate.hql.internal.ast.util.ASTPrinter;
import org.hibernate.internal.CoreMessageLogger;
/** /**
* A translator which coordinates translation of an <tt>order-by</tt> mapping. * A translator for order-by mappings, whether specified by hbm.xml files, Hibernate
* {@link org.hibernate.annotations.OrderBy} annotation or JPA {@link javax.persistence.OrderBy} annotation.
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class OrderByFragmentTranslator { public class OrderByFragmentTranslator {
private static final Logger LOG = Logger.getLogger( OrderByFragmentTranslator.class.getName() );
private static final CoreMessageLogger LOG = Logger.getMessageLogger( CoreMessageLogger.class, OrderByFragmentTranslator.class.getName() );
public final TranslationContext context;
public OrderByFragmentTranslator(TranslationContext context) {
this.context = context;
}
/** /**
* The main contract, performing the transaction. * Perform the translation of the user-supplied fragment, returning the translation.
* <p/>
* The important distinction to this split between (1) translating and (2) resolving aliases is that
* both happen at different times
* *
* @param fragment The <tt>order-by</tt> mapping fragment to be translated.
* *
* @return The translated fragment. * @param context Context giving access to delegates needed during translation.
* @param fragment The user-supplied order-by fragment
*
* @return The translation.
*/ */
public String render(String fragment) { public static OrderByTranslation translate(TranslationContext context, String fragment) {
GeneratedOrderByLexer lexer = new GeneratedOrderByLexer( new StringReader( fragment ) ); GeneratedOrderByLexer lexer = new GeneratedOrderByLexer( new StringReader( fragment ) );
// Perform the parsing (and some analysis/resolution). Another important aspect is the collection
// of "column references" which are important later to seek out replacement points in the
// translated fragment.
OrderByFragmentParser parser = new OrderByFragmentParser( lexer, context ); OrderByFragmentParser parser = new OrderByFragmentParser( lexer, context );
try { try {
parser.orderByFragment(); parser.orderByFragment();
@ -71,6 +74,7 @@ public class OrderByFragmentTranslator {
LOG.trace( printer.showAsString( parser.getAST(), "--- {order-by fragment} ---" ) ); LOG.trace( printer.showAsString( parser.getAST(), "--- {order-by fragment} ---" ) );
} }
// Render the parsed tree to text.
OrderByFragmentRenderer renderer = new OrderByFragmentRenderer(); OrderByFragmentRenderer renderer = new OrderByFragmentRenderer();
try { try {
renderer.orderByFragment( parser.getAST() ); renderer.orderByFragment( parser.getAST() );
@ -82,6 +86,29 @@ public class OrderByFragmentTranslator {
throw new HibernateException( "Unable to render parsed order-by fragment", t ); throw new HibernateException( "Unable to render parsed order-by fragment", t );
} }
return renderer.getRenderedFragment(); return new StandardOrderByTranslationImpl( renderer.getRenderedFragment(), parser.getColumnReferences() );
}
public static class StandardOrderByTranslationImpl implements OrderByTranslation {
private final String sqlTemplate;
private final Set<String> columnReferences;
public StandardOrderByTranslationImpl(String sqlTemplate, Set<String> columnReferences) {
this.sqlTemplate = sqlTemplate;
this.columnReferences = columnReferences;
}
@Override
public String injectAliases(OrderByAliasResolver aliasResolver) {
String sql = sqlTemplate;
for ( String columnReference : columnReferences ) {
final String replacementToken = "{" + columnReference + "}";
sql = sql.replace(
replacementToken,
aliasResolver.resolveTableAlias( columnReference ) + '.' + columnReference
);
}
return sql;
}
} }
} }

View File

@ -0,0 +1,41 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.sql.ordering.antlr;
/**
* Represents the result of an order-by translation by {@link @OrderByTranslator}
*
* @author Steve Ebersole
*/
public interface OrderByTranslation {
/**
* Inject table aliases into the translated fragment to properly qualify column references, using
* the given 'aliasResolver' to determine the the proper table alias to use for each column reference.
*
* @param aliasResolver The strategy to resolver the proper table alias to use per column
*
* @return The fully translated and replaced fragment.
*/
public String injectAliases(OrderByAliasResolver aliasResolver);
}

View File

@ -1,10 +1,10 @@
/* /*
* Hibernate, Relational Persistence for Idiomatic Java * Hibernate, Relational Persistence for Idiomatic Java
* *
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as * Copyright (c) 2008 Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution * indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are * statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC. * distributed under license by Red Hat Inc.
* *
* This copyrighted material is made available to anyone wishing to use, modify, * This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU * copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,11 +20,9 @@
* Free Software Foundation, Inc. * Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*
*/ */
package org.hibernate.sql.ordering.antlr; package org.hibernate.sql.ordering.antlr;
/** /**
* Models an ordering specification (<tt>ASCENDING</tt> or <tt>DESCENDING</tt>) within a {@link SortSpecification}. * Models an ordering specification (<tt>ASCENDING</tt> or <tt>DESCENDING</tt>) within a {@link SortSpecification}.
* *

View File

@ -1,10 +1,10 @@
/* /*
* Hibernate, Relational Persistence for Idiomatic Java * Hibernate, Relational Persistence for Idiomatic Java
* *
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as * Copyright (c) 2008 Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution * indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are * statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC. * distributed under license by Red Hat Inc.
* *
* This copyrighted material is made available to anyone wishing to use, modify, * This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU * copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,7 +20,6 @@
* Free Software Foundation, Inc. * Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*
*/ */
package org.hibernate.sql.ordering.antlr; package org.hibernate.sql.ordering.antlr;

View File

@ -1,10 +1,10 @@
/* /*
* Hibernate, Relational Persistence for Idiomatic Java * Hibernate, Relational Persistence for Idiomatic Java
* *
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as * Copyright (c) 2008 Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution * indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are * statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC. * distributed under license by Red Hat Inc.
* *
* This copyrighted material is made available to anyone wishing to use, modify, * This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU * copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,13 +20,13 @@
* Free Software Foundation, Inc. * Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*
*/ */
package org.hibernate.sql.ordering.antlr; package org.hibernate.sql.ordering.antlr;
import antlr.collections.AST; import antlr.collections.AST;
/** /**
* Models each sorting exprersion. * Models each sorting expression.
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */

View File

@ -0,0 +1,36 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.sql.ordering.antlr;
/**
* Unifying interface between column and formula references mainly to give more strictly typed result
* to {@link ColumnMapper#map(String)}
*
* @see ColumnReference
* @see FormulaReference
*
* @author Steve Ebersole
*/
public interface SqlValueReference {
}

View File

@ -1,10 +1,10 @@
/* /*
* Hibernate, Relational Persistence for Idiomatic Java * Hibernate, Relational Persistence for Idiomatic Java
* *
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as * Copyright (c) 2008 Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution * indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are * statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC. * distributed under license by Red Hat Inc.
* *
* This copyrighted material is made available to anyone wishing to use, modify, * This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU * copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,9 +20,9 @@
* Free Software Foundation, Inc. * Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*
*/ */
package org.hibernate.sql.ordering.antlr; package org.hibernate.sql.ordering.antlr;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.function.SQLFunctionRegistry; import org.hibernate.dialect.function.SQLFunctionRegistry;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;

View File

@ -34,6 +34,9 @@ import org.hibernate.dialect.function.SQLFunctionRegistry;
import org.hibernate.persister.entity.PropertyMapping; import org.hibernate.persister.entity.PropertyMapping;
import org.hibernate.sql.ordering.antlr.ColumnMapper; import org.hibernate.sql.ordering.antlr.ColumnMapper;
import org.hibernate.testing.junit4.BaseUnitTestCase; import org.hibernate.testing.junit4.BaseUnitTestCase;
import org.hibernate.sql.ordering.antlr.ColumnReference;
import org.hibernate.sql.ordering.antlr.SqlValueReference;
import org.hibernate.type.Type; import org.hibernate.type.Type;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@ -76,10 +79,22 @@ public class TemplateTest extends BaseUnitTestCase {
}; };
private static final ColumnMapper MAPPER = new ColumnMapper() { private static final ColumnMapper MAPPER = new ColumnMapper() {
public String[] map(String reference) { public SqlValueReference[] map(String reference) {
return PROPERTY_MAPPING.toColumns( reference ); final String[] columnNames = PROPERTY_MAPPING.toColumns( reference );
final SqlValueReference[] result = new SqlValueReference[ columnNames.length ];
int i = 0;
for ( final String columnName : columnNames ) {
result[i] = new ColumnReference() {
@Override
public String getColumnName() {
return columnName;
}
};
i++;
}
return result;
} }
}; };
private static final Dialect DIALECT = new HSQLDialect(); private static final Dialect DIALECT = new HSQLDialect();

View File

@ -0,0 +1,60 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.annotations.collectionelement.ordered;
import javax.persistence.Embeddable;
/**
* @author Steve Ebersole
*/
@Embeddable
public class Address {
private String street;
private String city;
private String country;
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
}

View File

@ -0,0 +1,57 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.annotations.collectionelement.ordered;
import org.hibernate.Session;
import org.junit.Test;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
/**
* @author Steve Ebersole
*/
public class ElementCollectionSortingTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { Person.class };
}
@Test
public void testSortingElementCollectionSyntax() {
Session session = openSession();
session.beginTransaction();
session.createQuery( "from Person p join fetch p.nickNamesAscendingNaturalSort" ).list();
session.createQuery( "from Person p join fetch p.nickNamesDescendingNaturalSort" ).list();
session.createQuery( "from Person p join fetch p.addressesAscendingNaturalSort" ).list();
session.createQuery( "from Person p join fetch p.addressesDescendingNaturalSort" ).list();
session.createQuery( "from Person p join fetch p.addressesCityAscendingSort" ).list();
session.createQuery( "from Person p join fetch p.addressesCityDescendingSort" ).list();
session.getTransaction().commit();
session.close();
}
}

View File

@ -0,0 +1,141 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.annotations.collectionelement.ordered;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OrderBy;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.annotations.GenericGenerator;
/**
* @author Steve Ebersole
*/
@Entity
public class Person {
private Long id;
private String name;
private Set<String> nickNamesAscendingNaturalSort = new HashSet<String>();
private Set<String> nickNamesDescendingNaturalSort = new HashSet<String>();
private Set<Address> addressesAscendingNaturalSort = new HashSet<Address>();
private Set<Address> addressesDescendingNaturalSort = new HashSet<Address>();
private Set<Address> addressesCityAscendingSort = new HashSet<Address>();
private Set<Address> addressesCityDescendingSort = new HashSet<Address>();
@Id
@GeneratedValue( generator = "increment" )
@GenericGenerator( name = "increment", strategy = "increment" )
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ElementCollection
@JoinColumn
@OrderBy
public Set<String> getNickNamesAscendingNaturalSort() {
return nickNamesAscendingNaturalSort;
}
public void setNickNamesAscendingNaturalSort(Set<String> nickNamesAscendingNaturalSort) {
this.nickNamesAscendingNaturalSort = nickNamesAscendingNaturalSort;
}
@ElementCollection
@JoinColumn
@OrderBy( "desc" )
public Set<String> getNickNamesDescendingNaturalSort() {
return nickNamesDescendingNaturalSort;
}
public void setNickNamesDescendingNaturalSort(Set<String> nickNamesDescendingNaturalSort) {
this.nickNamesDescendingNaturalSort = nickNamesDescendingNaturalSort;
}
@ElementCollection
@JoinColumn
@OrderBy
public Set<Address> getAddressesAscendingNaturalSort() {
return addressesAscendingNaturalSort;
}
public void setAddressesAscendingNaturalSort(Set<Address> addressesAscendingNaturalSort) {
this.addressesAscendingNaturalSort = addressesAscendingNaturalSort;
}
@ElementCollection
@JoinColumn
@OrderBy( "desc" )
public Set<Address> getAddressesDescendingNaturalSort() {
return addressesDescendingNaturalSort;
}
public void setAddressesDescendingNaturalSort(Set<Address> addressesDescendingNaturalSort) {
this.addressesDescendingNaturalSort = addressesDescendingNaturalSort;
}
@ElementCollection
@JoinColumn
@OrderBy( "city" )
public Set<Address> getAddressesCityAscendingSort() {
return addressesCityAscendingSort;
}
public void setAddressesCityAscendingSort(Set<Address> addressesCityAscendingSort) {
this.addressesCityAscendingSort = addressesCityAscendingSort;
}
@ElementCollection
@JoinColumn
@OrderBy( "city desc" )
public Set<Address> getAddressesCityDescendingSort() {
return addressesCityDescendingSort;
}
public void setAddressesCityDescendingSort(Set<Address> addressesCityDescendingSort) {
this.addressesCityDescendingSort = addressesCityDescendingSort;
}
}

View File

@ -3,6 +3,7 @@ package org.hibernate.test.annotations.onetomany;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue; import javax.persistence.GeneratedValue;
import javax.persistence.Id; import javax.persistence.Id;
import javax.persistence.JoinColumn; import javax.persistence.JoinColumn;
@ -50,7 +51,7 @@ class City {
this.streets = streets; this.streets = streets;
} }
@OneToMany() @OneToMany
@JoinColumn(name = "mainstreetcity_id") @JoinColumn(name = "mainstreetcity_id")
@ForeignKey(name = "CITYSTR_FK") @ForeignKey(name = "CITYSTR_FK")
@OrderBy @OrderBy

View File

@ -32,6 +32,7 @@ import java.util.Set;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.TreeSet; import java.util.TreeSet;
import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.hibernate.Hibernate; import org.hibernate.Hibernate;
@ -113,10 +114,17 @@ public class OneToManyTest extends BaseCoreFunctionalTestCase {
s.flush(); s.flush();
s.clear(); s.clear();
//testing @OrderBy with explicit values including Formula // Assert the primary key value relationship amongst the 3 streets...
Assert.assertTrue( rochechoir.getId() < chmpsElysees.getId() );
Assert.assertTrue( chmpsElysees.getId() < grandeArmee.getId() );
paris = ( City ) s.get( City.class, paris.getId() ); paris = ( City ) s.get( City.class, paris.getId() );
// City.streets is defined to be ordered by name primarily...
assertEquals( 3, paris.getStreets().size() ); assertEquals( 3, paris.getStreets().size() );
assertEquals( chmpsElysees.getStreetName(), paris.getStreets().get( 0 ).getStreetName() ); assertEquals( chmpsElysees.getStreetName(), paris.getStreets().get( 0 ).getStreetName() );
assertEquals( grandeArmee.getStreetName(), paris.getStreets().get( 1 ).getStreetName() );
// City.mainStreets is defined to be ordered by street id
List<Street> mainStreets = paris.getMainStreets(); List<Street> mainStreets = paris.getMainStreets();
assertEquals( 2, mainStreets.size() ); assertEquals( 2, mainStreets.size() );
Integer previousId = -1; Integer previousId = -1;
@ -445,7 +453,7 @@ public class OneToManyTest extends BaseCoreFunctionalTestCase {
} }
@Test @Test
@FailureExpected(jiraKey = "HHH-3577") @TestForIssue( jiraKey = "HHH-4394" )
public void testOrderByOnSuperclassProperty() { public void testOrderByOnSuperclassProperty() {
OrganisationUser user = new OrganisationUser(); OrganisationUser user = new OrganisationUser();
user.setFirstName( "Emmanuel" ); user.setFirstName( "Emmanuel" );

View File

@ -0,0 +1,61 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.collection.ordered.joinedInheritence;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import org.hibernate.annotations.GenericGenerator;
/**
* @author Steve Ebersole
*/
@Entity
@Inheritance( strategy = InheritanceType.JOINED )
public class Animal {
private Long id;
private long weight;
@Id
@GeneratedValue( generator = "increment" )
@GenericGenerator( strategy = "increment", name = "increment" )
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public long getWeight() {
return weight;
}
public void setWeight(long weight) {
this.weight = weight;
}
}

View File

@ -0,0 +1,33 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.collection.ordered.joinedInheritence;
import javax.persistence.Entity;
/**
* @author Steve Ebersole
*/
@Entity
public class Lion extends Animal {
}

View File

@ -0,0 +1,49 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.collection.ordered.joinedInheritence;
import org.hibernate.Session;
import org.junit.Test;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
/**
* @author Steve Ebersole
*/
public class OrderCollectionOfJoinedHierarchyTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { Animal.class, Lion.class, Tiger.class, Zoo.class };
}
@Test
public void testQuerySyntaxCheck() {
Session session = openSession();
session.beginTransaction();
session.get( Zoo.class, 1L );
session.getTransaction().commit();
session.close();
}
}

View File

@ -0,0 +1,42 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.collection.ordered.joinedInheritence;
import javax.persistence.Entity;
/**
* @author Steve Ebersole
*/
@Entity
public class Tiger extends Animal {
private int numberOfStripes;
public int getNumberOfStripes() {
return numberOfStripes;
}
public void setNumberOfStripes(int numberOfStripes) {
this.numberOfStripes = numberOfStripes;
}
}

View File

@ -0,0 +1,97 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.collection.ordered.joinedInheritence;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.annotations.GenericGenerator;
/**
* @author Steve Ebersole
*/
@Entity
public class Zoo {
private Long id;
private String name;
private String city;
private Set<Tiger> tigers = new HashSet<Tiger>();
private Set<Lion> lions = new HashSet<Lion>();
@Id
@GeneratedValue( generator = "increment" )
@GenericGenerator( strategy = "increment", name = "increment" )
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
@OneToMany( fetch = FetchType.EAGER ) // eager so that load-by-id queries include this association
@JoinColumn
@javax.persistence.OrderBy( "weight" )
public Set<Tiger> getTigers() {
return tigers;
}
public void setTigers(Set<Tiger> tigers) {
this.tigers = tigers;
}
@OneToMany( fetch = FetchType.EAGER ) // eager so that load-by-id queries include this association
@JoinColumn
@org.hibernate.annotations.OrderBy( clause = "weight" )
public Set<Lion> getLions() {
return lions;
}
public void setLions(Set<Lion> lions) {
this.lions = lions;
}
}

View File

@ -192,6 +192,16 @@ public class ASTParserLoadingOrderByTest extends BaseCoreFunctionalTestCase {
s.close(); s.close();
} }
@Test
public void testOrderByOnJoinedSubclassPropertyWhoseColumnIsNotInDrivingTable() {
// this is simply a syntax check
Session s = openSession();
Transaction t = s.beginTransaction();
s.createQuery( "from Human h order by h.bodyWeight" ).list();
s.getTransaction().commit();
s.close();
}
@Test @Test
public void testOrderByNoSelectAliasRef() { public void testOrderByNoSelectAliasRef() {
createData(); createData();

View File

@ -12,4 +12,6 @@ log4j.logger.org.hibernate.testing.cache=debug
# SQL Logging - HHH-6833 # SQL Logging - HHH-6833
log4j.logger.org.hibernate.SQL=debug log4j.logger.org.hibernate.SQL=debug
log4j.logger.org.hibernate.hql.internal.ast=debug log4j.logger.org.hibernate.hql.internal.ast=debug
log4j.logger.org.hibernate.sql.ordering.antlr=trace