Data Stores
A data store represents a database or any other source of data in your application.
Jmix provides JpaDataStore
which stores JPA entities in a relational database. This is the main persistence mechanism in Jmix applications, and when we mention a data store in this manual, we always mean JpaDataStore
if not explicitly stated otherwise.
Main Store
When you create a new Jmix project in Studio, it has a single data store called main that connects to a relational database. The connection parameters are specified in the following application properties:
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
Use Studio interface to define database connection properties for a data store. |
The main application class contains a corresponding JDBC DataSource
bean declaration:
@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();
}
All JPA entities by default are associated with the main data store.
Additional Stores
In order to work with multiple databases, you need additional data stores.
Use Studio interface to define additional data stores. |
Each additional store has a unique name, which is specified in the comma-separated list of the jmix.core.additional-stores
application property. Parameters of the database connection have the store name as a prefix. In the examples below, the locations
additional store is configured:
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
For each additional store, Studio creates a Spring configuration class and defines the JDBC DataSource
and other related beans in it:
@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);
}
}
To associate an entity with an additional data store, use the @Store
annotation on the entity class:
@Store(name = "locations")
@JmixEntity
@Table(name = "COUNTRY")
@Entity
public class Country {
Studio adds @Store annotation when you select an additional data store for the entity in the entity designer.
|
In the example above, the Country
entity will be stored in the database connected as the locations
data store.
Custom Data Store
A custom data store may help you to work with some DTO entities in the same way as with JPA entities - by using DataManager
. If your DTO entity is associated with a custom store, DataManager
will delegate its CRUD operations to your data store and use it when resolving references to your DTOs from other entities.
Let’s consider the process of creating a custom data store. Imagine a transient Metric
entity and an in-memory store for it.
-
Create a class that implements the
DataStore
interface. The class must be a Spring prototype bean. The example below contains a primitive implementation that is capable of storing entities of different types in memory.@Component @Scope(BeanDefinition.SCOPE_PROTOTYPE) public class InMemoryStore implements DataStore { // ... }
-
Create a class that implements the
StoreDescriptor
interface. It must be a Spring singleton bean, and itsgetBeanName()
method must return the name of the data store implementation bean created on the previous step:@Component public class InMemoryStoreDescriptor implements StoreDescriptor { @Override public String getBeanName() { return "inMemoryStore"; } @Override public boolean isJpa() { return false; } }
-
Add the data store name (
inmem
in this case) to thejmix.core.additionalStores
property:jmix.core.additional-stores = locations,inmem
-
Set the
StoreDescriptor
bean name in thejmix.core.store-descriptor-<store_name>
property:jmix.core.store-descriptor-inmem = inMemoryStoreDescriptor
-
Add
@Store
annotation to the entity:@Store(name = "inmem") (1) @JmixEntity(annotatedPropertiesOnly = true) (2) public class Metric {
1 The @Store
annotation specifies a custom data store.2 See @JmixEntity description for more details. -
Now you can save and load the entity using
DataManager
and it will delegate the CRUD operations to your custom data store:Metric metric = dataManager.create(Metric.class); metric.setName("test"); metric.setValue(10.0); dataManager.save(metric); Metric metric1 = dataManager.load(Id.of(metric)).one();
Also, if another entity has a reference to
Metric
, theMetric
instance will be loaded automatically when accessing the reference attribute.