Сущности

В Jmix существуют следующие типы сущностей:

  • Сущности JPA – это объекты Java, хранящиеся в базе данных с использованием Java Persistence API.

  • Сущности DTO – это простые объекты Java, которые не привязаны к какой-либо конкретной технологии персистентности.

  • Сущность Key-Value (ключ-значение) – это динамическая сущность с произвольным количеством атрибутов.

Сущности характеризуются своими атрибутами.

Сущности JPA и DTO сущности определяются классами Java и имеют некоторые специфичные для Jmix аннотации.

Для создания сущностей JPA и DTO используйте дизайнер сущностей Studio.

Сущности JPA

JPA сущность – это класс Java, аннотированный в соответствии с правилами JPA. Сущности JPA хранятся в реляционной базе данных, подключенной в качестве основного или дополнительного хранилища данных.

Аннотации JPA определяют отображение между полями таблицы базы данных и атрибутами сущности. Jmix налагает следующие ограничения на аннотации JPA:

  • Аннотации атрибутов должны размещаться только в полях (AccessType.FIELD).

  • Не поддерживаются: @IdClass, @ElementCollection.

Ниже приведен пример типичного класса сущности JPA:

Customer.java
@JmixEntity (1)
@Table(name = "CUSTOMER") (2)
@Entity (3)
public class Customer {

    @JmixGeneratedValue (4)
    @Id (5)
    @Column(name = "ID", nullable = false) (6)
    private UUID id;

    @Version (7)
    @Column(name = "VERSION")
    private Integer version;

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

    @Email (9)
    @Column(name = "EMAIL", unique = true) (10)
    private String email;

    public UUID getId() {
        return id;
    }

    public void setId(UUID id) {
        this.id = id;
    }

    // other getters and setters
1 Обязательная аннотация @JmixEntity.
2 Аннотация @jakarta.persistence.Table указывает имя таблицы базы данных.
3 Аннотация @jakarta.persistence.Entity указывает, что класс – сущность JPA.
4 Аннотация @JmixGeneratedValue указывает, что Jmix должен сгенерировать и назначить первичный ключ при создании экземпляра сущности в памяти.
5 Аннотация @jakarta.persistence.Id указывает первичный ключ.
6 Аннотация @jakarta.persistence.Column определяет сопоставление со столбцом таблицы. Параметр nullable = false указывает, что механизм миграции базы данных должен создать столбец с ограничением NOT NULL.
7 @jakarta.persistence.Version указывает, что сущность должна быть оптимистически заблокирована, используя значение этого атрибута. Атрибут должен иметь тип Integer. Studio создает такой атрибут автоматически, если вы выберите для сущности черту Versioned.
8 Аннотация @InstanceName указывает здесь на один атрибут, выбранный в качестве имени экземпляра.
9 Аннотации @NotNull и @Email из пакета jakarta.validation.constraints являются примерами использования в сущностях аннотаций Bean Validation.
10 Параметр unique = true в аннотации @Column указывает, что механизм миграции базы данных должен добавить в столбец ограничение уникальности.

Как имя таблицы, так и имя сущности могут иметь префикс для устранения конфликтов с именами сущностей из других модулей. Studio ставит этот префикс, если проект имеет свойство jmix.projectId в build.gradle.

Черты

Черта (Trait) – это набор атрибутов, который придает сущности определенное поведение на системном уровне. Эти атрибуты обрабатываются фреймворком и не предназначены для редактирования пользователями или кодом вашего приложения.

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

Черта Has UUID

Черта Has UUID предоставляет глобально уникальный идентификатор, автоматически назначаемый при создании экземпляра в памяти. Эта черта реализуется атрибутом с типом UUID и аннотацией @JmixGeneratedValue.

При создании сущности в Studio черта Has UUID применяется автоматически, если вы выбрали UUID в Id type. Добавьте черту Has UUID, если вы выбрали другой тип и Id value, отличное от значения Generated by Jmix.

Эта черта не требует дополнительного атрибута, если вы выбираете тип UUID для идентификатора сущности.

Добавление черты Has UUID настоятельно рекомендуется, если вы используете атрибут идентификатора, значение которого не присваивается непосредственно при создании экземпляра сущности в памяти (не имеет аннотации @JmixGeneratedValue). Это относится к идентификаторам Long и Integer, отображенным на колонку с типом identity (то есть присваиваевым базой данных), а также к идентификаторам любого типа, значение которых вводится пользователями. У таких сущностей метод hashCode() всегда возвращает постоянное значение, что влияет на производительность коллекций на основе хэш-таблиц.

Черта Versioned

Черта Versioned обеспечивает оптимистическую блокировку на уровне JPA. Она реализуется целочисленным атрибутом с аннотацией @Version.

Никогда не изменяйте значение атрибута @Version в коде приложения. Это приведет к невозможности обновления экземпляра в базе данных.

Черты Audit

Черты Audit of creation и Audit of modification обеспечивают отслеживание того, кто и когда создал и изменил экземпляр сущности. Они реализуются атрибутами соответствующих типов с аннотациями @CreatedBy, @CreatedDate, @LastModifiedBy и @LastModifiedDate из проекта Spring Data.

Например:

@CreatedBy
@Column(name = "CREATED_BY")
private String createdBy;

@CreatedDate
@Temporal(TemporalType.DATE)
@Column(name = "CREATED_DATE")
private Date createdDate;

@LastModifiedBy
@Column(name = "LAST_MODIFIED_BY")
private String lastModifiedBy;

@LastModifiedDate
@Temporal(TemporalType.DATE)
@Column(name = "LAST_MODIFIED_DATE")
private Date lastModifiedDate;

Атрибуты Audit назначаются автоматически, когда фреймворк сохраняет экземпляры сущностей.

Черта Soft Delete

Черта Soft Delete обеспечивает мягкое удаление экземпляров сущностей. Она реализуется с помощью пары атрибутов с аннотациями @DeletedDate и @DeletedBy, например:

@DeletedBy
@Column(name = "DELETED_BY")
private String deletedBy;

@DeletedDate
@Temporal(TemporalType.DATE)
@Column(name = "DELETED_DATE")
private Date deletedDate;

Для более подробной информации перейдите к разделу Мягкое удаление.

Наследование сущностей

Jmix использует принципы наследования JPA. Дизайнер сущностей Studio автоматически генерирует необходимые аннотации в соответствии с выбранной стратегией наследования. Основные аннотации перечислены ниже.

@DiscriminatorColumn

Используется для определения колонки базы данных, отвечающей за различение типов сущностей в случаях стратегий наследования SINGLE_TABLE и JOINED.

Параметры:

  • name - имя колонки-дискриминатора.

  • discriminatorType - тип данных колонки-дискриминатора.

  • length - длина колонки для типов дискриминаторов на основе строки.

Пример:

@DiscriminatorColumn(name = "TYPE", discriminatorType = DiscriminatorType.INTEGER)

@DiscriminatorValue

Определяет значение колонки-дискриминатора для данной сущности.

Пример:

@DiscriminatorValue("0")

@Inheritance

Определяет стратегию наследования для иерархии классов сущностей. Данная аннотация должна быть помещена на корневом классе иерархии.

Параметры:

  • strategy - стратегия наследования, по умолчанию SINGLE_TABLE.

@MappedSuperclass

Определяет, что данный класс является предком некоторых сущностей, и его атрибуты должны быть использованы в составе сущностей-наследников. Такой класс не сопоставляется никакой отдельной таблице БД.

@PrimaryKeyJoinColumn

Используется в случае стратегии наследования JOINED для указания колонки внешнего ключа данной сущности, ссылающегося на первичный ключ сущности-предка.

Параметры:

  • name - имя колонки внешнего ключа данной сущности.

  • referencedColumnName - имя колонки первичного ключа сущности предка.

Пример:

@PrimaryKeyJoinColumn(name = "CARD_ID", referencedColumnName = "ID")

Сущности DTO

Модель данных вашего приложения может содержать сущности, которые существуют только в памяти или отражают некоторые внешние данные, используя механизмы, отличные от JPA. Мы называем их сущностями DTO, потому что они часто используются в качестве Объектов передачи данных (Data Transfer Objects) в параметрах и возвращаемых значениях в универсальном REST и при взаимодействии с внешними API.

Простой пример DTO-сущности:

OperationResult.java
@JmixEntity (1)
public class OperationResult {

    private String result; (2)

    private Integer errorCode; (2)

    private String errorMessage; (2)

    public String getResult() {
        return result;
    }

    public void setResult(String result) {
        this.result = result;
    }

    // other getters and setters
1 Обязательная аннотация @JmixEntity.
2 Все свойства объекта (поля с методами доступа) становятся атрибутами сущности.

Атрибуты сущностей могут иметь аннотации для указания каких-либо подробностей о них:

ProductPart.java
@JmixEntity (1)
public class ProductPart {

    @JmixProperty(mandatory = true) (2)
    @InstanceName (3)
    private String name;

    private Integer quantity; (4)

    // getters and setters
1 Аннотация @JmixEntity явно определяет имя сущности.
2 Аннотация @JmixProperty с параметром mandatory = true указывает, что атрибут является обязательным, т.е. должен содержать значение.
3 Аннотация @InstanceName указывает здесь на один атрибут, выбранный в качестве имени экземпляра.
4 Атрибут без аннотации.

Сущности DTO могут быть связаны с пользовательским хранилищем данных для общих операций CRUD с помощью DataManager и поддержки ссылок на сущности DTO из сущностей JPA.

В приведенном ниже примере показано, как исключить некоторые свойства объекта из атрибутов сущности (подробнее об этом в разделе Атрибуты сущности).

Metric.java
@Store(name = "inmem") (1)
@JmixEntity(annotatedPropertiesOnly = true) (2)
public class Metric {

    @JmixProperty(mandatory = true) (3)
    @JmixId (4)
    @JmixGeneratedValue (5)
    private UUID id;

    @JmixProperty (6)
    private String name;

    @JmixProperty (6)
    private Double value;

    private Object ephemeral; (7)

    public UUID getId() {
        return id;
    }

    public void setId(UUID id) {
        this.id = id;
    }

    // other getters and setters
1 Аннотация @Store указывает пользовательское хранилище данных.
2 Параметр annotatedPropertiesOnly = true аннотации @JmixEntity указывает, что свойства объекта, не помеченные с помощью @JmixProperty, не будут атрибутами сущности.
3 Аннотация @JmixProperty с параметром mandatory = true указывает, что атрибут является обязательным, т.е. должен содержать значение.
4 Аннотация @JmixId указывает, что атрибут является идентификатором сущности.
5 Аннотация @JmixGeneratedValue указывает, что Jmix должен сгенерировать и назначить идентификатор при создании экземпляра сущности в памяти.
6 Аннотация @JmixProperty здесь просто указывает, что свойство является атрибутом сущности.
7 Не аннотированное свойство не является атрибутом сущности из-за параметра annotatedPropertiesOnly = true аннотации @JmixEntity.

Сущность Key-Value

KeyValueEntity позволяет представлять произвольные наборы именованных значений в виде сущностей и, следовательно, работать с данными, которые не представлены непосредственно классами Java (сущностями JPA или DTO).

Рассмотрим пример: в вашей модели данных есть сущность Order, и вам нужно рассчитать сумму заказов, агрегированных клиентами, и отобразить эти данные в UI. Вы можете выполнить запрос JPQL и загрузить результат в виде списка экземпляров сущности KeyValueEntity используя DataManager:

List<KeyValueEntity> entities = dataManager.loadValues(
            "select e.customer, sum(e.amount) from Order_ e group by e.customer")
        .properties("customer", "total")
        .list();

Возвращаемые экземпляры KeyValueEntity будут иметь два атрибута, которые вы указали в методе properties(): customer со значением первого поля в списке результатов запроса и total со значением второго поля. Вы можете получить их следующим образом:

for (KeyValueEntity entity : entities) {
    Customer customer = entity.getValue("customer");
    BigDecimal totalAmount = entity.getValue("total");
    // ...
}

Jmix UI содержит специальные key-value контейнеры данных для привязки компонентов пользовательского интерфейса к экземплярам сущности KeyValueEntity.

Атрибуты сущности

Существует еще один термин для атрибутов сущности: свойства (properties) сущности. Он часто используется в кодовой базе Jmix, например, в аннотациях: @JmixProperty, @DependsOnProperties и т.д.

Каждый атрибут сущности должен иметь соответствующий тип. Jmix поддерживает следующие типы "из коробки":

  • java.lang.String

  • java.lang.Character

  • java.lang.Boolean

  • java.lang.Integer

  • java.lang.Long

  • java.lang.Double

  • java.math.BigDecimal

  • java.util.Date

  • java.time.LocalDate

  • java.time.LocalTime

  • java.time.LocalDateTime

  • java.time.OffsetTime

  • java.time.OffsetDateTime

  • java.sql.Date

  • java.sql.Time

  • java.util.UUID

  • java.net.URI

  • byte[] (массив байтов)

  • Перечисление

  • Сущность или набор сущностей (ссылочный атрибут)

Вы можете использовать тип, не указанный в приведенном выше списке, если создадите соответствующую реализацию Datatype и убедитесь, что ваш тип поддерживается базовым хранилищем данных.

Обратите внимание, что примитивные типы Java (int, boolean, и т.д.) не могут использоваться для атрибутов сущностей.

Для сущностей JPA и DTO существует два типа атрибутов:

  • Атрибут на основе поля соответствует полю и паре методов доступа accessor (геттер / сеттер) поля. Имя поля становится именем атрибута.

    Сеттер может быть опущен, тогда атрибут будет доступен только для чтения.

    Пример атрибута на основе поля:

    User.java
    @Column(name = "FIRST_NAME")
    protected String firstName;
    
    public String getFirstName() {
        return firstName;
    }
    
    public void setFirstName(final String firstName) {
        this.firstName = firstName;
    }
  • Атрибут на основе метода соответствует методу без параметров, возвращающему поддерживаемый тип и с именем, начинающимся с get, например getCustomer(). Имя метода без get с первой буквой в нижнем регистре становится именем атрибута: getFullName()fullName.

    Пример атрибута на основе метода:

    User.java
    @JmixProperty
    @DependsOnProperties({"firstName", "lastName"})
    public String getFullName() {
        return this.firstName + " " + this.lastName;
    }

Класс сущностей может иметь свойства (поле + геттер/сеттер) и методы, которые не являются атрибутами сущности, то есть не включены в метаданные. Таким образом, вы можете использовать такие свойства и методы в коде вашего приложения, но фреймворк не распознает их и не будет отображать в UI или передавать через REST API.

Становится ли свойство или квалифицирующий метод атрибутом сущности, зависит от следующих правил:

  • Если параметр annotatedPropertiesOnly аннотации @JmixEntity имеет значение false (по умолчанию), следующие свойства объекта становятся атрибутами сущности:

    • Для сущностей JPA: все свойства, кроме аннотированных @jakarta.persistence.Transient.

    • Для сущностей DTO: все свойства.

    • В обоих случаях: все свойства и методы с аннотацией @JmixProperty.

  • Если параметр annotatedPropertiesOnly имеет значение true, только свойства и методы с аннотацией @JmixProperty становятся атрибутами сущности.

Ссылки

Ссылочные атрибуты определяют отношения между сущностями. Ссылки могут быть одиночными значениями (отношения "to-one") или коллекциями (отношения "to-many").

По умолчанию связь является ассоциацией, что означает, что обе сущности могут существовать независимо друг от друга, без владения. Например, в связи Customer - Order у Order (заказа) есть атрибут, который является ссылкой на Customer (клиента). Пользователи самостоятельно создают клиентов и заказы, выбирают клиента для заказа и при необходимости меняют ссылку на другого клиента.

Jmix также поддерживает более сильную связь между сущностями – композицию. Композиция подразумевает владение, т.е. экземпляр сущности может существовать только как часть сущности-владельца. Например, в связи Order - OrderLine (строка заказа) у Order есть атрибут, который представляет собой коллекцию экземпляров OrderLine. Каждый экземпляр OrderLine создается для определенного Order, становится его частью и не может принадлежать другому Order.

Объекты, принадлежащие композиции, редактируются в UI вместе. Например, пользователь открывает экран редактирования Order и может создавать и редактировать OrderLines в их отдельных экранах редактирования, но все изменения как для Order, так и для всех его OrderLines сохраняются в базе данных вместе в одной транзакции и только после того, как пользователь подтвердит сохранение объекта-владельца – Order.

Отношение-композиция определяется аннотацией @Composition к ссылочному атрибуту.

Дизайнер сущностей Studio позволяет выбрать тип отношения в поле Attribute type.

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

DataManager автоматически поддерживает TO-ONE ссылки между сущностями из разных хранилищ, если они объявлены нужным образом.

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

Рассмотрим пример: у вас есть сущность Customer в основном хранилище данных и сущность Address в дополнительном хранилище данных, и вы хотите получить ссылку от Customer на Address. Тогда сущность Customer должна содержать следующие два атрибута:

@SystemLevel
@Column(name = "ADDRESS_ID")
private UUID addressId; (1)

@Transient
@JmixProperty
@DependsOnProperties("addressId")
private Address address; (2)

public UUID getAddressId() {
    return addressId;
}

public void setAddressId(UUID addressId) {
    this.addressId = addressId;
}

public Address getAddress() {
    return address;
}

public void setAddress(Address address) {
    this.address = address;
}
1 Атрибут addressId хранит идентификатор сущности Address. Этот атрибут аннотирован как @SystemLevel, чтобы указать фреймворку, что атрибут не должен отображаться пользователям.
2 Атрибут address содержит ссылку на сущность Address. Этот атрибут является неперсистентным (т.е. не хранится в базе данных) и аннотирован как @DependsOnProperties, чтобы указать фреймворку, что значение этого атрибута зависит от другого атрибута.

После этого, когда вы загружаете Customer с фетч-планом, включающим атрибут address, DataManager автоматически загружает связанный Address из дополнительного хранилища данных. Фреймворк оптимизирует загрузку коллекций для повышения производительности: после загрузки списка клиентов он загружает ссылки из дополнительного хранилища данных частями. Размер частей определяется свойством приложения jmix.core.cross-data-store-reference-loading-batch-size.

Когда вы сохраняете граф сущностей, который включает Customer с Address, DataManager сохраняет экземпляры с помощью соответствующих реализаций DataStore, а затем сохраняет идентификатор адреса в атрибуте addressId клиента.

Создание экземпляров сущностей

При создании экземпляров сущностей JPA и DTO используйте соответствующий интерфейс фреймворка вместо вызова конструктора класса с оператором new. Только в этом случае фреймворк сможет инициализировать поля с аннотациями @JmixGeneratedValue и вызывать методы @PostConstruct.

Наиболее общим методом создания экземпляров сущностей является Metadata.create():

@Autowired
private Metadata metadata;

Order createOrder() {
    return metadata.create(Order.class);
}

Если вы пишете бизнес-логику и в вашем коде уже есть DataManager, используйте его метод create(), что просто избавит вас от необходимости дополнительно инжектировать бин Metadata. Например:

@Autowired
private DataManager dataManager;

Order createAndSaveOrder(String number) {
    Order order = dataManager.create(Order.class);
    order.setNum(number);
    dataManager.save(order);
    return order;
}

На экране UI вы можете использовать один из двух методов, описанных выше. Чтобы созданный экземпляр автоматически сохранялся с помощью DataContext экрана, используйте метод DataContext.create(). Он создает экземпляр и сразу же помещает его в контекст, чтобы начать отслеживать изменения экземпляра. В приведенном ниже примере мы создаем экземпляр сущности ProductPart, помещаем его в DataContext и добавляем в контейнер данных для отображения в UI таблице:

@ViewComponent
private DataContext dataContext;

@ViewComponent
private CollectionPropertyContainer<ProductPart> partsDc;

@Subscribe("createProductPart")
public void onCreateProductPartClick(final ClickEvent<JmixButton> event) {
    ProductPart part = dataContext.create(ProductPart.class);
    partsDc.getMutableItems().add(part);
}

Уникальность

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

Дизайнер сущностей Studio содержит вкладку Indexes для определения уникальных индексов. Эти определения хранятся в аннотации @Table сущности и позже используются генератором файлов Liquibase changelog для создания индексов в схеме базы данных.

Jmix никоим образом не интерпретирует атрибут unique = true аннотации @Column.

Если вы хотите определить уникальный атрибут для сущности с чертой Soft Delete, обратитесь к разделу Мягкое удаление.

Аннотации сущностей Jmix

Аннотации сущностей Jmix описаны ниже в алфавитном порядке.

Сущности в Jmix также могут иметь аннотации для сопоставлений JPA, черт audit и мягкого удаления.

@Comment

Аннотация @Comment используется для добавления комментариев к сущностям модели данных и их атрибутам, например:

@Comment("""
        Stores information about locations.
        Has reference to address""")
@JmixEntity
@Table(name = "LOCATION")
@Entity
public class Location {

    @Comment("Location zip code")
    @InstanceName
    @Column(name = "ZIP_CODE", nullable = false, length = 10)
    @ValidZipCode
    private String zipCode;

Для всех баз данных, кроме HSQL, Studio генерирует операции изменения Liquibase setTableRemarks и setColumnRemarks для сохранения комментариев в схеме базы данных. Таким образом, комментарии становятся доступными через любой инструмент инспекции базы данных.

Вы также можете извлекать комментарии из метаданных (или непосредственно из аннотаций класса) для отображения в пользовательском интерфейсе приложения или генерации документации. Используйте для этого методы MetadataTools.getMetaAnnotationValue().

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

@Composition

Аннотация @Composition на ссылочном атрибуте указывает, что связь является композицией. Например:

@Composition
@OneToMany(mappedBy = "order")
private List<OrderLine> lines;

@DbView

Аннотация @DbView указывает, сопоставлена ли JPA сущность с представлением базы данных. Сценарии миграции базы данных не генерируются для таких сущностей.

@DdlGeneration

Аннотация @DdlGeneration определяет, должны ли средства разработки генерировать DDL-скрипты для этой сущности.

Режим генерации скриптов устанавливается с помощью перечисления DbScriptGenerationMode:

  • CREATE_AND_DROP - полная генерация скриптов инициализации и обновления;

  • CREATE_ONLY - полная генерация скриптов инициализации. Скрипты обновления генерируются без инструкций по удалению столбцов;

  • DISABLED - скрипты инициализации и обновления не генерируются.

Значение по умолчанию: CREATE_AND_DROP.

Также генерацию скриптов можно точно настроить, используя следующие атрибуты:

  • unmappedColumns - список столбцов, которые существуют в базе данных, но не должны быть сопоставлены с сущностью. Скрипты удаления для этих столбцов сгенерированы не будут;

  • unmappedConstraints - список ограничений и индексов, которые существуют в базе данных, но не должны быть сопоставлены с сущностью. Скрипты удаления для этих столбцов сгенерированы не будут.

@DependsOnProperties

Аннотация @DependsOnProperties определяет свойства сущности, от которых зависит аннотированный атрибут. Эти свойства учитываются при построении фетч-планов и при загрузке/сохранении ссылок на сущности из разных хранилищ данных. Кроме того, если аннотированное свойство доступно только для чтения (без сеттера), для этого свойства отправляется EntityPropertyChangeEvent при изменении указанных атрибутов.

Вы можете указать только непосредственные локальные и ссылочные свойства. Такие пути к свойствам, как customer.name не поддерживаются.

@InstanceName

Instance name – это понятный пользователю текст, представляющий экземпляр сущности. Думайте об этом как о методе toString() на уровне приложения. Он часто используется в UI при отображении экземпляра сущности в одном поле или ячейке таблицы. Вы также можете получить имя экземпляра программно, используя метод MetadataTools.getInstanceName().

Аннотация @InstanceName может присутствовать в одном поле или методе объекта.

В первом случае в качестве имени экземпляра используется аннотированное значение атрибута. Например:

@InstanceName
@Column(name = "NAME")
private String name;

Если вы хотите сгенерировать что-то более сложное, чем одно значение атрибута, создайте метод, возвращающий String в классе сущности. Например:

@JmixEntity(name = "sample_GeoPoint")
@Embeddable
public class GeoPointEntity {

    @Column(name = "LAT")
    protected Double latitude;

    @Column(name = "LON")
    protected Double longitude;

    @InstanceName
    @DependsOnProperties({"latitude", "longitude"})
    public String getDisplayName(Messages messages) {
        return messages.formatMessage(
                getClass(), "GeoPointEntity.instanceName", this.latitude, this.longitude);
    }

Метод принимает любые Spring бины в качестве параметров. В приведенном выше примере бин Messages используется для форматирования имени экземпляра в соответствии с текущей локалью пользователя.

Аннотация @DependsOnProperties к методу имени экземпляра необходима, поскольку она определяет атрибуты встроенного фетч-плана _instance_name.

@JmixEntity

@JmixEntity – это обязательная аннотация, указывающая, что класс является сущностью Jmix.

Если класс аннотирован @jakarta.persistence.Entity, фреймворк получает от него имя сущности для метаданных, и @JmixEntity не должен указывать параметр name. В противном случае укажите имя сущности в параметре name. Если ни у @JmixEntity, ни у @jakarta.persistence.Entity нет параметра name, имя сущности равно простому имени класса Java.

Параметр annotatedPropertiesOnly указывает, какие свойства объекта становятся атрибутами сущности. См. Атрибуты сущности для получения более подробной информации.

@JmixGeneratedValue

Аннотация @JmixGeneratedValue указывает, что фреймворк должен сгенерировать и назначить значение атрибута сущности при создании экземпляра сущности в памяти.

Аннотированный атрибут должен быть типа Long, Integer или UUID, и сущность не должна иметь более одного UUID атрибута, отмеченного этой аннотацией.

Обратите внимание, что аннотация @JmixGeneratedValue не вступает в силу, если вы создаете экземпляр сущности с помощью оператора new. См. надлежащие методы создания новых экземпляров в разделе Создание экземпляров сущностей.

@JmixId

Аннотация @JmixId указывает идентификатор сущности для сущностей DTO. Вы должны явно выбрать идентификатор, если ваша сущность DTO сопоставлена с некоторыми внешними данными, и вам нужно многократно загружать/сохранять ее экземпляры, потому что в этом случае вам необходимо поддерживать идентификацию объекта на протяжении всего жизненного цикла сущности.

Вы можете использовать существующий атрибут как идентификатор, если атрибут содержит уникальные значения, например:

@JmixId
private String code;

Если такого естественно уникального атрибута нет, определите его и также аннотируйте @JmixGeneratedValue, чтобы присвоить уникальное значение при создании экземпляра:

@JmixId
@JmixGeneratedValue
private UUID id;

@JmixProperty

Аннотация @JmixProperty указывает, что поле или метод объекта является атрибутом сущности. Дополнительные сведения см. в разделе Атрибуты сущности.

Используйте параметр mandatory, если хотите указать, что для атрибута требуется значение, а поле объекта не содержит JPA аннотацию @Column, в которой вы могли бы задать nullable = false.

@NumberFormat

Задает формат атрибута типа Number (это может быть BigDecimal, Integer, Long или Double). Значения такого атрибута будут форматироваться в пользовательском интерфейсе в соответствии c параметрами, указанными в аннотации:

  • pattern - паттерн форматирования, задается по правилам, описанным в DecimalFormat.

  • decimalSeparator - символ, используемый в качестве разделителя целой и дробной части (опционально).

  • groupingSeparator - символ, используемый в качестве разделителя групп разрядов (опционально).

Если decimalSeparator и/или groupingSeparator не указаны, то при форматировании с учетом локали будут использованы символы из локали текущего пользователя. При форматировании без учета локали используются символы из системной локали сервера.

Примеры:

@Column(name = "PRECISE_NUMBER", precision = 19, scale = 4)
@NumberFormat(pattern = "0.0000")
protected BigDecimal preciseNumber;

@Column(name = "WEIRD_NUMBER", precision = 19, scale = 4)
@NumberFormat(pattern = "#,##0.0000", decimalSeparator = "_", groupingSeparator = "`")
protected BigDecimal weirdNumber;

@Column(name = "SIMPLE_NUMBER")
@NumberFormat(pattern = "#")
protected Integer simpleNumber;

@Column(name = "PERCENT_NUMBER", precision = 19, scale = 4)
@NumberFormat(pattern = "#%")
protected BigDecimal percentNumber;

@PostConstruct

Используйте аннотацию jakarta.annotation.PostConstruct на методе, который выполняет инициализацию нового экземпляра сущности. Например:

@PostConstruct
void init() {
    setGrade(CustomerGrade.BRONZE);
}

Аннотированный метод принимает любые Spring бины. В приведенном ниже примере мы используем бин TimeSource для инициализации атрибута date:

@PostConstruct
void init(TimeSource timeSource) {
    setDate(timeSource.now().toLocalDate());
}
Обратите внимание, что аннотированный метод @PostConstruct не вызывается, если вы создаете экземпляр сущности с помощью оператора new. См. надлежащие методы создания новых экземпляров в разделе Создание экземпляров сущностей.

@PropertyDatatype

Если у вас есть несколько типов данных для Java-типа атрибута сущности, аннотация @PropertyDatatype позволяет прямо указать реализацию Datatype по ее идентификатору.

@PropertyDatatype("year")
@Column(name = "YEAR_")
private Integer productionYear;

@Store

Используйте аннотацию @Store для класса сущности, чтобы связать сущность с дополнительным хранилищем данных.

@SystemLevel

Аннотация @SystemLevel указывает, что аннотированная сущность или ее атрибут являются низкоуровневыми и не должны отображаться в пользовательском интерфейсе.