3. References and Unique Attributes

The next feature of your Onboarding application is the management of Departments.

New hire belongs to a Department. Each department has a unique name and a reference to HR Manager.

In this chapter, you will create:

  • The Department entity with the link to User.

  • A database table with a foreign key and a unique constraint.

  • CRUD views including UI component for selecting the related entity.

Creating Department Entity

The Department entity has the name (unique) attribute and the hrManager attribute which is a reference to the User entity:

references diagram

If your application is running, stop it using the Stop button (suspend) in the main toolbar.

In the Jmix tool window, click New (add) → JPA Entity as you did in the previous chapter. In the New JPA Entity dialog, enter Department into the Class field and select the TraitsVersioned checkbox:

create entity 1

Click OK.

Studio will create the entity class and open the entity designer:

create entity 2

Creating Unique Attribute

Let’s add the name attribute to the entity.

Click Add (add) in the Attributes toolbar. In the New Attribute dialog, enter name into the Name field and select the Mandatory checkbox:

create entity 3

Now let’s define a unique constraint for the NAME column in the database. It will ensure there will be no departments with the same name.

Switch to the Indexes tab at the bottom and click New Index (add) in the Database Indexes toolbar. Studio will add a row to the indexes list:

create entity 4

Select NAME in the Available attributes list and click arrow right in the toolbar to move it to the Selected attributes.

Select the Unique and Constraint checkboxes in the index row and change the name to IDX_DEPARTMENT_UNQ_NAME:

create entity 5

If you switch to the Text tab of the entity designer, you will see the unique constraint added to the @Table annotation:

@JmixEntity
@Table(name = "DEPARTMENT", uniqueConstraints = {
        @UniqueConstraint(name = "IDX_DEPARTMENT_UNQ_NAME", columnNames = {"NAME"})
})
@Entity
public class Department {
    // ...

Using this annotation, Studio will add a unique constraint for the database table to the Liquibase changelog.

Creating Reference Attribute

Now create a reference to an HR Manager of the department.

Click Add (add) in the Attributes toolbar. In the New Attribute dialog, enter hrManager into the Name field. Then select:

  • Attribute type: ASSOCIATION

  • Type: User

  • Cardinality: Many to One

create entity 6

Click OK.

You can find the new attribute on the Text tab:

@JoinColumn(name = "HR_MANAGER_ID")
@ManyToOne(fetch = FetchType.LAZY)
private User hrManager;

Also, the @Table annotation on the class header now defines the index for the foreign key column:

@JmixEntity
@Table(name = "DEPARTMENT", indexes = {
        @Index(name = "IDX_DEPARTMENT_HR_MANAGER", columnList = "HR_MANAGER_ID")
    },
    // ...

You can see it on the Indexes tab too.

Creating CRUD Views

Let’s generate CRUD views for the Department entity.

Click ViewsCreate view in the actions panel at the top of the entity designer:

create screens 1

On the first step of the view creation wizard, select the Entity list and detail views template:

screen wizard 1

Click Next.

Accept the suggested values on the first two steps of the wizard.

On the Entity list view fetch plan step, add the hrManager attribute to the selection:

create screens 2

Now you can be sure that the referenced User entity will be loaded together with the root Department entity and displayed in the list view.

If an attribute is not present in the fetch plan, Studio doesn’t create a visual component for it in the generated views.

Click Next.

On the next Entity detail view fetch plan step, this attribute will be selected automatically:

create screens 3

Click Next.

Accept the values on the Localizable messages step and click Create.

Studio will generate two views: Department.list and Department.detail and open their source code. Close all editor tabs for now - you will make some changes in the generated views later in this chapter.

Running the Application

Click the Debug button (start debugger) in the main toolbar.

Before running the application, Studio will generate a Liquibase changelog:

run app 1

As you can see, the changelog contains commands for creating the DEPARTMENT table, a unique constraint for the NAME column and the foreign key and the index for the HR_MANAGER_ID column.

Click Save and run.

Studio will execute the changelog, then build and run the application.

Open http://localhost:8080 in your web browser and log in to the application with admin / admin credentials.

Click on the Departments item in the Application menu. You will see the Department.list view:

run app 2

Click Create. The Department.detail view will open:

run app 3

You can select an HR Manager for the department by clicking the ellipsis button in the picker field. The users list view will open in a dialog window. Select a row in the users table and scroll down to reveal the Select button:

run app 4

Select a user and click Select. The user will be displayed in the picker field:

run app 5

Click OK. The referenced user will be displayed in the table too:

run app 6

Observing the Instance Name

You may wonder why the picker field and the table show the [admin] string for the selected user?

Jmix has a concept of instance name: a human-readable text that represents an entity instance. It can be defined for any entity using the @InstanceName annotation on a field or a method.

The User entity generated by the project template has the following method defining the instance name:

public class User implements JmixUserDetails, HasTimeZone {
    // ...

    @InstanceName
    @DependsOnProperties({"firstName", "lastName", "username"})
    public String getDisplayName() {
        return String.format("%s %s [%s]", (firstName != null ? firstName : ""),
                (lastName != null ? lastName : ""), username).trim();
    }

So when the firstName and lastName are empty, the User instance name is username in square brackets, which is what you see in the application at the moment.

The Studio entity designer automatically generates the @InstanceName annotation if it encounters an appropriately named attribute: name, description, etc. For example, your Department entity has @InstanceName on its name attribute:

public class Department {
    // ...

    @InstanceName
    @Column(name = "NAME", nullable = false)
    @NotNull
    private String name;

So the department’s name will be displayed in UI if you use a department as a reference in another entity. You will see it later in the tutorial.

The entity designer also helps you to define the instance name manually. You can select an attribute for it or generate a method using the Instance name field and the button next to it:

instance name 1

Simple UI Customizations

Let’s make some customizations in the application UI to become more familiar with Jmix features.

Changing Attribute Caption

Perhaps you have noticed that the generated caption for the hrManager attribute is not quite correct: it reads Hr manager. Let’s change it to HR Manager.

Select the hrManager attribute in the entity designer and click the globe (globe) button next to the attribute name:

change caption 1

The Localized Message dialog will appear:

change caption 2

Change the text and click OK.

You can view and edit all messages of your project if you double-click the User InterfaceMessage Bundle node in the Jmix tool window. The message that you have just changed is highlighted below:

change caption 3

Switch to the application running in your web browser. Refresh the web page. You will see the new caption for the hrManager attribute.

Thanks to the Studio hot deploy feature, you don’t have to restart the application when making changes in the UI.

Just save the changes in the IDE (press Ctrl/Cmd+S), wait a couple of seconds and refresh the web page.

Sorting in DataGrid

By default, users can sort data grid by a single column. Let’s enable sorting by multiple columns.

Find department-list-view.xml in the Jmix tool window and double-click it. The view designer appears:

customize ui 1

Studio allows you to preview the layout right inside the IDE. Click the Start Preview button:

customize ui 2

Studio will build the frontend and after a few seconds you will see the preview panel next to the source code. Depending on your display resolution, you may want to show only the XML editor or the preview at a time. Use the buttons on top of the editor panel to switch the mode:

customize ui 3

Locate departmentsDataGrid in the Jmix UI structure panel. The component will be selected in the preview, in the XML editor and in the Jmix UI inspector panel in the lower right corner:

customize ui 4

Select the checkbox for the multiSort property:

customize ui 5

Studio will add the multiSort="true" attribute for the dataGrid XML element.

It works in the opposite direction too. You can edit the XML directly and view the results in the designer panels and Preview.

Switch to the running application and refresh the page with the Departments list view. Test the sorting by click on the Name column, then on the HR Manager column.

Changing Unique Constraint Violation Message

If you try to create another department with the same name, you will see the error message about the unique constraint violation:

customize ui 8

The default message is not very user-friendly, but you can easily change it.

Double-click the User InterfaceMessage Bundle node in the Jmix tool window and add the following line:

databaseUniqueConstraintViolation.IDX_DEPARTMENT_UNQ_NAME=A department with the same name already exists

The message key should start with databaseUniqueConstraintViolation. and end with the name of the database unique constraint. You may notice that the file already contains a similar message for the unique constraint on the username attribute of the User entity.

Switch to the application and test your changes. Now the error shows your message:

customize ui 9

Summary

In this section, you have built the second feature: management of departments.

You have learned that:

  • Studio helps to create reference attributes and generates Liquibase changelogs with a foreign key and an index.

  • To show a reference attribute in an entity list or detail view, it should be included in the fetch plan of the view.

  • The instance name is used to show a reference in UI.

  • The entityPicker component is used by default to select a reference in a generated detail view.

  • The uniqueness of entity attributes is maintained on the database level by defining unique constraints.

  • A unique constraint violation message can be easily customized.

  • Captions and messages generated by Studio are stored in the message bundle of the application.

  • Studio hot deploys changes in views and messages to the running application, which saves from restarting the application when developing UI. Changes in entities are not hot deployed.