Открытие редактора Many-To-Many

Юрий, в очередной раз вынужден обратиться к вам за помощью.
Тот код который вы предоставили отлично работает в browser-скрине. Но у меня есть edit-скрин, в котором есть таблица с атрибутом многие-ко-многим, где создаются объекты того же типа. Пытаюсь сделать тоже самое, но получаю ошибку даже при попытке реализовать добавление как в семпле:

@Subscribe("createNewBtn.newDriveBtn")
private void onCreateNewBtnNewDriveBtn(Action.ActionPerformedEvent event) { 
    newUnit(UnitDrive.class, UnitDriveEdit.class); 
}

private <T extends Screen & EditorScreen> void newUnit(Class entityClass, Class<T> screenClass) {
        screenBuilders.editor(unitsTable)
                .withScreenClass(screenClass)
                .newEntity(((Unit) metadata.create(entityClass)))
                .build()
                .show();
    }
ошибка
java.lang.IllegalArgumentException: argument type mismatch
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.haulmont.chile.core.model.utils.MethodsCache.invokeSetter(MethodsCache.java:58)
	at com.haulmont.chile.core.model.impl.AbstractInstance.setValue(AbstractInstance.java:127)
	at com.haulmont.cuba.core.entity.BaseGenericIdEntity.setValue(BaseGenericIdEntity.java:126)
	at com.haulmont.chile.core.model.impl.AbstractInstance.setValue(AbstractInstance.java:111)
	at com.haulmont.cuba.gui.builders.EditorBuilderProcessor.initializeNestedEntity(EditorBuilderProcessor.java:254)
	at com.haulmont.cuba.gui.builders.EditorBuilderProcessor.initEntity(EditorBuilderProcessor.java:183)
	at com.haulmont.cuba.gui.builders.EditorBuilderProcessor.buildEditor(EditorBuilderProcessor.java:79)
	at com.haulmont.cuba.gui.builders.EditorClassBuilder.build(EditorClassBuilder.java:150)
	at com.borets.wedb.web.activities.activity.fragments.ActivityUnitsFragment.newUnit(ActivityUnitsFragment.java:118)
	at com.borets.wedb.web.activities.activity.fragments.ActivityUnitsFragment.onCreateNewBtnNewDriveBtn(ActivityUnitsFragment.java:111)
	at com.haulmont.bali.events.EventHub.publish(EventHub.java:170)
	at com.haulmont.cuba.gui.components.actions.BaseAction.actionPerform(BaseAction.java:221)
	at com.haulmont.cuba.web.gui.components.WebPopupButton.lambda$setPopupButtonAction$1(WebPopupButton.java:328)
	at com.haulmont.cuba.web.widgets.CubaButton.fireClick(CubaButton.java:76)
	at com.vaadin.ui.Button$1.click(Button.java:57)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	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:431)
	at com.vaadin.server.communication.ServerRpcHandler.handleInvocations(ServerRpcHandler.java:396)
	at com.vaadin.server.communication.ServerRpcHandler.handleRpc(ServerRpcHandler.java:260)
	at com.vaadin.server.communication.UidlRequestHandler.synchronizedHandleRequest(UidlRequestHandler.java:82)
	at com.vaadin.server.SynchronizedRequestHandler.handleRequest(SynchronizedRequestHandler.java:40)
	at com.vaadin.server.VaadinService.handleRequest(VaadinService.java:1577)
	at com.vaadin.server.VaadinServlet.service(VaadinServlet.java:425)
	at com.haulmont.cuba.web.sys.CubaApplicationServlet.serviceAppRequest(CubaApplicationServlet.java:329)
	at com.haulmont.cuba.web.sys.CubaApplicationServlet.service(CubaApplicationServlet.java:215)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:107)
	at jespa.http.HttpSecurityService.doFilter(HttpSecurityService.java:1596)
	at com.borets.wedb.web.JespaAuthProvider.doFilter(JespaAuthProvider.java:94)
	at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:112)
	at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:73)
	at com.haulmont.cuba.web.sys.CubaHttpFilter.doFilter(CubaHttpFilter.java:108)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
	at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:650)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:748)

Если заменить newEntity на editEntity на форма открывается нормально, но добавленные таким образом сущности не комитятся при комите родительской сущности. Дочерние сущности скомичены при закрытии формы редактирования, но в preCommit я наблюдаю следующую ситуацию:
image. Т.е. запись в Dc попала, но атрибут, который должен вернуть ее же равен null, соответственно родительская сущность комитится без изменения атрибута многие-ко-многим. Если при этом я использую действие add то все хорошо.

UPD: Забыл сказать, что Table находится во фрейме. Из контроллера фрейма собственно и пытаюсь создавать дочерние элементы.

UPD2: пока вышел из положения по старинке:

private <T extends Unit> void newUnit(Class<T> entityClass) {
    final T unit = metadata.create(entityClass);
    final Screen screen = screenBuilders.editor(entityClass, this)
            .editEntity(unit)
            .build();
    screen.addAfterCloseListener(afterCloseEvent -> {
        if (afterCloseEvent.getCloseAction().equals(WINDOW_COMMIT_AND_CLOSE_ACTION))
            unitsDc.getMutableItems().add(((Unit) ((StandardEditor) screen).getEditedEntity()));
    });
    screen.show();
}

Здравствуйте,

Не могли бы вы подробнее описать вариант использования? Вы хотите создать Many-To-Many запись и сохранить её вместе с родителем?

Тут стоит отметить, что композиция (@Composition) для Many-To-Many не поддерживается.

Юрий, спасибо за то, что помогаете мне.

Даже не знаю, что описывать. Вариант простейший. Отношение действительно Many-To-Many. Вот только дочернюю запись я хочу не добавить, а создать в экране родительской сущности. И да, я понимаю, что дочерняя запись будет скомичена при нажатии ОК, а не в родительском экране.
Т.е. мне, как я себе это представлю, просто нужно вызвать edit-экран для новой сущности, заполнить ее, нажать ОК, поместить в датасет и показать в таблице.


На самом деле выяснилось, что так как я описал в UPD2 работает тоже “не совсем корректно”. Попробую пояснить в чем проблема. У Unit есть атрибут композиция один-ко-многим. Соответствующий дата-контейнер добавлен к родительской сущности:

<data>
   <instance id="activityDc" class="com.borets.wedb.entity.ActivityInstall" view="activityInstall-edit">
        <loader/>
        <collection id="unitsDc" property="units">
             <collection id="unitConditionDc" property="conditions"/>
        </collection>
   </instance>
</data>

Есть 3 тест-кейса:

  1. Создаем Activity, добавляем Unit (AddAction), создаем UnitCoditions (добавляя в соответствующий Dc). Сохраняем. Результат: ОК;
  2. Создаем Activity, создаем Unit (описано в UPD2), создаем UnitConditions. Сохраняем. Результат: UnitConditions потеряны.
  3. Создаем Activity, создаем Unit(описано в UPD2). Сохраняем. Открываем Activity. Создаем UnitConditions. Сохраняем. Результат: ОК.

PS. Я извиняюсь, возможно, это вообще не связано с предметом обсуждения.
Пока писал, пришла мысль, что это может быть связано с edit-view Unit, в который в отличии от edit-view Activity этот атрибут (композиция) не включен. Проверил. Идея не правильная.

Юрий, решил набросать тестовый проект, который иллюстрирует проблему, с которой я столкнулся. Может вы сможете что-то мне посоветовать.
manyToMany.zip (89,7 КБ)

Что нужно смотреть:

  1. Application -> Activities -> Create -> Заполняем поле Name.
  2. На форме две кнопки Create (1.1 и 1.2) какой пользоваться не важно. Обе дают один результат. Жмем Create -> Заполняем Name -> OK
  3. Выделяем в таблице только что созданный объект. Жмем 2.CreateCondition -> OK
    Результат: Application -> Conditions, видим что Condition не скомичен.

  1. Повторяем п.1
  2. Добавляем предварительно созданный Unit с помощью кнопки 1.3 Add
  3. Повторяем п. 3.
    Результат: Application -> Conditions, видим что Condition скомичен.

PS: очень рассчитываю на вашу помощью.

1 симпатия

Юрий, извиняюсь за беспокойство, но вопрос все еще актуален для меня. Скажите, не удалось посмотреть тестовый проект?

Насколько я понял, вы хотите программно добавить объект в DataContext, отслеживать его изменения и сохранить его в БД. В этом случае в вашем коде не хватает вызова DataContext.merge():

@Inject
private DataContext dataContext;

@Subscribe("unitsTable.createCondition")
private void onUnitsTableCreateCondition(Action.ActionPerformedEvent event) {
    Condition condition = metadata.create(Condition.class);
    condition.setName(UUID.randomUUID().toString());
    condition.setUnit(unitsTable.getSingleSelected());

    // merge before adding to container
    Condition mergedCondition = dataContext.merge(condition);

    conditionsDc.getMutableItems().add(mergedCondition);
}

См. также пример помещения сущности в DataContext в документации.

P.S. Пожалуйста не используйте в проектах, которые прикладываете, ссылки на свою приватную инфраструктуру, такую как Maven репозитории.

1 симпатия

Юрий, спасибо. Сегодня проверю - отпишусь. А можете объяснить, почему в случае с уже существующим объектом-родителем этого не требуется, а в случае с новым - требуется?
UPD: Еще раз спасибо. Все отлично!