Релиз 3.1

В данный момент доступен только релиз-кандидат Jmix 3.1. Для работы с ним установите ночную сборку Studio 3.1.

Документация по релизу в процессе обновления.

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

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

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

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

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

  • Обновляет версию Gradle wrapper до 9.8.0

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

Обновленные зависимости

Следующие основные зависимости были обновлены:

  • Spring Boot 4.1

  • Vaadin 25.3

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

Jmix CLI

Jmix CLI — новый инструмент командной строки, который создает Jmix-проекты без IDE: в терминале, в скрипте или с помощью ИИ-агента для написания кода. Он использует те же шаблоны проектов, что и Studio, и добавляет в созданный проект Jmix Agent Toolkit. Как установить и использовать его, см. в разделе Использование Jmix CLI.

Улучшения Studio

  • В мастере создания экранов для экранов списка и экранов master-detail появился новый шаг, на котором выбирается способ фильтрации данных пользователями: genericFilter (по умолчанию), набор компонентов propertyFilter для выбранных атрибутов, fullTextFilter, если в проект включено дополнение Search, или отсутствие фильтра.

  • Дизайнер сущностей позволяет задать аннотацию @LookupField для сущности и для её ссылочных атрибутов. Мастер создания экранов учитывает эту аннотацию при генерации компонентов для ссылок.

  • Новое действие Views with Attribute дизайнера сущностей показывает компоненты экранов, связанные с атрибутом сущности, и открывает дескриптор экрана на выбранном компоненте. Оно также позволяет добавить атрибут в экраны сущности. Действие доступно на панели инструментов таблицы атрибутов и в каждой строке атрибута.

  • Новое действие Copy View создает копию экрана вместе с его контроллером, дескриптором и сообщениями.

  • Действие New → Advanced → Custom Component создает файлы кастомного UI-компонента: класс компонента, его XML-загрузчик и Spring-конфигурация для его регистрации. Действие также может создать XSD-схему для XML-элемента компонента, метаданные для визуального дизайнера Studio, и файл JavaScript с Lit веб-компонентом.

  • Диалог со списком changelog-файлов Liquibase, еще не примененных к базе данных, теперь показывает содержимое выбранного changelog-файла. С его помощью можно просматривать changelog-файлы, которые поставляются с дополнениями.

  • Дизайнер JPQL можно открыть с помощью иконки на левом поле редактора для запросов в LoadContext.Query и в методе setQueryString().

  • Инспекции Studio проверяют структуру отчетов, определенных в Java-коде и содержащих потоковый бэнд. Они сообщают о структуре, которую не поддерживает движок отчетов, например, о втором потоковом бэнде, дочернем бэнде потокового бэнда или наборе данных, отличном от SQL или JPQL.

Новое дополнение AI Chat

Новое дополнение AI Chat предоставляет UI-компоненты для создания интерфейсов AI-чата. Ответы формирует LLMProvider, реализуемый приложением, поэтому компоненты работают со Spring AI, SDK поставщика модели или вашим собственным сервисом.

  • aiChat — полноценный чат с потоковой выдачей ответов, сообщениями в формате Markdown, вложенными файлами и действиями для отдельных сообщений. Его можно связать с контейнером данных вашей собственной сущности сообщения, чтобы сохранять переписку.

  • aiMessageList и aiMessageInput можно использовать по отдельности, чтобы создать чат, возможности которого выходят за рамки aiChat.

  • aiCodeBlock отображает исходный код в сообщениях чата или в любом другом экране.

Новое дополнение UI Recovery

Новое дополнение UI Recovery сохраняет несохраненные изменения экранов, если сервер перезапускается, истекает HTTP-сессия или закрывается браузер. Во время работы пользователя оно сохраняет изменения в базу данных в виде черновика и предлагает восстановить черновик, когда пользователь снова открывает экран. Черновики доступны в кластере и после полного перезапуска приложения.

Дополнение Dynamic Model

Дополнение Dynamic Model больше не является экспериментальным. В нём появились следующие новые возможности:

Дополнение AI Tools

В дополнении AI Tools появились следующие новые возможности:

  • Аннотация @ExcludeFromAi на уровне кода скрывает сущность или атрибут от инструментов Data Load. В отличие от include- и exclude-свойств jmix.aitools.dataload.*, аннотацию нельзя переопределить при развёртывании.

  • Новые чаты в UI получают заголовки автоматически. По умолчанию заголовком становится первое сообщение пользователя. В свойстве jmix.aitools.ui.conversation-title-mode можно выбрать короткий заголовок, сгенерированный моделью, или заголовок по умолчанию.

Дополнение BPM

В BPMN-моделере дополнения BPM появились следующие новые возможности:

  • Можно задавать уровень истории процесса (History level) вместе с другими свойствами процесса. Выбранный уровень переопределяет уровень истории движка процессов.

  • Можно задавать атрибуты flowable:exclusive, flowable:asyncLeave и flowable:asyncLeaveExclusive для задач, встроенных подпроцессов, элементов call activity, шлюзов и большинства событий. Для пользовательских задач, шлюзов и событий теперь доступен и атрибут flowable:async. См. раздел Asynchronous Execution Properties.

  • Можно добавлять в процессы события эскалации, задавать их свойства и управлять определениями эскалаций (Escalation definitions) в свойствах процесса.

Дополнение Reports

В дополнении Reports появились следующие новые возможности:

  • Бэнд отчёта теперь может быть потоковым: его строки читаются из курсора базы данных и записываются в документ XLSX по одной, а не загружаются в память. Чтобы сделать бэнд потоковым, установите для бэнда флажок Streaming на экране деталей отчёта или задайте streaming = true в аннотации @BandDef.

  • Новый тип набора данных AI-generated JPQL позволяет описать данные бэнда на естественном языке. На экране деталей отчёта LLM генерирует по промпту JPQL-запрос, который хранится в определении отчёта. При запуске отчёта сохранённый запрос выполняется через DataManager с учётом разрешений текущего пользователя на доступ к данным.

Дополнение Grouping Data Grid

Чтобы сохранять применённые фильтры по заголовкам колонок в параметрах запроса URL, добавьте элемент groupDataGridFilter в фасет urlQueryParameters.

Дополнение Email Sending

Дополнение Email Sending упрощает настройку аутентификации OAuth2 для почтовых серверов Google и Microsoft:

  • Новый экран Email connection заменяет экран OAuth2 token. Он подключает почтовый ящик через Authorization Code Flow или, для Microsoft, через Device Code Flow и сохраняет refresh-токен. Экран также показывает параметры подключения и проверяет подключение к почтовому серверу.

  • Новое свойство jmix.email.oauth2.redirect-uri задаёт redirect URI для Authorization Code Flow, например, если приложение работает за обратным прокси.

  • Дополнение само задаёт механизм аутентификации XOAUTH2, поэтому добавлять его в spring.mail.properties.* не нужно.

Экраны чтения

Новые экраны чтения показывают экземпляр сущности, который пользователи не могут редактировать. Экран чтения наследуется от StandardReadView, загружает сущность по её идентификатору и всегда имеет DataContext только для чтения.

  • Экраны чтения можно создавать в Studio с помощью шаблонов Entity read view и DTO entity read view мастера создания экранов.

  • Экраны чтения можно открывать с помощью бинов ViewNavigators и DialogWindows, а также бина ViewBuilders дополнения Tabbed Application Mode.

  • Если у сущности есть экран чтения, действие list_read открывает его вместо экрана деталей в режиме только для чтения. Новое действие entity_read показывает сущность, выбранную в компоненте выбора сущностей.

  • Аннотация @ReadViewTemplate генерирует экран чтения для сущности во время выполнения. Дополнение Dynamic Model генерирует экраны чтения для динамических сущностей, в которых объявлен экран с type: read.

Изменение размера боковой панели

Теперь пользователь может изменять размер компонента sidePanelLayout во время работы приложения, перетаскивая разделитель у внутреннего края панели. Для горизонтальных положений изменяется ширина, для вертикальных — высота. Размер не выходит за заданные минимальный и максимальный пределы. Фасет settings сохраняет и восстанавливает выбранный пользователем размер.

Компонент SVG

Новый компонент svg встраивает SVG-разметку непосредственно в экран. Разметку можно задать во вложенном элементе content, загрузить из файла с помощью атрибута file или установить в контроллере экрана.

Компонент Popover

Новый компонент popover — всплывающее окно, которое открывается рядом с целевым компонентом и может содержать любые компоненты. Объявите его в дескрипторе экрана или фрагмента и укажите в атрибуте target идентификатор целевого компонента. Окно открывается по нажатию, при наведении курсора, при получении фокуса или из контроллера экрана. Его можно сделать модальным.

PopoverRenderer для DataGrid

Новый рендерер всплывающего окна показывает длинный текст в колонке dataGrid. Текст в ячейке обрезается многоточием, если не помещается, а при нажатии на ячейку полный текст показывается во всплывающем окне. Добавьте элемент popoverRenderer в колонку в XML или создайте PopoverRenderer в Java-коде.

Компоненты-слайдеры

Новые компоненты позволяют выбрать число или диапазон чисел, перетаскивая ползунки по шкале:

  • integerSlider и decimalSlider выбирают значение типа Integer или Double. Как и другие поля, их можно связать с атрибутом сущности и проверять валидаторами.

  • integerRangeSlider и decimalRangeSlider выбирают диапазон значений, например для фильтрации данных.

Аннотация @LookupField

Новая аннотация сущности @LookupField определяет компонент, который фреймворк генерирует для выбора ссылки: entityComboBox с выпадающим списком или entityPicker, который открывает экран выбора. Она она учитывается при генерации параметров genericFilter и propertyFilter, редактируемых ячеек dataGrid и в шаблонах экранов времени выполнения. Поместите аннотацию на класс сущности, чтобы применить ко всем ссылкам на эту сущность, или на ссылочный атрибут, чтобы применить только к этому атрибуту. Также можно задать действия компонента и загружать элементы выпадающего списка порциями по мере ввода текста пользователем – с помощью JPQL-запроса или поиска по имени экземпляра.

Элемент itemsQuery компонента entityComboBox получил новый атрибут byInstanceName, который находит элементы по имени экземпляра без JPQL-запроса.

Критические изменения

Vaadin 25.3

Обновление до Vaadin 25.3 приносит следующие изменения (см. #5720):

  • Компонент image больше не может содержать другие компоненты. У JmixImage больше нет методов setText()/getText(), setWhiteSpace(), setEnabled()/isEnabled(), add() и remove(). В XML атрибуты enabled, text и whiteSpace, а также вложенные компоненты image игнорируются.

  • Удалён защищённый метод JmixMenuBarRootItem.updateClassName().

  • InMemoryUploadHandler и FileTemporaryStorageUploadHandler теперь наследуются от AbstractUploadHandler из Vaadin вместо TransferProgressAwareHandler. FileTemporaryStorageUploadHandler удаляет временный файл, если загрузка завершилась ошибкой или была отклонена, а его метод getFileInfo() возвращает null, пока текущая загрузка не создаст файл.

  • В дополнении Tabbed Application Mode конфигурация @Push оболочки приложения (app shell) применяется до вызова слушателей UIInitListener, поэтому слушатель может её переопределить.

  • Anchor.setHref() и IFrame.setSrc(), а также XML-атрибуты href и src компонентов anchor и iframe выбрасывают IllegalArgumentException для схемы URL, которую Vaadin не считает безопасной, например javascript:. Добавьте схему в параметр конфигурации Vaadin safeUrlSchemes или используйте в коде setUnsafeHref() и setUnsafeSrc().

  • addThemeName("") выбрасывает IllegalArgumentException.

  • Методы expand(), collapse(), expandRecursively() и collapseRecursively() компонента TreeGrid теперь принимают параметры Collection<? extends T> и Stream<? extends T>. Обновите сигнатуры этих методов, если вы переопределяете их в подклассе TreeDataGrid.

  • Многие константы VaadinIcon и соответствующие им константы JmixFontIcon объявлены устаревшими. Метод setRole() класса JmixSideDialog объявлен устаревшим, вместо него используйте setAriaRole(). Метод withOverlayRole() интерфейса Dialogs.SideDialogBuilder объявлен устаревшим, вместо него используйте withAriaRole().

Дополнение AI Tools

  • Строки, возвращаемые JpqlExecutionResult.getRows() и EntityDataLoadResult.getRows(), теперь содержат null для колонки, у которой нет значения (см. #5598). Раньше такая колонка содержала пустую строку. Строки, которые инструменты возвращают модели, также содержат null вместо пустой строки. Сигнатуры методов не изменились, поэтому компилятор не покажет места, которые нужно исправить. Проверяйте значения на null при чтении строк:

    EntityDataLoadResult result = aiDataLoadService.loadData("customers with their email");
    for (Map<String, Object> row : result.getRows()) {
        Object email = row.get("email");
        String text = email != null ? email.toString() : ""; // email может быть null
    }
  • EntityDataLoadResult.getQuery() теперь возвращает запрос, который был фактически выполнен или не прошёл валидацию (см. #5749). Если сгенерированный запрос был исправлен, метод возвращает исправленный запрос, а его объяснение, предупреждения, maxResults и firstResult берутся из результата исправления. Исходный запрос, сгенерированный LLM, получить из результата нельзя.

  • В интерфейсе AiChatMessageService появился новый метод loadLatestMessages() (см. #5649). Если у вас есть собственная реализация этого интерфейса, реализуйте этот метод. Хаб чатов теперь сортирует и группирует беседы по времени последнего сообщения, а не по времени создания.

  • Заголовком нового чата теперь становится первое сообщение пользователя (см. #5648). Раньше у чата оставался заголовок New AI Conversation. Если ваш код задаёт чату собственный заголовок до первого сообщения, первое сообщение заменит этот заголовок. Чтобы сохранить свой заголовок, вызовите setTitleMode(AiConversationTitleMode.NONE) у AiChatFragment, который показывает этот чат. Чтобы вернуть прежнее поведение во всём приложении, установите для свойства jmix.aitools.ui.conversation-title-mode значение NONE.

Дополнение Charts

Дополнение Charts теперь использует Apache ECharts 6.1 вместо 5.4 (см. #5699). Существующие экраны работают без изменений кода, но диаграммы могут выглядеть иначе:

  • В теме по умолчанию используются новая цветовая палитра и новые отступы. Легенда расположена внизу по центру, а заголовок — вверху по центру. Чтобы получить вид, близкий к предыдущей версии, задайте colorPalette="#5470c6, #91cc75, #fac858, #ee6666, #73c0de, #3ba272, #fc8452, #9a60b4, #ea7ccc" для charts:chart, top="0" для charts:legend и left="0" top="0" для charts:title. Проверьте диаграммы, у которых есть компоненты вверху или внизу, например слайдер dataZoom: они могут перекрывать заголовок или легенду.

  • Подписи и названия осей теперь остаются внутри диаграммы, а названия осей сдвигаются так, чтобы не перекрывать подписи осей. Чтобы вернуть прежнее поведение, задайте outerBoundsMode="NONE" для charts:gridItem или nameMoveOverlap="false" для оси.

  • Стили форматированного текста (rich text) наследуют настройки шрифта и тени текста обычной подписи. Чтобы вернуть прежнее поведение, добавьте "richInheritPlainLabel": false в nativeJson.

  • Фигуры bar, pictorialBar, candlestick и boxplot больше не выходят за пределы сетки. Чтобы вернуть прежнее поведение, задайте containShape="false" для оси.

  • Второй аргумент функции valueFormatter всплывающей подсказки теперь является индексом в исходных данных серии, а не индексом после фильтрации dataZoom. Обновите JavaScript-функции, которые его используют.

  • Grid.containLabel объявлен устаревшим. Вместо него используйте outerBoundsMode="SAME" вместе с outerBoundsContain="AXIS_LABEL". Когда containLabel включён, параметры outerBounds* игнорируются.

Дополнение Email Sending

  • Экран OAuth2 token заменён новым экраном подключения, а его идентификатор изменён с email_tokenView на email_connectionView (см. #5546). Старый маршрут email/token по-прежнему работает. Замените старый идентификатор экрана:

    • в menu.xml, если приложение использует режим единого меню. Иначе при открытии главного экрана возникнет исключение NoSuchViewException.

    • в аннотациях @ViewPolicy и @MenuPolicy ваших ресурсных ролей. Иначе пользователи потеряют доступ к экрану.

  • Дополнение больше не определяет бин mailSendTaskExecutor (см. #5703). Раньше Vaadin мог использовать этот бин для своей асинхронной работы, а приложение не запускалось, если в нём был другой бин TaskExecutor. Теперь:

    • Код, который инжектирует бин mailSendTaskExecutor по имени, при запуске завершается ошибкой NoSuchBeanDefinitionException. Удалите такие инжекции.

    • Ваш собственный бин с именем mailSendTaskExecutor больше не заменяет пул отправки писем. Дополнение игнорирует его и при запуске записывает в лог предупреждение.

    • Методы @Async и асинхронные задачи Vaadin выполняются в бине applicationTaskExecutor из Spring Boot, а не в пуле отправки писем. У этого пула другой размер и неограниченная очередь.

  • Если аутентификация OAuth2 включена, дополнение строже проверяет конфигурацию (см. #5539):

    • Свойство spring.mail.password игнорируется, а дополнение записывает в лог предупреждение. Раньше пароль, заданный вместе с аутентификацией OAuth2, переключал дополнение на базовую аутентификацию.

    • Приложение не запускается, если не задано одно из свойств spring.mail.username, jmix.email.oauth2.client-id или jmix.email.oauth2.secret. Раньше эти ошибки проявлялись только при отправке письма.

Дополнение Maps

Удалением объектов карты теперь управляет атрибут featureDeleteEnabled векторного источника, отдельно от режима изменения (см. #5576):

  • Источник с featureModifyEnabled="true" больше не показывает действие Delete по умолчанию. Задайте featureDeleteEnabled="true" для источника или установите свойство приложения jmix.maps.ui.feature-modify-includes-delete в true, чтобы сохранить прежнее поведение для всех источников, в которых не задан featureDeleteEnabled.

  • SourceFeatureDeleteEvent и addSourceFeatureDeleteListener() перенесены из HasFeatureModify в HasFeatureDelete. Замените в своём коде HasFeatureModify.SourceFeatureDeleteEvent на HasFeatureDelete.SourceFeatureDeleteEvent.

Дополнения OIDC и SAML

Следующие элементы автоконфигурации переименованы (см. #5484):

  • OidcAutoConfiguration.DefaulOidcVaadinWebSecurity → DefaultOidcVaadinWebSecurity

  • SamlAutoConfiguration.DefaulSamlVaadinWebSecurity → DefaultSamlVaadinWebSecurity

  • Бин claimsRoleMapper в SamlAutoConfiguration → samlAssertionRolesMapper

Обновите код, который наследует эти классы или обращается к бину по имени.

Дополнение Reports

Из дополнения Reports удалён неиспользуемый код, пришедший из библиотеки YARG (см. #2774):

  • Класс io.jmix.reports.yarg.loaders.impl.GroovyDataLoader. Для типа загрузчика groovy дополнение использует JmixGroovyDataLoader. В отличие от удалённого класса, он проверяет свойства jmix.core.unsafe-runtime-features-enabled и jmix.reports.groovy-enabled.

  • Классы InitializationException и ReportingXmlException из пакета io.jmix.reports.yarg.exception.

  • Методы update() и batch() класса io.jmix.reports.yarg.util.db.QueryRunner. Вместо них используйте Spring JdbcTemplate или JDBC.

  • Методы commitAndClose(), commitAndCloseQuietly(), rollback(), rollbackAndClose(), rollbackAndCloseQuietly(), loadDriver(), printStackTrace() и printWarnings() класса io.jmix.reports.yarg.util.db.DbUtils.

  • Метод UnoConverter.createAny(Object). Вместо него используйте new Any(new Type(XCell.class), value).

  • Поля AbstractFormatter.SIMPLE_ALIAS_REGEXP и DataExtractorImpl.EMPTY_MAP.

Дополнение больше не меняет XML-процессоры всего приложения (см. #5755). Раньше Xalan 2.7.3 и Xerces 2.12.2 из зависимостей дополнения становились JAXP TransformerFactory и SchemaFactory по умолчанию, и из-за этого приложение с Hazelcast не запускалось. Теперь приложение получает эти фабрики из JDK. Это может привести к следующим проблемам:

  • Код, который использует специфичные для Xalan возможности XSLT, может перестать работать.

  • Обработка XML, который превышает ограничения обработки JDK, может завершаться ошибкой.

  • В Java 21 валидация StAXSource с пустой секцией CDATA завершается ошибкой из-за бага JDK-8349516. Например, BPM не может развернуть такой DMN-файл (см. #5778).

Чтобы сохранить прежние фабрики, задайте системные свойства до запуска приложения, например в методе main() перед SpringApplication.run(). В этом случае приложение с Hazelcast, как и раньше, не запустится.

System.setProperty("javax.xml.transform.TransformerFactory",
        "org.apache.xalan.processor.TransformerFactoryImpl");
System.setProperty("javax.xml.validation.SchemaFactory:" + XMLConstants.W3C_XML_SCHEMA_NS_URI,
        "org.apache.xerces.jaxp.validation.XMLSchemaFactory");

Дополнение Dynamic Model

Дополнение Dynamic Model теперь читает YAML-определение модели строго (см. #5731). Правила описаны в разделе Написание YAML вручную.

Это касается только моделей, которые были написаны или отредактированы вручную в представлении Code. Модели, сохранённые из визуального редактора, всегда содержат значения в кавычках и читаются как раньше.

Состояние загруженности ссылок на сущности

Экземпляр сущности, возвращаемый методом DataManager.getReference(), теперь сообщает как загруженные только свой идентификатор и атрибуты, которые были установлены в нём позже (см. #5660). Раньше EntityStates.isLoaded() возвращал true для любого атрибута такого экземпляра.

Теперь для ссылки:

  • isLoaded() возвращает false для всех атрибутов, кроме идентификатора и атрибутов, установленных после создания.

  • isLoadedSafe() возвращает YES или NO и не вызывает геттеры.

  • isLoadedWithFetchPlan() возвращает false для фетч-плана, который содержит другие атрибуты.

  • getCurrentFetchPlan() возвращает фетч-план с идентификатором и атрибутами, установленными после создания.

Это приводит к следующим видимым последствиям:

  • Ссылка сериализуется (в REST, в переменных процессов BPM и т.д.) только со своим идентификатором и установленными в ней атрибутами.

  • Ссылка не получает значения по умолчанию, которые присваиваются при создании в методах @PostConstruct или в инициализаторах полей.

  • DataContext.merge() ссылки в контекст, в котором уже есть загруженный экземпляр, не заменяет загруженные значения на null.

Если ваш код рассчитывал на то, что ссылка сообщает все атрибуты как загруженные, обновите его.

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

  • Решенные проблемы в Jmix Framework:

  • Решенные проблемы в Jmix Studio: