diff --git a/hibernate-core/src/main/antlr/sql-gen.g b/hibernate-core/src/main/antlr/sql-gen.g index c8eeda17f6..e2659df245 100644 --- a/hibernate-core/src/main/antlr/sql-gen.g +++ b/hibernate-core/src/main/antlr/sql-gen.g @@ -146,7 +146,7 @@ selectStatement from ( #(WHERE { out(" where "); } whereExpr ) )? ( #(GROUP { out(" group by "); } groupExprs ( #(HAVING { out(" having "); } booleanExpr[false]) )? ) )? - ( #(ORDER { out(" order by "); } orderExprs ) )? + ( #(ORDER { out(" order by "); } orderBySqlFragmentOrExprs ) )? ) ; @@ -191,6 +191,11 @@ whereClauseExpr | booleanExpr[ false ] ; +orderBySqlFragmentOrExprs + : sqlToken // for the purpose of mapping-defined orderBy SQL fragment + | orderExprs + ; + orderExprs { String ordExp = null; String ordDir = null; String ordNul = null; } // TODO: remove goofy space before the comma when we don't have to regression test anymore. // Dialect is provided a hook to render each ORDER BY element, so the expression is being captured instead of @@ -277,7 +282,7 @@ distinctOrAll ; countExpr - // Syntacitic predicate resolves star all by itself, avoiding a conflict with STAR in expr. + // Syntactic predicate resolves star all by itself, avoiding a conflict with STAR in expr. : ROW_STAR { out("*"); } | simpleExpr ; diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/onetomany/OrderByTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/onetomany/OrderByTest.java index 8b22d32c70..52870917a8 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/onetomany/OrderByTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/onetomany/OrderByTest.java @@ -24,15 +24,17 @@ import org.hibernate.Criteria; import org.hibernate.Hibernate; import org.hibernate.NullPrecedence; import org.hibernate.Session; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.Configuration; import org.hibernate.dialect.H2Dialect; import org.hibernate.dialect.MySQLDialect; import org.hibernate.dialect.Oracle8iDialect; import org.hibernate.dialect.SQLServer2008Dialect; -import org.hibernate.dialect.SQLServer2012Dialect; -import org.hibernate.dialect.SQLServerDialect; import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.graph.RootGraph; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.QueryableCollection; +import org.hibernate.query.Query; import org.hibernate.sql.SimpleSelect; import org.hibernate.testing.RequiresDialect; import org.hibernate.testing.TestForIssue; @@ -515,6 +517,28 @@ public class OrderByTest extends BaseCoreFunctionalTestCase { s.close(); } + @Test + @TestForIssue( jiraKey = "HHH-14148" ) + @RequiresDialect(value = { H2Dialect.class, MySQLDialect.class, SQLServer2008Dialect.class }, + comment = "By default H2 places NULL values first, so testing 'NULLS LAST' expression. " + + "For MySQL and SQL Server 2008 testing overridden Dialect#renderOrderByElement(String, String, String, NullPrecedence) method. " + + "MySQL and SQL Server 2008 does not support NULLS FIRST / LAST syntax at the moment, so transforming the expression to 'CASE WHEN ...'.") + public void testNullPrecedenceWithOrderBySqlFragment() { + inTransaction( session -> { + final RootGraph graph = session.createEntityGraph( Order.class ); + graph.addAttributeNodes( "itemList" ); + + Query query = session.createQuery( "from Order", Order.class ); + query.applyFetchGraph( graph ); + query.getResultList(); // before HHH-14148 is fixed, incorrect SQL would be generated ending with " nulls last nulls last" + } ); + } + + @Override + protected void configure(Configuration configuration) { + configuration.setProperty( AvailableSettings.DEFAULT_NULL_ORDERING, "last" ); + } + @Override protected Class[] getAnnotatedClasses() { return new Class[] {