Skip to content

Support in-memory sorting for DataGrid (3389)#5141

Open
Flaurite wants to merge 8 commits intomasterfrom
feature/2093-support-for-sorting-delegate-in-dataGrid
Open

Support in-memory sorting for DataGrid (3389)#5141
Flaurite wants to merge 8 commits intomasterfrom
feature/2093-support-for-sorting-delegate-in-dataGrid

Conversation

@Flaurite
Copy link
Copy Markdown
Contributor

@Flaurite Flaurite commented Mar 13, 2026

Fixes for: #3389

Changes

Sort.ExpressionOrder

Added new type of sort order- ExpressionOrder.

It is supported by SortJpqlGenerator. The generator checks ExpressionOrder and place it as is to sortExpressions. It means that ExpressionOrder skips checks for MetaPropertyPath and does not pass to JpqlSortExpressionProvider.

ExpressionOrder can be used when it is need to sort by specific expression. For instance, for JPQL, the expressions can be:

  • "function('calc_total_sum', {E}.id)";
  • "length({E}.firstName)";
  • "{E}.firstName".

ExpressionOrder and KeyValueEntity query

When ExpressionOrder is used in query which result is KeyValueEntity, the ExpressionOrder requires to use the same entity alias as in a query.

For instance:
Query: select e.name, e.startTime, e.endTime from CarService e
The expression: length(e.name)


Comparator in DataGridColumn

Now, when DataGridColumn has custom comparator, it will be used when sorting is in-memory.
Note that in-memory sorting occurs:

  • when loader is not set to CollecitionContainer
  • when loaded first page (e.g. due to SimplePagination) and number of loaded items is less than page size.

Example of adding comparator:

DataGridColumn<Customer> fullNameColumn = dataGrid.getColumnByKey("fullName");
if (fullNameColumn != null) {
    fullNameColumn.setComparator(
            Comparator.comparing(Customer::getFirstName)
                    .thenComparing(Customer::getLastName));
}

Sort builder delegate in DataGrid

Added the extension point to customize sort in DataGrid. For instance:

dataGrid.setSortBuilderDelegate(context ->
        DataGridSortBuilder.create(context)
                .replaceSort("firstName", "length({E}.firstName)")
                .build());

When this feature may be used:

  1. You need to customize persistent sorting or in-memory sorting when the user sorts by a column bound with entity property;
  2. You have a column that is not bound with entity property, but there is a way to sort it in-memory and in persistent sorting.
  3. You need to sort transient entity property in persistent sorting.

Sample 1. Override sorting for JPA entity property

Let's imagine that some entity property with String type stores integer values.

By default, this property is sorted as String values:

  • Original order: "5", "56", "1", "12", "43"
  • Sort asc: "1", "12", "43", "5", "56"
  • Sort: desc: "56", "5", "43", "12", "1"

Let's make sorting to sort by number values as follows:

  • Original order: "5", "56", "1", "12", "43"
  • Sort asc: "1", "5", "12", "43", "56"
  • Sort: desc: "56", "43", "12", "5", "1"
dataGrid.setSortBuilderDelegate(context ->
        DataGridSortBuilder.create(context)
                .replaceSort("loyaltyPointsStr", "cast({E}.loyaltyPointsStr integer)",
                        (o1, o2) -> {
                            int calc1 = Integer.parseInt(o1.getLoyaltyPointsStr());
                            int calc2 = Integer.parseInt(o2.getLoyaltyPointsStr());
                            return Integer.compare(calc1, calc2);
                        })
                .build());

We have provided sorting for in-memory and for persistent (JPQL).

Sample 2. Add sorting for column without property.

Let's say we have a column "Full name". It renders concatenated firstName and lastName properties.

DataGridColumn<Customer> fullNameColumn = dataGrid.getColumnByKey("fullName");
if (fullNameColumn != null) {
    fullNameColumn.setRenderer(new TextRenderer<>(item -> item.getFirstName() + " " + item.getLastName()));
}

In the sort builder delegate we can do the following:

dataGrid.setSortBuilderDelegate(context ->
        DataGridSortBuilder.create(context)
                .replaceSort("firstName", List.of("{E}.firstName", "{E}.lastName"),
                        Comparator.comparing(Customer::getFirstName).thenComparing(Customer::getLastName))
                .build());

Sort Builder Delegate vs CollectionContainerSorter and JpqlSortExpressionProvider

Use Sort Builder Delegate when sorting must be customized for a specific DataGrid:

  • one place to define both persistent sort (JPQL expressions) and in-memory sort (Comparator);
  • convenient for columns without bound entity property, transient/computed fields, or per-screen rules;
  • best choice for local UI behavior without global side effects.

Use CollectionContainerSorter when you need to customize container-level in-memory sorting mechanics:

  • affects sorting of the whole CollectionContainer (not only one grid column mapping);
  • useful for custom comparator creation logic in sorter internals;
  • if this behavior should be global/system-wide, override SorterFactory (@Primary custom factory).

Use custom JpqlSortExpressionProvider when you need global customization of DB-level ORDER BY expression generation for entity properties:

  • applies to persistent sorting for entity property-based orders across loaders/views;
  • good for DB-specific functions/casts/null handling as a default policy;
  • does not handle in-memory sorting and is bypassed when using Sort.ExpressionOrder (expression is inserted as-is).

In short:

  • view/column-specific + both persistent/in-memory -> Sort Builder Delegate
  • global/container in-memory sorting strategy -> CollectionContainerSorter (+ SorterFactory for global rollout)
  • global JPQL expression policy for property sorting -> JpqlSortExpressionProvider.

@Flaurite Flaurite requested a review from glebfox March 13, 2026 12:14
@Flaurite Flaurite self-assigned this Mar 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant