Что нового

В данном разделе приведена информация о новой функциональности и возможных несовместимых изменениях в фреймворке Jmix и Jmix Studio версии 1.5. Примите их во внимание при обновлении с предыдущей версии фреймворка.

Для создания новых проектов в Jmix 1.5 или для апгрейда существующего проекта требуется Studio 1.5 или более поздней версии, поэтому в первую очередь обновите плагин Jmix Studio.

Минимальная требуемая версия IntelliJ IDEA - 2022.3.

Перейдите к разделу Апгрейд проекта, чтобы узнать, как обновить проект с помощью Studio. Процедура автоматической миграции вносит следующие изменения в ваш проект:

  • Обновляет версию Jmix BOM, которая, в свою очередь, определяет версии всех зависимостей.

  • Обновляет версию Jmix Gradle plugin.

  • Обновляет версию Gradle wrapper до 7.6 в gradle/wrapper/gradle-wrapper.properties.

  • Добавляет все необходимые включения в корневой Liquibase changelog.

  • Добавляет свойство main.liquibase.change-log в application.properties.

  • Переименовывает свойства jmix.liquibase.* в main.liquibase.*.

  • Обновляет конфигурации Liquibase для дополнительных хранилищ (если они есть).

  • В проектах с Flow UI создает роль UiMinimalRole.

  • Если в проекте используются дополнения Grid Export Actions и/или GrapesJS, изменяет имена их артефактов.

См. полный список опасных изменений, которые могут затронуть ваш проект после обновления.

Новая и улучшенная функциональность

Корневой Liquibase Changelog со всеми включениями

Корневой файл Liquibase changelog для главного хранилища теперь содержит директивы include для файлов changelog всех дополнений, используемых в проекте. Это делает changelog совместимым с плагином Gradle для Liquibase и Liquibase CLI, что позволяет запускать Liquibase вне Studio и приложения, например на сервере CI.

Проект должен содержать свойство main.liquibase.change-log, указывающее путь к корневому changelog, например:

main.liquibase.change-log=com/company/demo/liquibase/changelog.xml

Studio поддерживает включения автоматически при добавлении и удалении дополнений. Кроме того, при старте приложения Studio проверяет что включения в корневом changelog соответствуют дополнениям, используемым в проекте. При обнаружении расхождения отображается диалог с рекомендациями. Определенные пути можно добавить в игнорируемые, чтобы не получать о них уведомление впредь.

Процедура миграции Studio автоматически обновляет корневой changelog и устанавливает необходимое свойство.

Ядро Flow UI

Модуль Flow UI обновлен до Vaadin 23.3.

Следующие компоненты Vaadin интегрированы в Flow UI:

  • TabSheetJmixTabSheet (tabSheet в XML)

    <tabSheet width="100%">
        <tab id="mainTab" label="Main">
            <formLayout id="form" dataContainer="userDc">...</formLayout>
        </tab>
        <tab id="additionalTab" label="Onboarding steps">
            <vbox>
                <hbox>...</hbox>
                <dataGrid width="100%" dataContainer="stepsDc">...</dataGrid>
            </vbox>
        </tab>
    </tabSheet>
  • MultiSelectComboBoxJmixMultiSelectComboBox (multiSelectComboBox в XML)

    <instance id="productDc"
              class="com.company.demo.entity.Product">
        <fetchPlan extends="_base">
            <property name="tags" fetchPlan="_base"/>
        </fetchPlan>
        <loader/>
    </instance>
    <collection class="com.company.demo.entity.Tag" id="allTagsDc">
        <fetchPlan extends="_base"/>
        <loader id="allTagsDl">
            <query>
                <![CDATA[select e from Tag e]]>
            </query>
        </loader>
    </collection>
    <!-- ... -->
    <formLayout id="form" dataContainer="productDc">
        <textField id="nameField" property="name"/>
        <multiSelectComboBox property="tags" itemsContainer="allTagsDc"/>
    </formLayout>
  • UploadFileStorageUploadField (fileStorageUploadField в XML) и FileUploadField (fileUploadField в XML)

    <fileStorageUploadField id="uploadField"
            dataContainer="userDc" property="picture"/>
  • ImageJmixImage (image в XML)

    <image id="image"
            dataContainer="userDc" property="picture"
            height="280px" width="200px" classNames="user-picture"/>
  • Многие компоненты теперь поддерживают Tooltip, например:

    <textField id="nameField" property="name">
        <tooltip text="Product name" position="BOTTOM_START"/>
    </textField>

    В Studio tooltip добавляется с помощью кнопки Add в панели инспектора Jmix UI.

Input Dialog

Интерфейс Dialogs теперь включает метод createInputDialog(), который позволяет отображать диалог ввода произвольных параметров. Например:

dialogs.createInputDialog(this)
        .withParameters(
                stringParameter("name").withLabel("Name"),
                intParameter("age").withLabel("Age"))
        .withCloseListener(dialogCloseEvent -> {
            if (dialogCloseEvent.closedWith(DialogOutcome.OK)) {
                System.out.println("Name: " + dialogCloseEvent.getValue("name"));
                System.out.println("Age: " + dialogCloseEvent.getValue("age"));
            }
        })
        .open();

Background Tasks

Теперь Flow UI имеет механизм выполнения фоновых задач без блокировки пользовательского интерфейса, аналогичный классическому UI.

Пример запуска задачи и отображения прогресса в надписи:

@Autowired
private BackgroundWorker backgroundWorker;

@ViewComponent
private Span taskProgress;

@Subscribe("testBtn")
public void onTestBtnClick(ClickEvent<Button> event) {
    BackgroundTaskHandler<Void> handler = backgroundWorker.handle(new SampleTask(15, null, 10));
    handler.execute();
}

protected class SampleTask extends BackgroundTask<Integer, Void> {
    int count;

    public SampleTask(long timeoutSeconds, View<?> view, int count) {
        super(timeoutSeconds, view);
        this.count = count;
    }

    @Override
    public Void run(TaskLifeCycle<Integer> taskLifeCycle) throws Exception {
        for (int i = 1; i < count + 1; i++) {
            Thread.sleep(1000);
            taskLifeCycle.publish(i);
        }
        return null;
    }

    @Override
    public void progress(List<Integer> changes) {
        taskProgress.setText(changes.get(0) + "");
    }
}

Пример запуска задачи и отображения прогресса в модальном диалоге:

@Autowired
private Dialogs dialogs;

@Subscribe("testBtn")
public void onTestBtnClick(ClickEvent<Button> event) {
    dialogs.createBackgroundTaskDialog(new SampleTask(15, this, 10))
            .withTotal(10)
            .withCancelAllowed(true)
            .open();
}

protected class SampleTask extends BackgroundTask<Integer, Void> {
    int count;

    public SampleTask(long timeoutSeconds, View<?> view, int count) {
        super(timeoutSeconds, view);
        this.count = count;
    }

    @Override
    public Void run(TaskLifeCycle<Integer> taskLifeCycle) throws Exception {
        for (int i = 1; i < count + 1; i++) {
            Thread.sleep(1000);
            taskLifeCycle.publish(i);
        }
        return null;
    }
}

Flow UI в дополнениях

Модули Flow UI теперь доступны для следующих дополнений:

  • Multitenancy

  • Quartz

  • Application Settings

  • Grid Export Actions (с возможностью экспорта всех строк, см. Экспорт в Excel).

Дизайнер меню Flow UI

Значительно усовершенствован дизайнер меню Flow UI. В режиме "single", Studio позволяет добавлять в проект пункты меню, унаследованные из подключенных дополнений. Доступные пункты всегда отображаются в отдельной панели слева, что позволяет при необходимости перетаскивать их в свое меню.

Экспорт в Excel

Дополнение Grid Export Actions теперь позволяет пользователям экспортировать в Excel все строки по текущим критериям отбора. Действие excelExport отображает диалог с опциями All rows | Current page | Selected rows.

Ранее данное действие позволяло экспортировать только строки текущей загруженной страницы.

UI управления пессимистичными блокировками

В ядро фреймворка добавлен UI управления пессимистичными блокировками. Соответствующий пункт меню можно найти в подменю Administration классического UI и в подменю System Flow UI.

Окно инструментов UI дизайнера

Дизайнер и классического и Flow UI теперь имеет одно окно инструментов справа: Jmix UI. Оно включает дерево иерархии компонентов и инспектор свойств выбранного компонента.

Палитра компонентов открывается при нажатии Add component в контекстном меню иерархии компонентов, в верхней панели XML-дескриптора, или в меню Generate.

Сниппеты кода

Палитра Code Snippets теперь доступна при нажатии конпки Code Snippets в верхней панели окна редактора для бинов Spring и контроллеров экранов.

Окно инструментов со сниппетами удалено.

Предварительные функции

Универсальный фильтр для Flow UI

Компонент GenericFilter (genericFilter в XML) позволяет пользователям фильтровать данные по произвольным условиям во время работы приложения.

Пример использования:

<facets>
    <dataLoadCoordinator auto="true"/>
    <queryParameters>
        <genericFilter component="filter"/>
    </queryParameters>
</facets>
<layout>
        <genericFilter id="filter" dataLoader="usersDl"
                       summaryText="My filter">
            <responsiveSteps>
                <responsiveStep minWidth="0" columns="1"/>
                <responsiveStep minWidth="800px" columns="2"/>
                <responsiveStep minWidth="1200px" columns="3"/>
            </responsiveSteps>
        </genericFilter>

Элемент genericFilter фасета queryParameters необходим для отражения состояния фильтра в параметрах запроса в URL. Благодаря ему тот же фильтр применяется при навигации обратно в экран списка из экрана деталей, а также при перезагрузке страницы браузера.

В данный момент универсальный фильтр поддерживает только условия по атрибутам сущностей. Все остальные функции фильтра классического UI (условия JPQL, кастомные условия, группировка условий, возможность сохранения конфигураций) будут реализованы в следующем релизе.

Опасные изменения

Измененные артефакты дополнений

Grid Export Actions

Изменено имя артефакта стартера:

  • Было io.jmix.ui:io.jmix.ui:jmix-ui-export-starter

  • Стало io.jmix.gridexport:jmix-gridexport-ui-starter

И базовый пакет:

  • Было io.jmix.uiexport

  • Стало io.jmix.gridexportui

Процедура миграции Studio автоматически изменяет имя артефакта в build.gradle. Имя пакета необходимо изменить вручную, если он используется в коде проекта.

GrapesJS

Изменено имя артефакта стартера:

  • Было io.jmix.grapesjs:jmix-grapesjs-starter

  • Стало io.jmix.grapesjs:jmix-grapesjs-ui-starter

и имя артефакта темы:

  • Было io.jmix.grapesjs:jmix-grapesjs

  • Стало io.jmix.grapesjs:jmix-grapesjs-ui

Quartz

Необходим дополнительный стартер для UI: io.jmix.quartz:jmix-quartz-ui-starter

Liquibase Properties

  1. Префикс jmix.liquibase.* переименован в main.liquibase.* для соответствия паттерну именования свойств источника данных (например main.datasource.url, где main - это имя хранилища). Если добавить хранилище second, его свойства конфигурации Liquibase будут начинаться с second.liquibase.*.

  2. application.properties должны содержать путь к корневому файлу Liquibase changelog для каждого хранилища. Например:

    main.liquibase.change-log=com/company/demo/liquibase/changelog.xml
    
    second.liquibase.change-log=com/company/demo/liquibase/second-changelog.xml

Конфигурация хранилища данных

Конфигурация дополнительных хранилищ данных должны быть изменены.

Класс LiquibaseChangeLogProcessor удален.

Ранее при определении дополнительного хранилища с установкой параметра DB Schema Management в значение Create and Update Studio генерировала следующее определение бина:

@Bean
public SpringLiquibase thirdLiquibase(
            LiquibaseChangeLogProcessor processor,
            @Qualifier("thirdDataSource") DataSource dataSource) {
   return JmixLiquibaseCreator.create(
                dataSource,
                new LiquibaseProperties(),
                processor,
                "third");
}

Теперь определение бина должно выглядеть следующим образом (здесь "third" - это имя хранилища):

@Bean("thirdLiquibaseProperties")
@ConfigurationProperties(prefix = "third.liquibase")
public LiquibaseProperties thirdLiquibaseProperties() {
   return new LiquibaseProperties();
}

@Bean("thirdLiquibase")
public SpringLiquibase thirdLiquibase(
            @Qualifier("thirdDataSource") DataSource dataSource,
            @Qualifier("thirdLiquibaseProperties") LiquibaseProperties liquibaseProperties) {
    return JmixLiquibaseCreator.create(
                dataSource,
                liquibaseProperties);
}

Ссылки на сообщения в форматтерах

Исправлена неверная обработка ссылок на сообщения в форматтерах, определенных в XML.

Теперь, как и для других сообщений, ссылка вида msg://myFormat ищет сообщение с группой текущего экрана, например com.company.app.screen.foo/myFormat. Префикс с тройной косой чертой ищет сообщение без группы, например myFormat.

Для адаптации проекта к данному изменению необходимо найти все ссылки на сообщения в форматтерах и заменить двойную косую черту на тройную. Например:

<formatter>
     <date format="msg:///myDateFormat"/>
</formatter>

UiMinimalRole в проектах с Flow UI

UiMinimalRole определяет доступ к главному экрану и экрану логина. Для того, чтобы можно было заменить эти экраны, данная роль перемещена из фреймворка в проекты. Например:

package com.company.demo.security;

import io.jmix.core.entity.KeyValueEntity;
import io.jmix.security.model.*;
import io.jmix.security.role.annotation.*;
import io.jmix.securityflowui.role.annotation.ViewPolicy;

@ResourceRole(name = "Flow UI: minimal access", code = UiMinimalRole.CODE, scope = SecurityScope.UI)
public interface UiMinimalRole {

    String CODE = "flowui-minimal";

    @ViewPolicy(viewIds = "MainView")
    void main();

    @ViewPolicy(viewIds = "LoginView")
    @SpecificPolicy(resources = "flowui.loginToUi")
    void login();

    @EntityPolicy(entityClass = KeyValueEntity.class, actions = EntityPolicyAction.READ)
    @EntityAttributePolicy(entityClass = KeyValueEntity.class, attributes = "*", action = EntityAttributePolicyAction.VIEW)
    void keyValueEntity();
}

Flow UI DataGrid

  1. Метод getColumns() компонентов DataGrid и TreeDataGrid теперь возвращает только колонки, не скрытые подсистемой безопасности. Ранее он возвращал все колонки, включая скрытые.

  2. Колонки, скрытые подсистемой безопасности, не изменяют своего свойства видимости. Ранее атрибут visible таких колонок устанавливался в false.

ui-test-assist

Модуль ui-test-assist теперь не приносит транзитивных зависимостей на Spock и Groovy. Кроме того, он не содержит классов UiTestAssistSpecification, ScreenSpecification м TestMainScreen.

Все касающееся Spock и Groovy было перемещено в новый модуль ui-test-assist-spock.

Если ваш проект использовал классы ScreenSpecification или UiTestAssistSpecification, добавьте зависимость в build.gradle:

testImplementation 'io.jmix.ui:jmix-ui-test-assist-spock'

и измените импорты классов ScreenSpecification, UiTestAssistSpecification, TestMainScreen на io.jmix.ui.testassistspock.*.

Известные проблемы

Jmix 1.5.3 зависит от Spring Boot 2.7.13, который в свою очередь зависит от версии Hazelcast 5.1.7. К сожалению, в Hazelcast 5.1.7 имеется баг. Этот баг делает невозможным работу с кластером Kubernetes во встроенном режиме, и инструкции, описанные в разделе Кластер Kubernetes, не работают.

Обходным путем является откат к версии Hazelcast 5.1.5. Это можно сделать, добавив следующую инструкцию в файл build.gradle:

implementation('com.hazelcast:hazelcast') {
    version {
        strictly '5.1.5'
    }
}

Список изменений