Сущности
В Jmix существуют следующие типы сущностей:
-
Сущности JPA – это объекты Java, хранящиеся в базе данных с использованием Java Persistence API.
-
Сущности DTO – это простые объекты Java, которые не привязаны к какой-либо конкретной технологии персистентности.
-
Сущность Key-Value (ключ-значение) – это динамическая сущность с произвольным количеством атрибутов.
Сущности характеризуются своими атрибутами.
Сущности JPA и DTO сущности определяются классами Java и имеют некоторые специфичные для Jmix аннотации.
Для создания сущностей JPA и DTO используйте дизайнер сущностей Studio. |
Не создавайте методы equals() и hashCode() в ваших классах сущностей явно или с помощью аннотаций Lombok. Эти методы будут сгенерированы автоматически фреймворком во время сборки приложения. Сгенерированные методы будут использовать идентификаторы сущностей для сравнения.
|
Сущности JPA
JPA сущность – это класс Java, аннотированный в соответствии с правилами JPA. Сущности JPA хранятся в реляционной базе данных, подключенной в качестве основного или дополнительного хранилища данных.
Аннотации JPA определяют отображение между полями таблицы базы данных и атрибутами сущности. Jmix налагает следующие ограничения на аннотации JPA:
-
Аннотации атрибутов должны размещаться только в полях (
AccessType.FIELD
). -
Не поддерживаются:
@IdClass
,@ElementCollection
.
Ниже приведен пример типичного класса сущности JPA:
@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-сущности:
@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 | Все свойства объекта (поля с методами доступа) становятся атрибутами сущности. |
Атрибуты сущностей могут иметь аннотации для указания каких-либо подробностей о них:
@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.
В приведенном ниже примере показано, как исключить некоторые свойства объекта из атрибутов сущности (подробнее об этом в разделе Атрибуты сущности).
@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;
@OnDelete
Аннотация @OnDelete
может использоваться для ссылочных атрибутов сущностей с мягким удалением. Она определяет, что должно произойти с ссылаемой сущностью, когда сущность, содержащая аннотацию (данная сущность), удаляется.
Аннотация может иметь одно из следующих значений:
-
DeletePolicy.DENY
– выбросить исключение при попытке удалить данную сущность, если ссылка не равна null. -
DeletePolicy.CASCADE
– удалить ссылаемые сущности вместе с данной сущностью. -
DeletePolicy.UNLINK
– отсоединить ссылаемую сущность, установив атрибут ссылки в null. Используйте это значение только на владеющей стороне отношения (той, у которой есть аннотация@JoinColumn
).
@OnDeleteInverse
Аннотация @OnDelete
может использоваться для ссылочных атрибутов сущностей с мягким удалением и обычных жестко удаляемых сущностей. Она определяет, что должно произойти с сущностью, содержащей аннотацию (данной сущностью), когда ссылаемая сущность удаляется.
Аннотация может иметь одно из следующих значений:
-
DeletePolicy.DENY
– выбросить исключение при попытке удалить ссылаемую сущность. -
DeletePolicy.CASCADE
– удалить данную сущность вместе со ссылаемой сущностью. -
DeletePolicy.UNLINK
– установить атрибут ссылки в null.
Для сущностей с мягким удалением @OnDeleteInverse
обрабатывается фреймворком во время выполнения приложения. Значения DENY
и CASCADE
могут использоваться как на владеющей стороне (той, у которой есть аннотация @JoinColumn
), так и на не владеющей (mappedBy
) сторонах отношения. Значение UNLINK
следует использовать только на владеющей стороне.
Для обычных жестко удаляемых сущностей @OnDeleteInverse
со значениями CASCADE
и UNLINK
обрабатывается Studio при генерации Liquibase changelogs. Его нужно использовать только на владеющей стороне отношения (той, у которой есть аннотация @JoinColumn
) и это приводит к созданию ограничения внешнего ключа с параметрами ON DELETE CASCADE
или ON DELETE SET NUll
.
@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
для класса сущности, чтобы связать сущность с дополнительным хранилищем данных.