3.5.3.4.5. 自定义排序
UI table 中按照实体属性排序的功能是通过 CollectionContainerSorter
实现的,需要为 CollectionContainer 设置该排序器。标准的实现是,如果数据在一页以内能显示,则在内存做数据排序,否则会使用合适的 "order by" 语句发送数据库的请求。"order by" 语句是中间层的 JpqlSortExpressionProvider
bean创建的。
有些实体属性需要一个特殊的排序实现。下面我们用个例子解释一下如何自定义排序:假设有 Foo
实体带有 String
类型的 number
属性,但是我们知道该属性其实是只保存数字。所以我们希望排序的顺序是 1, 2, 3, 10, 11
。但是,默认的排序行为会产生这样的结果:1, 10, 11, 2, 3
。
首先,在 web
模块创建一个 CollectionContainerSorter
类的子类,在内存进行排序:
package com.company.demo.web;
import com.company.demo.entity.Foo;
import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.chile.core.model.MetaPropertyPath;
import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.core.global.Sort;
import com.haulmont.cuba.gui.model.BaseCollectionLoader;
import com.haulmont.cuba.gui.model.CollectionContainer;
import com.haulmont.cuba.gui.model.impl.CollectionContainerSorter;
import com.haulmont.cuba.gui.model.impl.EntityValuesComparator;
import javax.annotation.Nullable;
import java.util.Comparator;
import java.util.Objects;
public class CustomCollectionContainerSorter extends CollectionContainerSorter {
public CustomCollectionContainerSorter(CollectionContainer container,
@Nullable BaseCollectionLoader loader) {
super(container, loader);
}
@Override
protected Comparator<? extends Entity> createComparator(Sort sort, MetaClass metaClass) {
MetaPropertyPath metaPropertyPath = Objects.requireNonNull(
metaClass.getPropertyPath(sort.getOrders().get(0).getProperty()));
if (metaPropertyPath.getMetaClass().getJavaClass().equals(Foo.class)
&& "number".equals(metaPropertyPath.toPathString())) {
boolean isAsc = sort.getOrders().get(0).getDirection() == Sort.Direction.ASC;
return Comparator.comparing(
(Foo e) -> e.getNumber() == null ? null : Integer.valueOf(e.getNumber()),
EntityValuesComparator.asc(isAsc));
}
return super.createComparator(sort, metaClass);
}
}
如果有几个界面需要这个自定义的排序,可以在界面中实例化 CustomCollectionContainerSorter
:
public class FooBrowse extends StandardLookup<Foo> {
@Inject
private CollectionContainer<Foo> fooDc;
@Inject
private CollectionLoader<Foo> fooDl;
@Subscribe
private void onInit(InitEvent event) {
CustomCollectionContainerSorter sorter = new CustomCollectionContainerSorter(fooDc, fooDl);
fooDc.setSorter(sorter);
}
}
如果排序器定义了一些全局的行为,则可以创建自定义的工厂在系统级别实例化该排序器:
package com.company.demo.web;
import com.haulmont.cuba.gui.model.*;
import javax.annotation.Nullable;
public class CustomSorterFactory extends SorterFactory {
@Override
public Sorter createCollectionContainerSorter(CollectionContainer container,
@Nullable BaseCollectionLoader loader) {
return new CustomCollectionContainerSorter(container, loader);
}
}
然后在 web-spring.xml
注册该工厂以替换默认工厂:
<bean id="cuba_SorterFactory" class="com.company.demo.web.CustomSorterFactory"/>
现在我们在 core
模块为数据库级别的排序创建我们自己定义的 JpqlSortExpressionProvider
实现:
package com.company.demo.core;
import com.company.demo.entity.Foo;
import com.haulmont.chile.core.model.MetaPropertyPath;
import com.haulmont.cuba.core.app.DefaultJpqlSortExpressionProvider;
public class CustomSortExpressionProvider extends DefaultJpqlSortExpressionProvider {
@Override
public String getDatatypeSortExpression(MetaPropertyPath metaPropertyPath, boolean sortDirectionAsc) {
if (metaPropertyPath.getMetaClass().getJavaClass().equals(Foo.class)
&& "number".equals(metaPropertyPath.toPathString())) {
return String.format("CAST({E}.%s BIGINT)", metaPropertyPath.toString());
}
return String.format("{E}.%s", metaPropertyPath.toString());
}
}
在 spring.xml
注册此 expression provider,覆盖默认:
<bean id="cuba_JpqlSortExpressionProvider" class="com.company.demo.core.CustomSortExpressionProvider"/>