From 6f37a2ee6b694dac2ebffaff208b80962a420745 Mon Sep 17 00:00:00 2001 From: amaeda Date: Sat, 4 Apr 2015 13:36:04 -0300 Subject: [PATCH] HHH-9707 - Nulls first behavior on SQLServer database --- .../dialect/SQLServer2012Dialect.java | 7 +++++ .../hibernate/dialect/SQLServerDialect.java | 26 ++++++++++++++++++ .../annotations/onetomany/OrderByTest.java | 27 ++++++++++--------- 3 files changed, 48 insertions(+), 12 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServer2012Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServer2012Dialect.java index 79467066f1..9ede59ead1 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServer2012Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServer2012Dialect.java @@ -23,6 +23,8 @@ */ package org.hibernate.dialect; +import org.hibernate.NullPrecedence; + /** * Microsoft SQL Server 2012 Dialect @@ -65,4 +67,9 @@ public class SQLServer2012Dialect extends SQLServer2008Dialect { public String getQuerySequencesString() { return "select name from sys.sequences"; } + + @Override + public String renderOrderByElement(String expression, String collation, String order, NullPrecedence nulls) { + return renderOrderByElementDefaultBehavior( expression, collation, order, nulls ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java index c8dba6137c..4f98e73d29 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java @@ -27,6 +27,7 @@ import java.sql.Types; import org.hibernate.LockMode; import org.hibernate.LockOptions; +import org.hibernate.NullPrecedence; import org.hibernate.dialect.function.AnsiTrimEmulationFunction; import org.hibernate.dialect.function.SQLFunctionTemplate; import org.hibernate.dialect.function.StandardSQLFunction; @@ -208,5 +209,30 @@ public class SQLServerDialect extends AbstractTransactSQLDialect { public int getInExpressionCountLimit() { return PARAM_LIST_SIZE_LIMIT; } + @Override + public String renderOrderByElement(String expression, String collation, String order, NullPrecedence nulls) { + final StringBuilder orderByElement = new StringBuilder(); + + if ( nulls != null && !NullPrecedence.NONE.equals( nulls ) ) { + // Workaround for NULLS FIRST / LAST support. + orderByElement.append( "case when " ).append( expression ).append( " is null then " ); + if ( NullPrecedence.FIRST.equals( nulls ) ) { + orderByElement.append( "0 else 1" ); + } + else { + orderByElement.append( "1 else 0" ); + } + orderByElement.append( " end, " ); + } + + // Nulls precedence has already been handled so passing NONE value. + orderByElement.append( super.renderOrderByElement( expression, collation, order, NullPrecedence.NONE ) ); + + return orderByElement.toString(); + } + + String renderOrderByElementDefaultBehavior(String expression, String collation, String order, NullPrecedence nulls) { + return super.renderOrderByElement( expression, collation, order, nulls ); + } } 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 c30fef4081..f0ad03e497 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 @@ -43,6 +43,9 @@ import org.hibernate.Session; 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.persister.collection.CollectionPersister; import org.hibernate.persister.collection.QueryableCollection; @@ -97,10 +100,10 @@ public class OrderByTest extends BaseCoreFunctionalTestCase { @Test @TestForIssue(jiraKey = "HHH-465") - @RequiresDialect(value = { H2Dialect.class, MySQLDialect.class }, + @RequiresDialect(value = { H2Dialect.class, MySQLDialect.class, SQLServer2008Dialect.class }, comment = "By default H2 places NULL values first, so testing 'NULLS LAST' expression. " + - "For MySQL testing overridden Dialect#renderOrderByElement(String, String, String, NullPrecedence) method. " + - "MySQL does not support NULLS FIRST / LAST syntax at the moment, so transforming the expression to 'CASE WHEN ...'.") + "For MySQL and SQL Server 2000/2005/2008 testing overridden Dialect#renderOrderByElement(String, String, String, NullPrecedence) method. " + + "MySQL and SQLServer 2000/2005/2008 does not support NULLS FIRST / LAST syntax at the moment, so transforming the expression to 'CASE WHEN ...'.") public void testAnnotationNullsFirstLast() { Session session = openSession(); @@ -156,10 +159,10 @@ public class OrderByTest extends BaseCoreFunctionalTestCase { @Test @TestForIssue(jiraKey = "HHH-465") - @RequiresDialect(value = { H2Dialect.class, MySQLDialect.class }, + @RequiresDialect(value = { H2Dialect.class, MySQLDialect.class, SQLServer2008Dialect.class }, comment = "By default H2 places NULL values first, so testing 'NULLS LAST' expression. " + - "For MySQL testing overridden Dialect#renderOrderByElement(String, String, String, NullPrecedence) method. " + - "MySQL does not support NULLS FIRST / LAST syntax at the moment, so transforming the expression to 'CASE WHEN ...'.") + "For MySQL and SQL Server 2000/2005/2008 testing overridden Dialect#renderOrderByElement(String, String, String, NullPrecedence) method. " + + "MySQL and SQL Server 2000/2005/2008 does not support NULLS FIRST / LAST syntax at the moment, so transforming the expression to 'CASE WHEN ...'.") public void testCriteriaNullsFirstLast() { Session session = openSession(); @@ -194,10 +197,10 @@ public class OrderByTest extends BaseCoreFunctionalTestCase { @Test @TestForIssue(jiraKey = "HHH-465") - @RequiresDialect(value = { H2Dialect.class, MySQLDialect.class }, + @RequiresDialect(value = { H2Dialect.class, MySQLDialect.class, SQLServer2008Dialect.class }, comment = "By default H2 places NULL values first, so testing 'NULLS LAST' expression. " + - "For MySQL testing overridden Dialect#renderOrderByElement(String, String, String, NullPrecedence) method. " + - "MySQL does not support NULLS FIRST / LAST syntax at the moment, so transforming the expression to 'CASE WHEN ...'.") + "For MySQL and SQL Server 2000/2005/2008 testing overridden Dialect#renderOrderByElement(String, String, String, NullPrecedence) method. " + + "MySQL and SQL Server 2000/2005/2008 does not support NULLS FIRST / LAST syntax at the moment, so transforming the expression to 'CASE WHEN ...'.") public void testNullsFirstLastSpawnMultipleColumns() { Session session = openSession(); @@ -243,10 +246,10 @@ public class OrderByTest extends BaseCoreFunctionalTestCase { @Test @TestForIssue(jiraKey = "HHH-465") - @RequiresDialect(value = { H2Dialect.class, MySQLDialect.class }, + @RequiresDialect(value = { H2Dialect.class, MySQLDialect.class, SQLServer2008Dialect.class }, comment = "By default H2 places NULL values first, so testing 'NULLS LAST' expression. " + - "For MySQL testing overridden Dialect#renderOrderByElement(String, String, String, NullPrecedence) method. " + - "MySQL does not support NULLS FIRST / LAST syntax at the moment, so transforming the expression to 'CASE WHEN ...'.") + "For MySQL and SQL Server 2000/2005/2008 testing overridden Dialect#renderOrderByElement(String, String, String, NullPrecedence) method. " + + "MySQL and SQL Server 2000/2005/2008 does not support NULLS FIRST / LAST syntax at the moment, so transforming the expression to 'CASE WHEN ...'.") public void testHqlNullsFirstLast() { Session session = openSession();