What’s New

This section covers new features of Jmix framework and Studio 2.2, as well as some breaking changes to be aware of when upgrading from a previous version of the framework.

How To Upgrade

To create new projects with Jmix 2.2 or to upgrade an existing project, you need Studio 2.2 or later, so update your Jmix Studio plugin first.

The minimal supported IntelliJ IDEA version is now 2023.2.

See Upgrading Project section for how to upgrade your project using Studio. The automatic migration procedure makes the following changes in your project:

  • Updates the version of Jmix BOM which in turn defines versions of all dependencies.

  • Updates the version of Jmix Gradle plugin.

  • Updates the version of Gradle wrapper to 8.6 in gradle/wrapper/gradle-wrapper.properties.

  • In Kotlin projects, updates the version of Kotlin plugin to 1.9.22.

  • Due to relocation of the pessimistic locking feature, replaces usages of io.jmix.core.pessimisticlocking package with io.jmix.pessimisticlock, replaces jmix.core.pessimistic-lock.* application properties with jmix.pslock.\* and adds dependencies to the new add-on to build.gradle.

  • Adds jmix.ui.view.prevent-browser-tab-closing = true application property. See below for more information.

  • Adds spring.main.allow-circular-references = true application property. See below for more information.

  • Adds jmix.core.skip-null-or-empty-conditions-by-default = true application property. See below for more information.

  • Adds jmix.appsettings.check-permissions-for-app-settings-entity = true application property if the project includes the Application Settings add-on. See below for more information.

See also the full list of breaking changes that can affect your project after the upgrade.

Updated Dependencies

The following major dependencies have been updated:

  • Spring Boot 3.2. See its release notes for more information.

  • Vaadin 24.3. See release notes for Vaadin 24.2 and 24.3 for more information. Studio removes the .pnpmfile.cjs file from your project when migrating it to Jmix 2.2 (because of vaadin/flow#17873). Make sure this file doesn’t exist after migration.

New Features and Improvements

Support for Java 21

You can now use Java 21 and all its language features to write your applications.

The framework is still built using Java 17, so both Java 17 and 21 are available for application projects.

Charts Add-on

The new Charts add-on integrates the Apache ECharts JavaScript library into Jmix UI.

See the Charts documentation and issue #2437 for more information.

Maps Improvements

BPM Improvements

  • The BPM form wizard in Studio now supports creating forms for Start events and using entity instances instead of process variables. See the Form template and Form type dropdowns on the first step of the wizard.

  • The Async flag can be set for service tasks in the Studio BPMN designer.

  • Business process definitions developed in Studio can now be deployed to the running application. Use the Hot Deploy Process button in the top panel of the BPMN designer in Studio.

  • The runtime BPM modeler now provides support for the flowable:failedJobRetryTimeCycle element. See the Failed job retry time cycle property when a service task is selected.

  • The runtime BPM modeler now allows you to select existing process variables for bean arguments from dropdowns.

RichTextEditor Component

The new richTextEditor component integrates the Quill JavaScript library into Jmix UI. It’s available in the standard Add component palette.

Horizontal Main Menu

The new horizontalMenu component allows you to create a main view with a horizontal menu.

The new Main view with top menu template is available in the view creation wizard. If you want to use the new view instead of the existing main view, select the Use as default main view checkbox on the first step of the wizard. Then Studio will replace the layout attribute of the @Route annotation in all views and set the new view in the jmix.ui.main-view-id application property.

See #2492 for more information.

Filtering Main Menu

The new menuFilterField component allows users to filter main menu items. It’s available in the standard Add component palette.

The menu attribute should point to a listMenu component to be filtered:

<menuFilterField menu="menu"
                 placeholder="Search..." classNames="ms-s me-s"/>
<nav id="navigation" classNames="jmix-main-view-navigation" ariaLabel="msg://navigation.ariaLabel">
    <listMenu id="menu"/>
</nav>

Please note that filtering of Horizontal Main Menu is not supported.

Initial Layout in Main View

Now you can declaratively define an initial layout that will be shown when no views are opened in the main view. Use the initialLayout element of the appLayout component:

<appLayout>
    <navigationBar .../>
    <drawerLayout .../>
    <initialLayout>
        <h2 text="Hello world!"/>
    </initialLayout>
</appLayout>

See #2213 for more information.

Data Grid Improvements

Handling Double-Click

The dataGrid component now handles double-click in list views: it either opens a detail view or, in lookup mode, finishes the selection. See #2582 for more information.

URL Parameters for Filter in Column Headers

The dataGrid column header filter can now be reflected in URL to provide a deep link and to save the view state when navigating to a detail view and back.

Use the dataGridFilter element of the urlQueryParameters facet, pointing to the data grid:

<facets>
    <urlQueryParameters>
        <dataGridFilter component="usersDataGrid"/>
    </urlQueryParameters>
</facets>
<layout>
    <dataGrid id="usersDataGrid" dataContainer="usersDc">
        <columns>
            <column property="username" filterable="true" resizable="false" autoWidth="true"/>

Column Visibility Control

The new gridColumnVisibility component allows users to hide and show columns of a data grid. It consists of a button and a dropdown menu with the list of columns.

Usage example:

<hbox id="buttonsPanel" classNames="buttons-panel">
    <!-- ... -->
    <gridColumnVisibility icon="COG" themeNames="icon"
                          dataGrid="usersDataGrid" exclude="picture"/>
</hbox>
<dataGrid id="usersDataGrid" dataContainer="usersDc">
    <columns resizable="true">
        <column key="picture" sortable="false" flexGrow="0" resizable="false"/>
        <column property="username"/>
        <column property="firstName"/>

Collection Properties in Generic Filter

The genericFilter component now allows you to create conditions for collection (to-many) properties.

For example, in the Onboarding application, you can filter Users by the steps property and its nested properties: steps.dueDate, steps.step.name, etc. The JPA data store will automatically create an appropriate JPQL query with the join clause. Previously, you could achieve this only by defining a JPQL condition manually.

See #518 for more information.

Sending Events to All User Sessions

The UiEventPublisher bean now has the publishEventForUsers() method that accepts an application event instance and a collection of usernames. This method allows you to send events to user session of particular users, regardless of whether they are connected to the same server or a different server within the cluster.

Example of sending an event to alice:

public class DepartmentListView extends StandardListView<Department> {
    @Autowired
    private UiEventPublisher uiEventPublisher;

    @Subscribe(id = "sendEventBtn", subject = "clickListener")
    public void onSendEventBtnClick(final ClickEvent<JmixButton> event) {
        uiEventPublisher.publishEventForUsers(new MyUiEvent(this), List.of("alice"));
    }

    public static class MyUiEvent extends ApplicationEvent {

        public MyUiEvent(Object source) {
            super(source);
        }
    }
}

Example of an event listener:

public class MainView extends StandardMainView {
    @Autowired
    private Notifications notifications;

    @EventListener
    public void onMyUiEvent(DepartmentListView.MyUiEvent event) {
        notifications.show("Event received");
    }
}

If the second argument of the publishEventForUsers() method is null, the event is sent to all connected users.

See #1235 for more information.

Improved Save Performance

Now edited entity is not reloaded by default after save-and-close action if the detail view was opened by navigation, because in this case the list view reloads the whole list anyway. It improves the performance for complex views that load and save large object graphs.

You can control the reloading of saved instances explicitly using the setReloadSaved() method of the DetailView interface, for example:

@Subscribe
public void onInit(final InitEvent event) {
    setReloadSaved(true);
}

See potentially breaking changes and #1725 for more information.

Reduced Build Time

The build process now skips enhancing entities if they have not been modified since the last build, which significantly reduces build time for projects with a large data model.

For example, if you have built your project, then you modify a view controller and build again, you should see the following message in the console: Entities enhancing was skipped, because entity classes haven’t been changed since the last build.

To disable this behavior and enhance all entities on each compilation, add the following configuration to build.gradle:

jmix {
    entitiesEnhancing {
        skipUnmodifiedEntitiesEnhancing = false
    }
}

Studio Improvements

Since Jmix Studio 2.2, the premium RAD features are available without an active subscription in small projects with up to 10 entities and roles.

Code Snippets

New code snippets are available in Studio for BPM, Reports, Notifications and Email sending functionality if the corresponding add-ons are included in your project.

Adding Components Using Wizards

The Add Component action of View Designer now has two tabs:

  • From Palette tab shows the component palette as before;

  • Using Wizard tab contains wizards that help solving complex tasks related to UI. For example, the Edit entity attributes wizard creates a formLayout with fields for selected entity attributes and a data container with a proper fetch plan.

    The list of wizards is sensitive to the current view contents: for example, if the view already contains a dataGrid, the Add column to DataGrid wizard is available.

The inspector panel of the Jmix UI tool window now shows a link to the documentation on the selected UI component. See the question mark icon next to the component type.

The same link is available as the Jmix Documentation item in the component hierarchy context menu.

Test Scaffolding

Studio now shows the Tests item in the Jmix tool window. Double-click on this item opens the Project tree in the src/test/java folder.

The New → Advanced → Integration Test and New → Advanced → UI Integration Test actions allow you to quickly create test classes for testing business logic and views.

Breaking Changes

Browser Tab Closing Prevention

The feature that prevents accidental browser tab closing introduced in Release 2.0 is now off by default. You can turn it on for a particular view using the setPreventBrowserTabClosing(true) method or globally for the application using the following application property:

jmix.ui.view.prevent-browser-tab-closing = true

Circular Dependencies Between Spring Beans

Previously, circular dependencies between Spring beans were enabled by Jmix on the framework level.

Jmix 2.2 doesn’t have circular dependencies itself anymore and doesn’t enable them in application projects by default.

There is a chance that your project contains circular dependencies between its Spring beans, therefore the Studio migration procedure automatically adds the following property to the project:

spring.main.allow-circular-references = true

We recommend you to remove this property and try to start the application. If it fails to initialize, either refactor your beans to eliminate circular dependencies, or return the property.

See #287 for more information.

Handling Empty Conditions

Previously, a property condition evaluated to true if its parameter value was empty (null, empty string or empty collection).

Since Jmix 2.2, the null or empty parameters do not lead to skipping conditions. For example, consider the following code:

dataManager.load(User.class)
    .condition(PropertyCondition.contains("email", null))
    .list();

In Jmix 2.1 and earlier, it executes the following SQL:

SELECT ID, ACTIVE, EMAIL, <...> FROM USER_

In Jmix 2.2, by default it executes the following SQL and passes null as a parameter:

SELECT ID, ACTIVE, EMAIL, <...> FROM USER_ WHERE EMAIL LIKE ?

As a result, in Jmix 2.1 the whole list of users is returned, while in Jmix 2.2 the result list is empty.

If you want to return the previous behavior, set the following application property:

jmix.core.skip-null-or-empty-conditions-by-default = true

The Studio migration procedure automatically adds this property to your project.

Alternatively, you can skip empty parameters for particular conditions:

dataManager.load(User.class)
    .condition(PropertyCondition.contains("email", null).skipNullOrEmpty())
    .list();

See #2490 for more information.

NoResultException

io.jmix.core.NoResultException is now thrown instead of java.lang.IllegalStateException if the one() method of DataManager’s fluent loading API returns no results. See #2682.

Pessimistic Locking

The pessimistic locking feature has been extracted to the add-on.

The io.jmix.core.pessimisticlocking package has been renamed to io.jmix.pessimisticlocking.

Changed the following properties:

  • jmix.core.pessimistic-lock.use-default-quartz-configurationjmix.pslock.use-default-quartz-configuration

  • jmix.core.pessimistic-lock.expiration-cronjmix.pslock.expiration-cron

The Studio migration procedure automatically adds dependencies to your build.gradle and changes the imports and property names.

See #1958 for more information.

Validation in File Upload Fields

The isInvalid() method of fileUploadField and fileStorageUploadField does not trigger validation anymore. It only checks invalid state of the field. See #2821.

Action Shortcuts

Keyboard shortcuts of actions assigned to components like button or dataGrid are now handled differently. See #1758 for more information.

Security in Application Settings

The Application Settings add-on now doesn’t require to add security permissions to AppSettingsEntity entity to work with settings through the AppSettings bean.

If you want to return the previous behavior, set the following application property:

jmix.appsettings.check-permissions-for-app-settings-entity = true

The Studio migration procedure automatically adds this property to your project.

See #2710 for more information.

Security Views

The layout of the standard security views for resource and row-level roles has been changed for better usability, see #2519.

If you have extended these views, you may have to modify your code.

DetailView and DataContext Interfaces

The following methods have been added as part of the detail view save performance improvement:

  • DataContext.save(boolean reloadSaved)

  • DetailView.isReloadSaved()

  • DetailView.setReloadSaved(boolean reloadSaved)

You may need to modify your code if you directly implemented these interfaces.

Also, DataContext.PostSaveEvent.getSavedInstances() method returns an empty collection if the entities were not reloaded. It can be checked using the new DataContext.PostSaveEvent.isEntitiesReloaded() method.

DTO Entities in Standard Views

The framework now doesn’t make any difference between JPA and DTO entities when navigating to a detail view: it passes the entity ID in the route parameter. The detail view for DTO entity is supposed to get this ID and load the entity instance from some data storage using the load delegate. If the "new" constant is passed instead of ID, the view creates a new instance.

If the whole entity instance is passed instead of ID (e.g. when opening in a dialog window), EntityStates.isNew() is used to distinguish between Edit and Create mode. Consequently, it’s important to set the entity in the not-new state after loading it from a storage. For a DTO entity it can be done using the new EntityStates.setNew() method, for JPA entity it’s done by the standard JPA data store implementation.

If the edited entity should not be reloaded from the data storage before setting to the data container, call setReloadEdited(false) in the detail view constructor or the InitEvent handler. This is the case for DTO entities existing purely in memory and not mapped directly to external data.

See #2788 for more information and recommendations and the External Data Sample project for example code.

Maps API

The following changes have been made in the Maps add-on:

  • io.jmix.mapsflowui.kit.component.model.style.text.Padding has been moved to io.jmix.mapsflowui.kit.component.model package. See #2822.

  • The addStyles() method of the Feature, PointFeature, MarkerFeature, LineStringFeature, PolygonFeature classes now returns void. Use withStyles() instead if you need the current instance of the feature. See #2807.

  • In the VectorLayer class, the addStyles() method now returns void. Use withStyles() instead if you need the current instance of the layer. Renamed methods: isDeclutter()getDeclutter(), isUpdateWhileAnimating()getUpdateWhileAnimating(). See #2790.

  • In the ClusterSource class, the addPointStyles() method now returns void. Use withPointStyles() instead if you need the current instance of the source. See #2790.

  • In the Layer class, the isVisible() method is renamed to getVisible(). See #2790.

  • The type of zoom properties in VectorLayer, TileLayer, ImageLayer and GeoMapView has been changed from Integer to Double. See #2701.

Changelog