Хранилища данных

Хранилище представляет собой базу данных или любой другой источник данных в приложении.

Подсистема ядра Jmix предоставляет JpaDataStore, которое хранит сущности JPA в реляционной базе данных. Это основной механизм персистентности в приложениях Jmix. Когда в этом руководстве упоминается хранилище данных, всегда имеется в виду JpaDataStore, если явно не указано иное.

Дополнение REST DataStore предоставляет RestDataStore, которое работает с DTO сущностями, сопоставленными с REST API удаленного приложение Jmix. REST хранилище данных имеет некоторые ограничения по сравнению с JPA хранилищем.

Основное хранилище

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

main.datasource.url = jdbc:hsqldb:file:.jmix/hsqldb/demo
main.datasource.username = sa
main.datasource.password =

main.liquibase.change-log = com/company/demo/liquibase/changelog.xml
Используйте интерфейс Studio для настройки подключения к базе данных.

Основной класс приложения содержит соответствующее объявление бина JDBC DataSource:

@Bean
@Primary
@ConfigurationProperties("main.datasource")
DataSourceProperties dataSourceProperties() {
    return new DataSourceProperties();
}

@Bean
@Primary
@ConfigurationProperties("main.datasource.hikari")
DataSource dataSource(final DataSourceProperties dataSourceProperties) {
    return dataSourceProperties.initializeDataSourceBuilder().build();
}

Все сущности JPA по умолчанию связаны с основным хранилищем данных.

Дополнительные хранилища

Для работы с несколькими базами данных необходимы дополнительные хранилища данных.

Определите дополнительные хранилища данных в интерфейсе Studio.

У каждого дополнительного хранилища есть уникальное имя, которое указано в списке свойства приложения jmix.core.additional-stores, разделенном запятыми. Параметры подключения к базе данных имеют имя хранилища в качестве префикса. В приведенных ниже примерах настроено дополнительное хранилище locations:

jmix.core.additional-stores = locations,inmem

locations.datasource.url = jdbc:hsqldb:file:.jmix/hsqldb/locations
locations.datasource.username = sa
locations.datasource.password =

locations.liquibase.change-log = com/company/demo/liquibase/locations-changelog.xml

Для каждого дополнительного хранилища Studio создает класс конфигурации Spring и определяет в нем JDBC DataSource и другие необходимые бины:

@Configuration
public class LocationsStoreConfiguration {

    @Bean
    @ConfigurationProperties("locations.datasource")
    DataSourceProperties locationsDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    @ConfigurationProperties(prefix = "locations.datasource.hikari")
    DataSource locationsDataSource(@Qualifier("locationsDataSourceProperties") DataSourceProperties properties) {
        return properties.initializeDataSourceBuilder().build();
    }

    @Bean
    LocalContainerEntityManagerFactoryBean locationsEntityManagerFactory(
            @Qualifier("locationsDataSource") DataSource dataSource,
            JpaVendorAdapter jpaVendorAdapter,
            DbmsSpecifics dbmsSpecifics,
            JmixModules jmixModules,
            Resources resources
    ) {
        return new JmixEntityManagerFactoryBean("locations", dataSource, jpaVendorAdapter, dbmsSpecifics, jmixModules, resources);
    }

    @Bean
    JpaTransactionManager locationsTransactionManager(@Qualifier("locationsEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
        return new JmixTransactionManager("locations", entityManagerFactory);
    }

    @Bean("locationsLiquibaseProperties")
    @ConfigurationProperties(prefix = "locations.liquibase")
    public LiquibaseProperties locationsLiquibaseProperties() {
        return new LiquibaseProperties();
    }

    @Bean("locationsLiquibase")
    public SpringLiquibase locationsLiquibase(@Qualifier("locationsDataSource") DataSource dataSource,
                                              @Qualifier("locationsLiquibaseProperties") LiquibaseProperties liquibaseProperties) {
        return JmixLiquibaseCreator.create(dataSource, liquibaseProperties);
    }
}

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

@Store(name = "locations")
@JmixEntity
@Table(name = "COUNTRY")
@Entity
public class Country {
Studio добавляет аннотацию @Store при выборе дополнительного хранилища данных для сущности в дизайнере сущностей.

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

Нестандартные хранилища

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

Давайте рассмотрим процесс создания нестандартного хранилища данных. Представим неперсистентную сущность Metric и хранилище в памяти для нее.

  1. Создайте класс, реализующий интерфейс DataStore. Класс должен быть prototype бином Spring. Приведенный ниже пример демонстрирует примитивную реализацию, которая способна хранить в памяти сущности различных типов.

    @Component
    @Scope(BeanDefinition.SCOPE_PROTOTYPE)
    public class InMemoryStore implements DataStore {
        // ...
    }
  2. Создайте класс, реализующий интерфейс StoreDescriptor. Класс должен быть singleton бином Spring и его метод getBeanName() должен возвращать имя бина реализации хранилища данных, созданного на предыдущем шаге:

    @Component
    public class InMemoryStoreDescriptor implements StoreDescriptor {
    
        @Override
        public String getBeanName() {
            return "inMemoryStore";
        }
    
        @Override
        public boolean isJpa() {
            return false;
        }
    }
  3. Добавьте имя хранилища данных (в данном случае это inmem) в свойство jmix.core.additionalStores:

    jmix.core.additional-stores = locations,inmem
  4. Задайте имя бина StoreDescriptor в свойстве jmix.core.storeDescriptor_<store_name>:

    jmix.core.store-descriptor_inmem = inMemoryStoreDescriptor
  5. Добавьте к сущности аннотацию @Store:

    @Store(name = "inmem") (1)
    @JmixEntity(annotatedPropertiesOnly = true) (2)
    public class Metric {
    1 Аннотация @Store указывает имя хранилища.
    2 Подробности см. в описании @JmixEntity.
  6. Теперь вы можете сохранять и загружать сущность с помощью DataManager, и он будет делегировать операции CRUD нестандартному хранилищу данных:

    Metric metric = dataManager.create(Metric.class);
    metric.setName("test");
    metric.setValue(10.0);
    dataManager.save(metric);
    
    Metric metric1 = dataManager.load(Id.of(metric)).one();

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