HHH-12122 - Checking @OrderBy for special cases should perform case-insensitive checking
This commit is contained in:
parent
65e44267d6
commit
f49efc7864
|
@ -112,7 +112,7 @@ import static org.hibernate.cfg.BinderHelper.toAliasTableMap;
|
||||||
* @author inger
|
* @author inger
|
||||||
* @author Emmanuel Bernard
|
* @author Emmanuel Bernard
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({"unchecked", "serial"})
|
@SuppressWarnings({"unchecked", "serial", "WeakerAccess", "deprecation"})
|
||||||
public abstract class CollectionBinder {
|
public abstract class CollectionBinder {
|
||||||
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, CollectionBinder.class.getName());
|
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, CollectionBinder.class.getName());
|
||||||
|
|
||||||
|
@ -121,14 +121,14 @@ public abstract class CollectionBinder {
|
||||||
protected Collection collection;
|
protected Collection collection;
|
||||||
protected String propertyName;
|
protected String propertyName;
|
||||||
PropertyHolder propertyHolder;
|
PropertyHolder propertyHolder;
|
||||||
int batchSize;
|
private int batchSize;
|
||||||
private String mappedBy;
|
private String mappedBy;
|
||||||
private XClass collectionType;
|
private XClass collectionType;
|
||||||
private XClass targetEntity;
|
private XClass targetEntity;
|
||||||
private Ejb3JoinColumn[] inverseJoinColumns;
|
private Ejb3JoinColumn[] inverseJoinColumns;
|
||||||
private String cascadeStrategy;
|
private String cascadeStrategy;
|
||||||
String cacheConcurrencyStrategy;
|
private String cacheConcurrencyStrategy;
|
||||||
String cacheRegionName;
|
private String cacheRegionName;
|
||||||
private boolean oneToMany;
|
private boolean oneToMany;
|
||||||
protected IndexColumn indexColumn;
|
protected IndexColumn indexColumn;
|
||||||
protected boolean cascadeDeleteEnabled;
|
protected boolean cascadeDeleteEnabled;
|
||||||
|
@ -161,6 +161,10 @@ public abstract class CollectionBinder {
|
||||||
private String explicitType;
|
private String explicitType;
|
||||||
private final Properties explicitTypeParameters = new Properties();
|
private final Properties explicitTypeParameters = new Properties();
|
||||||
|
|
||||||
|
protected CollectionBinder(boolean isSortedCollection) {
|
||||||
|
this.isSortedCollection = isSortedCollection;
|
||||||
|
}
|
||||||
|
|
||||||
protected MetadataBuildingContext getBuildingContext() {
|
protected MetadataBuildingContext getBuildingContext() {
|
||||||
return buildingContext;
|
return buildingContext;
|
||||||
}
|
}
|
||||||
|
@ -173,7 +177,7 @@ public abstract class CollectionBinder {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setIsHibernateExtensionMapping(boolean hibernateExtensionMapping) {
|
protected void setIsHibernateExtensionMapping(boolean hibernateExtensionMapping) {
|
||||||
this.hibernateExtensionMapping = hibernateExtensionMapping;
|
this.hibernateExtensionMapping = hibernateExtensionMapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -347,10 +351,6 @@ public abstract class CollectionBinder {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected CollectionBinder(boolean isSortedCollection) {
|
|
||||||
this.isSortedCollection = isSortedCollection;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMappedBy(String mappedBy) {
|
public void setMappedBy(String mappedBy) {
|
||||||
this.mappedBy = mappedBy;
|
this.mappedBy = mappedBy;
|
||||||
}
|
}
|
||||||
|
@ -533,7 +533,7 @@ public abstract class CollectionBinder {
|
||||||
binder.setName( propertyName );
|
binder.setName( propertyName );
|
||||||
binder.setValue( collection );
|
binder.setValue( collection );
|
||||||
binder.setCascade( cascadeStrategy );
|
binder.setCascade( cascadeStrategy );
|
||||||
if ( cascadeStrategy != null && cascadeStrategy.indexOf( "delete-orphan" ) >= 0 ) {
|
if ( cascadeStrategy != null && cascadeStrategy.contains( "delete-orphan" ) ) {
|
||||||
collection.setOrphanDelete( true );
|
collection.setOrphanDelete( true );
|
||||||
}
|
}
|
||||||
binder.setLazy( collection.isLazy() );
|
binder.setLazy( collection.isLazy() );
|
||||||
|
@ -552,8 +552,6 @@ public abstract class CollectionBinder {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applySortingAndOrdering(Collection collection) {
|
private void applySortingAndOrdering(Collection collection) {
|
||||||
boolean isSorted = isSortedCollection;
|
|
||||||
|
|
||||||
boolean hadOrderBy = false;
|
boolean hadOrderBy = false;
|
||||||
boolean hadExplicitSort = false;
|
boolean hadExplicitSort = false;
|
||||||
|
|
||||||
|
@ -567,10 +565,10 @@ public abstract class CollectionBinder {
|
||||||
}
|
}
|
||||||
hadExplicitSort = deprecatedSort.type() != SortType.UNSORTED;
|
hadExplicitSort = deprecatedSort.type() != SortType.UNSORTED;
|
||||||
if ( deprecatedSort.type() == SortType.NATURAL ) {
|
if ( deprecatedSort.type() == SortType.NATURAL ) {
|
||||||
isSorted = true;
|
isSortedCollection = true;
|
||||||
}
|
}
|
||||||
else if ( deprecatedSort.type() == SortType.COMPARATOR ) {
|
else if ( deprecatedSort.type() == SortType.COMPARATOR ) {
|
||||||
isSorted = true;
|
isSortedCollection = true;
|
||||||
comparatorClass = deprecatedSort.comparator();
|
comparatorClass = deprecatedSort.comparator();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -649,7 +647,7 @@ public abstract class CollectionBinder {
|
||||||
Fetch fetch = property.getAnnotation( Fetch.class );
|
Fetch fetch = property.getAnnotation( Fetch.class );
|
||||||
OneToMany oneToMany = property.getAnnotation( OneToMany.class );
|
OneToMany oneToMany = property.getAnnotation( OneToMany.class );
|
||||||
ManyToMany manyToMany = property.getAnnotation( ManyToMany.class );
|
ManyToMany manyToMany = property.getAnnotation( ManyToMany.class );
|
||||||
ElementCollection elementCollection = property.getAnnotation( ElementCollection.class ); //jpa 2
|
ElementCollection elementCollection = property.getAnnotation( ElementCollection.class );
|
||||||
ManyToAny manyToAny = property.getAnnotation( ManyToAny.class );
|
ManyToAny manyToAny = property.getAnnotation( ManyToAny.class );
|
||||||
FetchType fetchType;
|
FetchType fetchType;
|
||||||
if ( oneToMany != null ) {
|
if ( oneToMany != null ) {
|
||||||
|
@ -775,14 +773,14 @@ public abstract class CollectionBinder {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
catch (MappingException e) {
|
catch (MappingException e) {
|
||||||
StringBuilder error = new StringBuilder( 80 );
|
throw new AnnotationException(
|
||||||
error.append( "mappedBy reference an unknown target entity property: " )
|
"mappedBy reference an unknown target entity property: " +
|
||||||
.append( collType ).append( "." ).append( this.mappedBy )
|
collType + "." + this.mappedBy +
|
||||||
.append( " in " )
|
" in " +
|
||||||
.append( collection.getOwnerEntityName() )
|
collection.getOwnerEntityName() +
|
||||||
.append( "." )
|
"." +
|
||||||
.append( property.getName() );
|
property.getName()
|
||||||
throw new AnnotationException( error.toString() );
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( persistentClass != null
|
if ( persistentClass != null
|
||||||
|
@ -1011,13 +1009,6 @@ public abstract class CollectionBinder {
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getCondition(FilterJoinTable filter) {
|
|
||||||
//set filtering
|
|
||||||
String name = filter.name();
|
|
||||||
String cond = filter.condition();
|
|
||||||
return getCondition( cond, name );
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getCondition(Filter filter) {
|
private String getCondition(Filter filter) {
|
||||||
//set filtering
|
//set filtering
|
||||||
String name = filter.name();
|
String name = filter.name();
|
||||||
|
@ -1076,17 +1067,30 @@ public abstract class CollectionBinder {
|
||||||
return orderByFragment;
|
return orderByFragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String adjustUserSuppliedValueCollectionOrderingFragment(String orderByFragment) {
|
public static String adjustUserSuppliedValueCollectionOrderingFragment(String orderByFragment) {
|
||||||
|
LOG.debugf( "#adjustUserSuppliedValueCollectionOrderingFragment(%)", orderByFragment );
|
||||||
|
|
||||||
if ( orderByFragment != null ) {
|
if ( orderByFragment != null ) {
|
||||||
// NOTE: "$element$" is a specially recognized collection property recognized by the collection persister
|
orderByFragment = orderByFragment.trim();
|
||||||
if ( orderByFragment.length() == 0 ) {
|
if ( orderByFragment.length() == 0 || orderByFragment.equalsIgnoreCase( "asc" ) ) {
|
||||||
//order by element
|
// This indicates something like either:
|
||||||
|
// `@OrderBy()`
|
||||||
|
// `@OrderBy("asc")
|
||||||
|
//
|
||||||
|
// JPA says this should indicate an ascending natural ordering of the elements - id for
|
||||||
|
// entity associations or the value(s) for "element collections"
|
||||||
return "$element$ asc";
|
return "$element$ asc";
|
||||||
}
|
}
|
||||||
else if ( "desc".equals( orderByFragment ) ) {
|
else if ( orderByFragment.equalsIgnoreCase( "desc" ) ) {
|
||||||
|
// This indicates:
|
||||||
|
// `@OrderBy("desc")`
|
||||||
|
//
|
||||||
|
// JPA says this should indicate a descending natural ordering of the elements - id for
|
||||||
|
// entity associations or the value(s) for "element collections"
|
||||||
return "$element$ desc";
|
return "$element$ desc";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return orderByFragment;
|
return orderByFragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1208,7 +1212,7 @@ public abstract class CollectionBinder {
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void bindManyToManySecondPass(
|
private void bindManyToManySecondPass(
|
||||||
Collection collValue,
|
Collection collValue,
|
||||||
Map persistentClasses,
|
Map persistentClasses,
|
||||||
Ejb3JoinColumn[] joinColumns,
|
Ejb3JoinColumn[] joinColumns,
|
||||||
|
@ -1276,14 +1280,12 @@ public abstract class CollectionBinder {
|
||||||
boolean mappedBy = !BinderHelper.isEmptyAnnotationValue( joinColumns[0].getMappedBy() );
|
boolean mappedBy = !BinderHelper.isEmptyAnnotationValue( joinColumns[0].getMappedBy() );
|
||||||
if ( mappedBy ) {
|
if ( mappedBy ) {
|
||||||
if ( !isCollectionOfEntities ) {
|
if ( !isCollectionOfEntities ) {
|
||||||
StringBuilder error = new StringBuilder( 80 )
|
throw new AnnotationException(
|
||||||
.append(
|
"Collection of elements must not have mappedBy or association reference an unmapped entity: " +
|
||||||
"Collection of elements must not have mappedBy or association reference an unmapped entity: "
|
collValue.getOwnerEntityName() +
|
||||||
)
|
"." +
|
||||||
.append( collValue.getOwnerEntityName() )
|
joinColumns[0].getPropertyName()
|
||||||
.append( "." )
|
);
|
||||||
.append( joinColumns[0].getPropertyName() );
|
|
||||||
throw new AnnotationException( error.toString() );
|
|
||||||
}
|
}
|
||||||
Property otherSideProperty;
|
Property otherSideProperty;
|
||||||
try {
|
try {
|
||||||
|
@ -1425,7 +1427,7 @@ public abstract class CollectionBinder {
|
||||||
XClass elementClass;
|
XClass elementClass;
|
||||||
AnnotatedClassType classType;
|
AnnotatedClassType classType;
|
||||||
|
|
||||||
CollectionPropertyHolder holder = null;
|
CollectionPropertyHolder holder;
|
||||||
if ( BinderHelper.PRIMITIVE_NAMES.contains( collType.getName() ) ) {
|
if ( BinderHelper.PRIMITIVE_NAMES.contains( collType.getName() ) ) {
|
||||||
classType = AnnotatedClassType.NONE;
|
classType = AnnotatedClassType.NONE;
|
||||||
elementClass = null;
|
elementClass = null;
|
||||||
|
@ -1522,7 +1524,6 @@ public abstract class CollectionBinder {
|
||||||
collValue.setElement( component );
|
collValue.setElement( component );
|
||||||
|
|
||||||
if ( StringHelper.isNotEmpty( hqlOrderBy ) ) {
|
if ( StringHelper.isNotEmpty( hqlOrderBy ) ) {
|
||||||
String path = collValue.getOwnerEntityName() + "." + joinColumns[0].getPropertyName();
|
|
||||||
String orderBy = adjustUserSuppliedValueCollectionOrderingFragment( hqlOrderBy );
|
String orderBy = adjustUserSuppliedValueCollectionOrderingFragment( hqlOrderBy );
|
||||||
if ( orderBy != null ) {
|
if ( orderBy != null ) {
|
||||||
collValue.setOrderBy( orderBy );
|
collValue.setOrderBy( orderBy );
|
||||||
|
@ -1544,7 +1545,7 @@ public abstract class CollectionBinder {
|
||||||
column.setLength( Ejb3Column.DEFAULT_COLUMN_LENGTH );
|
column.setLength( Ejb3Column.DEFAULT_COLUMN_LENGTH );
|
||||||
column.setLogicalColumnName( Collection.DEFAULT_ELEMENT_COLUMN_NAME );
|
column.setLogicalColumnName( Collection.DEFAULT_ELEMENT_COLUMN_NAME );
|
||||||
//TODO create an EMPTY_JOINS collection
|
//TODO create an EMPTY_JOINS collection
|
||||||
column.setJoins( new HashMap<String, Join>() );
|
column.setJoins( new HashMap<>() );
|
||||||
column.setBuildingContext( buildingContext );
|
column.setBuildingContext( buildingContext );
|
||||||
column.bind();
|
column.bind();
|
||||||
elementColumns[0] = column;
|
elementColumns[0] = column;
|
||||||
|
|
|
@ -551,6 +551,7 @@ public abstract class AbstractCollectionPersister
|
||||||
|
|
||||||
hasOrder = collectionBinding.getOrderBy() != null;
|
hasOrder = collectionBinding.getOrderBy() != null;
|
||||||
if ( hasOrder ) {
|
if ( hasOrder ) {
|
||||||
|
LOG.debugf( "Translating order-by fragment [%s] for collection role : %s", collectionBinding.getOrderBy(), getRole() );
|
||||||
orderByTranslation = Template.translateOrderBy(
|
orderByTranslation = Template.translateOrderBy(
|
||||||
collectionBinding.getOrderBy(),
|
collectionBinding.getOrderBy(),
|
||||||
new ColumnMapperImpl(),
|
new ColumnMapperImpl(),
|
||||||
|
@ -577,6 +578,7 @@ public abstract class AbstractCollectionPersister
|
||||||
|
|
||||||
hasManyToManyOrder = collectionBinding.getManyToManyOrdering() != null;
|
hasManyToManyOrder = collectionBinding.getManyToManyOrdering() != null;
|
||||||
if ( hasManyToManyOrder ) {
|
if ( hasManyToManyOrder ) {
|
||||||
|
LOG.debugf( "Translating many-to-many order-by fragment [%s] for collection role : %s", collectionBinding.getOrderBy(), getRole() );
|
||||||
manyToManyOrderByTranslation = Template.translateOrderBy(
|
manyToManyOrderByTranslation = Template.translateOrderBy(
|
||||||
collectionBinding.getManyToManyOrdering(),
|
collectionBinding.getManyToManyOrdering(),
|
||||||
new ColumnMapperImpl(),
|
new ColumnMapperImpl(),
|
||||||
|
|
|
@ -36,6 +36,8 @@ public class OrderByFragmentTranslator {
|
||||||
* @return The translation.
|
* @return The translation.
|
||||||
*/
|
*/
|
||||||
public static OrderByTranslation translate(TranslationContext context, String fragment) {
|
public static OrderByTranslation translate(TranslationContext context, String fragment) {
|
||||||
|
LOG.tracef( "Beginning parsing of order-by fragment : " + 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
|
// Perform the parsing (and some analysis/resolution). Another important aspect is the collection
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.test.jpa.compliance;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.ElementCollection;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.OrderBy;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
import org.hibernate.boot.MetadataSources;
|
||||||
|
import org.hibernate.cfg.annotations.CollectionBinder;
|
||||||
|
import org.hibernate.dialect.Dialect;
|
||||||
|
import org.hibernate.dialect.function.SQLFunctionRegistry;
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.sql.ordering.antlr.ColumnMapper;
|
||||||
|
import org.hibernate.sql.ordering.antlr.ColumnReference;
|
||||||
|
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.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.hamcrest.CoreMatchers;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public class OrderByAnnotationTests extends BaseNonConfigCoreFunctionalTestCase {
|
||||||
|
private static final String ELEMENT_TOKEN = "$element$";
|
||||||
|
private static final String TABLE_ALIAS = "a";
|
||||||
|
private static final String COLUMN_NAME = "name";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOrderByEmpty() {
|
||||||
|
assertThat( translate( "" ), CoreMatchers.is( TABLE_ALIAS + '.' + COLUMN_NAME + " asc" ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOrderByJustDesc() {
|
||||||
|
assertThat( translate( "desc" ), CoreMatchers.is( TABLE_ALIAS + '.' + COLUMN_NAME + " desc" ) );
|
||||||
|
|
||||||
|
assertThat( translate( "DESC"), CoreMatchers.is( TABLE_ALIAS + '.' + COLUMN_NAME + " desc" ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOrderByJustAsc() {
|
||||||
|
assertThat( translate( "asc"), CoreMatchers.is( TABLE_ALIAS + '.' + COLUMN_NAME + " asc" ) );
|
||||||
|
|
||||||
|
assertThat( translate( "ASC"), CoreMatchers.is( TABLE_ALIAS + '.' + COLUMN_NAME + " asc" ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private String translate(String fragment) {
|
||||||
|
fragment = CollectionBinder.adjustUserSuppliedValueCollectionOrderingFragment( fragment );
|
||||||
|
|
||||||
|
final TranslationContext translationContext = translationContext();
|
||||||
|
final OrderByTranslation translation = OrderByFragmentTranslator.translate( translationContext, fragment );
|
||||||
|
|
||||||
|
return translation.injectAliases( columnReference -> TABLE_ALIAS );
|
||||||
|
}
|
||||||
|
|
||||||
|
private TranslationContext translationContext() {
|
||||||
|
final ColumnMapper columnMapper = reference -> {
|
||||||
|
assert ELEMENT_TOKEN.equals( reference );
|
||||||
|
return new SqlValueReference[] {
|
||||||
|
(ColumnReference) () -> COLUMN_NAME
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return new TranslationContext() {
|
||||||
|
@Override
|
||||||
|
public SessionFactoryImplementor getSessionFactory() {
|
||||||
|
return sessionFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dialect getDialect() {
|
||||||
|
return getSessionFactory().getJdbcServices().getJdbcEnvironment().getDialect();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SQLFunctionRegistry getSqlFunctionRegistry() {
|
||||||
|
return getSessionFactory().getSqlFunctionRegistry();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ColumnMapper getColumnMapper() {
|
||||||
|
return columnMapper;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
// protected void applyMetadataSources(MetadataSources metadataSources) {
|
||||||
|
// super.applyMetadataSources( metadataSources );
|
||||||
|
// metadataSources.addAnnotatedClass( A.class );
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Entity( name = "A" )
|
||||||
|
// @Table( name = "T_A" )
|
||||||
|
// public static class A {
|
||||||
|
// @Id
|
||||||
|
// public Integer id;
|
||||||
|
// @ElementCollection
|
||||||
|
// @Column( name = "name" )
|
||||||
|
// @OrderBy
|
||||||
|
// public List<String> names;
|
||||||
|
// }
|
||||||
|
}
|
Loading…
Reference in New Issue