Работа интерфейса Filter с сущностями DTO

Есть сущность DTO каторая хранится в виртуальном хранилище, на стандартном экране employee-browse.xml отоброжается список всех сущностей загруженных в виртуальное хранилище. Загрузчик данных и контейнер определены в экране, фильтр тоже настроен, какая может быть причина того что фильтр не работает?

111.txt (1021 Байт)

<data readOnly="true">
        <collection id="employeesDc"
                    class="com.company.application.model.Employee">
            <fetchPlan extends="_local">
                <property name="jobNumber"/>
            </fetchPlan>
            <loader id="employeesDl">
                <query>
                    <![CDATA[select e from Employee e]]>
                </query>
            </loader>
        </collection>
    </data>

 <filter id="filterWithJobNumber"
                dataLoader="employeesDl">
            <properties include=".*"/>
            <conditions>
                <propertyFilter property="jobNumber"
                                operation="EQUAL"
                                operationEditable="true"
                                caption="Номер сотрудника"/>
            </conditions>
        </filter>
  1. Почему сущность DTO если она хранится в хранилище?
  2. Фильтры не работают с DTO сущностями, потому что они генерят SQL запросы в базу данных. А DTO по определению нигде не хранятся.
  1. Справедливое замечание, сущность персистентная хранится в виртуальном хранилище и судя по документации DataManager работает с ней так же как с обычными JPA сущностями хронящимися в БД.

Но чего то не хватает, что-бы фильтр заработал

Можете приложить код сущности? И как именно фильтр не работает? Грузятся те-же сущности не учитывая фильтр? В экране вы не переопределяли loadDelegate? Вы используете Нестандартные хранилища? Если Нестандартное хранилище то добавлена ли там логика обработки Conditions?

Код сущности:

package com.company.application.model;

import io.jmix.core.entity.annotation.JmixId;
import io.jmix.core.metamodel.annotation.JmixEntity;
import io.jmix.core.metamodel.annotation.JmixProperty;
import io.jmix.core.metamodel.annotation.Store;


@Store(name = "employee")
@JmixEntity(name = "employee", annotatedPropertiesOnly = true)
public class Employee {

    @JmixId
    @JmixProperty(mandatory = true)
    private String jobNumber;

    @JmixProperty
    private String firstName;

    @JmixProperty(mandatory = true)
    private String name;

    @JmixProperty
    private Integer phoneNumber;

    @JmixProperty
    private String address;

    @JmixProperty
    private String lastName;

    @JmixProperty
    private String office;

    @JmixProperty
    private String position;

    @JmixProperty
    private String email;

    public Employee() {
    }

    public Employee(String jobNumber, String firstName, String name, String lastName, String office, String email) {
        this.jobNumber = jobNumber;
        this.firstName = firstName;
        this.name = name;
        this.lastName = lastName;
        this.office = office;
        this.email = email;
    }

    public String getJobNumber() {
        return jobNumber;
    }

    public void setJobNumber(String jobNumber) {
        this.jobNumber = jobNumber;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getPhoneNumber() {
        return phoneNumber;
    }

    public void setPhoneNumber(Integer phoneNumber) {
        this.phoneNumber = phoneNumber;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getOffice() {
        return office;
    }

    public void setOffice(String office) {
        this.office = office;
    }

    public String getPosition() {
        return position;
    }

    public void setPosition(String position) {
        this.position = position;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Override
    public String toString() {
        return " " + jobNumber + ", " +
                " " + firstName + " " +
                " " + name + " " +
                " " + lastName;

    }
}

При нажатии на кнопку Refrech ничего не происходит, все те же сущности остаются на экране.


@UiController("employee.browse")
@UiDescriptor("employee-browse.xml")
@LookupComponent("employeesTable")
public class EmployeeBrowse extends StandardLookup<Employee> {

    @Autowired
    private EmployeeService employeeService;

    @Install(to = "employeesDl", target = Target.DATA_LOADER)
    private List<Employee> employeesDlLoadDelegate(LoadContext<Employee> loadContext) {
        LoadContext.Query query = loadContext.getQuery();
        return employeeService.employeeList;
    }
}

где переменная query - не используется.
Да, использую нестандартное хранилище, логика по обработке Conditions в самом хранилище не добавлена

В DataStore (у всех хранилищ) в функции загрузок передается loadContext. Там в query лежит condition, там лежат все FilterConditon + если вы в загрузчик добавили condition то и они там. Этот condition нужно обработать чтоб загружались нужные сущности.

В самом экране вы еще переопределили LoadDelegate так чтоб он при либо запросе возвращал одни и те-же сущности. (туда тоже передается loadContext с query)

Если вы используете нестандартное хранилище то зачем переопределять LoadDelegate в экране?

переопределять LoadDelegate в классе контроллере экрана необходимо, иначе СollectionsContainer будет пуст и приложение упадёт с IllegalArgumentException: Loader ‘employeesDl’ not found, или IllegalStateException: both query and delegate are null

IllegalArgumentException: Loader ‘employeesDl’ not found
Возможно у вас в экране что-то не так?

Вообще loader есть и у filter dataLoader он же стоит. Студия сама ни на что не ругается?

Если не сможете разобраться что не так. (Хотя если на @Install(to = "employeesDl" никакой ругани нет, то я не понимаю что не так). То мб в экране вызывать dataManager.load(loadContext) и всю обработку делать в хранилище?

IllegalArgumentException: Loader ‘employeesDl’ not found да, эта ошибка возникала из-за ошибки в экране. НО если убрать из контроллера employeesDlLoadDelegate то при вызове экрана получим Caused by: java.lang.reflect.InvocationTargetException: null

Вы имеете ввиду делать фильтрацию в хранилище и в контроллере обращаться к методу dataManager.load(loadContext)?

Да.
Caused by: java.lang.reflect.InvocationTargetException: null а полный стак трейс можете скинуть?

Могу, но эта ошибка возникнет только если уберу employeesDlLoadDelegate из контроллера, а так всё работает, без фильтра конечно же

Без стак трейса просто сложно сказать что не так. Желательно канечно loadDelegate не использовать если у вас вся обработка будет в Нестандартом хранилище. Да и хочется разобраться почему так))

Возможно вы в нестандартном хранилище возвращается null и на это ошибка у него? В общем. Смотрите по стак трейсу что не так)

А на главный вопрос я уже вроде как дал ответ - Нужно обработать Condition и вернуть нужные сущности.

IllegalStateException: both query and delegate are null
java.lang.IllegalStateException: both query and delegate are null
at io.jmix.ui.model.impl.CollectionLoaderImpl._load(CollectionLoaderImpl.java:82)
at io.jmix.ui.model.impl.CollectionLoaderImpl.load(CollectionLoaderImpl.java:75)
at io.jmix.ui.component.dataloadcoordinator.OnFrameOwnerEventLoadTrigger.load(OnFrameOwnerEventLoadTrigger.java:48)
at io.jmix.ui.component.dataloadcoordinator.OnFrameOwnerEventLoadTrigger.lambda$new$0(OnFrameOwnerEventLoadTrigger.java:39)
at io.jmix.core.common.event.EventHub.publish(EventHub.java:170)
at io.jmix.ui.screen.Screen.fireEvent(Screen.java:124)
at io.jmix.ui.screen.UiControllerUtils.fireEvent(UiControllerUtils.java:58)
at io.jmix.ui.sys.ScreensImpl.fireScreenBeforeShowEvent(ScreensImpl.java:1364)
at io.jmix.ui.sys.ScreensImpl.show(ScreensImpl.java:357)
at io.jmix.ui.screen.Screen.show(Screen.java:306)
at io.jmix.ui.action.entitypicker.EntityLookupAction.execute(EntityLookupAction.java:155)
at io.jmix.ui.action.entitypicker.EntityLookupAction.actionPerform(EntityLookupAction.java:120)
at io.jmix.ui.component.impl.ValuePickerImpl.lambda$setupButtonAction$0(ValuePickerImpl.java:231)
at io.jmix.ui.widget.JmixButton.fireClick(JmixButton.java:77)
at com.vaadin.ui.Button$1.click(Button.java:57)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at com.vaadin.server.ServerRpcManager.applyInvocation(ServerRpcManager.java:153)
at com.vaadin.server.ServerRpcManager.applyInvocation(ServerRpcManager.java:115)
at com.vaadin.server.communication.ServerRpcHandler.handleInvocation(ServerRpcHandler.java:442)
at com.vaadin.server.communication.ServerRpcHandler.handleInvocations(ServerRpcHandler.java:407)
at com.vaadin.server.communication.ServerRpcHandler.handleRpc(ServerRpcHandler.java:275)
at com.vaadin.server.communication.UidlRequestHandler.synchronizedHandleRequest(UidlRequestHandler.java:83)
at com.vaadin.server.SynchronizedRequestHandler.handleRequest(SynchronizedRequestHandler.java:40)
at com.vaadin.server.VaadinService.handleRequest(VaadinService.java:1636)
at com.vaadin.server.VaadinServlet.service(VaadinServlet.java:465)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:750)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at io.jmix.core.impl.logging.LogMdcFilter.doFilterInternal(LogMdcFilter.java:28)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:337)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:115)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:81)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:122)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:116)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:126)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:81)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:109)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:106)
at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:97)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:149)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:147)
at org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:125)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:103)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:89)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:112)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:82)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:55)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:221)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:186)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:177)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:360)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:891)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1784)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base/java.lang.Thread.run(Thread.java:834)

@Component("InMemoryEmployeeStore")
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class InMemoryEmployeeStore implements DataStore {

    @Autowired
    private EmployeeService employeeService;

    private String name;

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void setName(String name) {
        this.name = name;
    }

    @Nullable
    @Override
    public Object load(LoadContext<?> context) {
        return null;
    }

    @Override
    public List<Object> loadList(LoadContext<?> context) {

        try {
            return new ArrayList<>(employeeService.userList(employeeService.readUsers()));
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }

    }

    @Override
    public long getCount(LoadContext<?> context) {
        return 0;
    }

    @Override
    public Set<?> save(SaveContext context) {
        return Collections.emptySet();
    }

    @Override
    public List<KeyValueEntity> loadValues(ValueLoadContext context) {
        return null;
    }

    @Override
    public long getCount(ValueLoadContext context) {
        return 0;
    }
}
<filter id="filterWithСonditions"
        dataLoader="customersDl">
    <properties include=".*"/>
    <conditions>
        <propertyFilter property="hobby"
                        operation="NOT_IN_LIST"
                        operationEditable="true"
                        caption="Hobby condition"/>
    </conditions>
</filter>

я думал такой обработки Conditions - достаточно

А… так ошибка все еще связана с CollectionLoader… тогда я не могу дать ответ что именно ему не нравится… Если в дескрипторе экрана все нормально…

А что это за customersDl если у вас employeesDl? Мб в этом проблема?

Нет
это я брал из документации, мой код XML - впринципе аналогичен, и приведён выше, причём idea ни на что не ругается, но фильтр не отрабатывает, аналогичный фильтр работает с сущностями JPA из БД, соответственно по логике документации, здесь должен тоже работать

Ещё по поводу LoadDelegate, если оставить загрузку сущностей в XML дескрипторе то его loader вполне справляется но неудовлетворительно долго это делает, ввиду того что сущности парсятся из Json который получается при обращении по URL в другую базу, и получает в ответ около 11500 записей, потом парисм это в нужную модель и храним в виртуальном хранилище, и сомощью LoadDelegate и EmployeeService в предзагруженном состояниее удалось добиться того, что отображение экрана browse с 11500 сотрудников получается почти мгновенным)