Skip to content

GH-821 Add support for specifying ORDER BY null handling for supported dialects #1156

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 9 commits into from
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-relational-parent</artifactId>
<version>2.4.0-SNAPSHOT</version>
<version>2.4.0-821-support-sort-null-handling-SNAPSHOT</version>
<packaging>pom</packaging>

<name>Spring Data Relational Parent</name>
Expand Down
2 changes: 1 addition & 1 deletion spring-data-jdbc-distribution/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-relational-parent</artifactId>
<version>2.4.0-SNAPSHOT</version>
<version>2.4.0-821-support-sort-null-handling-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
4 changes: 2 additions & 2 deletions spring-data-jdbc/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<modelVersion>4.0.0</modelVersion>

<artifactId>spring-data-jdbc</artifactId>
<version>2.4.0-SNAPSHOT</version>
<version>2.4.0-821-support-sort-null-handling-SNAPSHOT</version>

<name>Spring Data JDBC</name>
<description>Spring Data module for JDBC repositories.</description>
Expand All @@ -15,7 +15,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-relational-parent</artifactId>
<version>2.4.0-SNAPSHOT</version>
<version>2.4.0-821-support-sort-null-handling-SNAPSHOT</version>
</parent>

<properties>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2017-2021 the original author or authors.
* Copyright 2017-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -51,6 +51,7 @@
* @author Milan Milanov
* @author Myeonghyeon Lee
* @author Mikhail Polivakha
* @author Chirag Tailor
*/
class SqlGenerator {

Expand Down Expand Up @@ -714,7 +715,7 @@ private OrderByField orderToOrderByField(Sort.Order order) {

SqlIdentifier columnName = this.entity.getRequiredPersistentProperty(order.getProperty()).getColumnName();
Column column = Column.create(columnName, this.getTable());
return OrderByField.from(column, order.getDirection());
return OrderByField.from(column, order.getDirection()).withNullHandling(order.getNullHandling());
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,21 @@ void saveAndLoadManyEntitiesWithReferencedEntitySortedAndPaged() {
.containsExactly("Star");
}

@Test // GH-821
@EnabledOnFeature({SUPPORTS_QUOTED_IDS, SUPPORTS_NULL_HANDLING})
void saveAndLoadManyEntitiesWithReferencedEntitySortedWithNullHandling() {

template.save(createLegoSet(null));
template.save(createLegoSet("Star"));
template.save(createLegoSet("Frozen"));

Iterable<LegoSet> reloadedLegoSets = template.findAll(LegoSet.class, Sort.by(new Sort.Order(Sort.Direction.ASC, "name", Sort.NullHandling.NULLS_LAST)));

assertThat(reloadedLegoSets) //
.extracting("name") //
.containsExactly("Frozen", "Star", null);
}

@Test // DATAJDBC-112
@EnabledOnFeature(SUPPORTS_QUOTED_IDS)
void saveAndLoadManyEntitiesByIdWithReferencedEntity() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2017-2021 the original author or authors.
* Copyright 2017-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -25,7 +25,6 @@

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.ReadOnlyProperty;
import org.springframework.data.annotation.Version;
Expand Down Expand Up @@ -64,6 +63,7 @@
* @author Milan Milanov
* @author Myeonghyeon Lee
* @author Mikhail Polivakha
* @author Chirag Tailor
*/
class SqlGeneratorUnitTests {

Expand Down Expand Up @@ -245,6 +245,26 @@ void findAllSortedByMultipleFields() {
"x_other ASC");
}

@Test // GH-821
void findAllSortedWithNullHandling_resolvesNullHandlingWhenDialectSupportsIt() {

SqlGenerator sqlGenerator = createSqlGenerator(DummyEntity.class, PostgresDialect.INSTANCE);

String sql = sqlGenerator.getFindAll(Sort.by(new Sort.Order(Sort.Direction.ASC, "name", Sort.NullHandling.NULLS_LAST)));

assertThat(sql).contains("ORDER BY \"dummy_entity\".\"x_name\" ASC NULLS LAST");
}

@Test // GH-821
void findAllSortedWithNullHandling_ignoresNullHandlingWhenDialectDoesNotSupportIt() {

SqlGenerator sqlGenerator = createSqlGenerator(DummyEntity.class, SqlServerDialect.INSTANCE);

String sql = sqlGenerator.getFindAll(Sort.by(new Sort.Order(Sort.Direction.ASC, "name", Sort.NullHandling.NULLS_LAST)));

assertThat(sql).endsWith("ORDER BY dummy_entity.x_name ASC");
}

@Test // DATAJDBC-101
void findAllPagedByUnpaged() {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2020-2021 the original author or authors.
* Copyright 2020-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -29,6 +29,7 @@
* presence or absence of features in tests.
*
* @author Jens Schauder
* @author Chirag Tailor
*/
public class TestDatabaseFeatures {

Expand Down Expand Up @@ -83,6 +84,10 @@ private void supportsMultiDimensionalArrays() {
assumeThat(database).isNotIn(Database.H2, Database.Hsql);
}

private void supportsNullHandling() {
assumeThat(database).isNotIn(Database.MySql, Database.MariaDb, Database.SqlServer);
}

public void databaseIs(Database database) {
assumeThat(this.database).isEqualTo(database);
}
Expand Down Expand Up @@ -115,6 +120,7 @@ public enum Feature {
SUPPORTS_ARRAYS(TestDatabaseFeatures::supportsArrays), //
SUPPORTS_GENERATED_IDS_IN_REFERENCED_ENTITIES(TestDatabaseFeatures::supportsGeneratedIdsInReferencedEntities), //
SUPPORTS_NANOSECOND_PRECISION(TestDatabaseFeatures::supportsNanosecondPrecision), //
SUPPORTS_NULL_HANDLING(TestDatabaseFeatures::supportsNullHandling),
IS_POSTGRES(f -> f.databaseIs(Database.PostgreSql)), //
IS_HSQL(f -> f.databaseIs(Database.Hsql));

Expand Down
4 changes: 2 additions & 2 deletions spring-data-relational/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
<modelVersion>4.0.0</modelVersion>

<artifactId>spring-data-relational</artifactId>
<version>2.4.0-SNAPSHOT</version>
<version>2.4.0-821-support-sort-null-handling-SNAPSHOT</version>

<name>Spring Data Relational</name>
<description>Spring Data Relational support</description>

<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-relational-parent</artifactId>
<version>2.4.0-SNAPSHOT</version>
<version>2.4.0-821-support-sort-null-handling-SNAPSHOT</version>
</parent>

<properties>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 the original author or authors.
* Copyright 2019-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -18,6 +18,7 @@
import java.util.OptionalLong;
import java.util.function.Function;

import org.springframework.data.domain.Sort;
import org.springframework.data.relational.core.sql.LockMode;
import org.springframework.data.relational.core.sql.LockOptions;
import org.springframework.data.relational.core.sql.Select;
Expand All @@ -28,6 +29,7 @@
*
* @author Mark Paluch
* @author Myeonghyeon Lee
* @author Chirag Tailor
* @since 1.1
*/
public abstract class AbstractDialect implements Dialect {
Expand All @@ -42,7 +44,7 @@ public SelectRenderContext getSelectContext() {
Function<Select, ? extends CharSequence> afterFromTable = getAfterFromTable();
Function<Select, ? extends CharSequence> afterOrderBy = getAfterOrderBy();

return new DialectSelectRenderContext(afterFromTable, afterOrderBy);
return new DialectSelectRenderContext(afterFromTable, afterOrderBy, orderByNullHandling());
}

/**
Expand Down Expand Up @@ -105,12 +107,14 @@ static class DialectSelectRenderContext implements SelectRenderContext {

private final Function<Select, ? extends CharSequence> afterFromTable;
private final Function<Select, ? extends CharSequence> afterOrderBy;
private final OrderByNullHandling orderByNullHandling;

DialectSelectRenderContext(Function<Select, ? extends CharSequence> afterFromTable,
Function<Select, ? extends CharSequence> afterOrderBy) {
Function<Select, ? extends CharSequence> afterOrderBy, OrderByNullHandling orderByNullHandling) {

this.afterFromTable = afterFromTable;
this.afterOrderBy = afterOrderBy;
this.orderByNullHandling = orderByNullHandling;
}

/*
Expand All @@ -130,6 +134,11 @@ static class DialectSelectRenderContext implements SelectRenderContext {
public Function<Select, ? extends CharSequence> afterOrderBy(boolean hasOrderBy) {
return afterOrderBy;
}

@Override
public String evaluateOrderByNullHandling(Sort.NullHandling nullHandling) {
return orderByNullHandling.evaluate(nullHandling);
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2020-2021 the original author or authors.
* Copyright 2020-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -25,6 +25,7 @@
* An SQL dialect for DB2.
*
* @author Jens Schauder
* @author Chirag Tailor
* @since 2.0
*/
public class Db2Dialect extends AbstractDialect {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 the original author or authors.
* Copyright 2019-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -33,6 +33,7 @@
* @author Myeonghyeon Lee
* @author Christoph Strobl
* @author Mikhail Polivakha
* @author Chirag Tailor
* @since 1.1
*/
public interface Dialect {
Expand Down Expand Up @@ -120,4 +121,13 @@ default Set<Class<?>> simpleTypes() {
default InsertRenderContext getInsertRenderContext() {
return InsertRenderContexts.DEFAULT;
}

/**
* Return the {@link OrderByNullHandling} used by this dialect.
*
* @return the {@link OrderByNullHandling} used by this dialect.
*/
default OrderByNullHandling orderByNullHandling() {
return OrderByNullHandling.SQL_STANDARD;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 the original author or authors.
* Copyright 2019-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -30,6 +30,7 @@
* @author Mark Paluch
* @author Myeonghyeon Lee
* @author Christph Strobl
* @author Chirag Tailor
* @since 2.0
*/
public class H2Dialect extends AbstractDialect {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 the original author or authors.
* Copyright 2019-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -20,6 +20,7 @@
*
* @author Jens Schauder
* @author Myeonghyeon Lee
* @author Chirag Tailor
*/
public class HsqlDbDialect extends AbstractDialect {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,4 +169,9 @@ public IdentifierProcessing getIdentifierProcessing() {
public Collection<Object> getConverters() {
return Collections.singletonList(TimestampAtUtcToOffsetDateTimeConverter.INSTANCE);
}

@Override
public OrderByNullHandling orderByNullHandling() {
return OrderByNullHandling.NONE;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 the original author or authors.
* Copyright 2019-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -20,14 +20,14 @@
import org.springframework.data.convert.WritingConverter;

import java.util.Collection;
import java.util.Collections;

import static java.util.Arrays.*;

/**
* An SQL dialect for Oracle.
*
* @author Jens Schauder
* @author Chirag Tailor
* @since 2.1
*/
public class OracleDialect extends AnsiDialect {
Expand Down
Loading