Возможно ли переключение источника данных jpa во время выполнения? В моих приложениях зарегистрированы некоторые источники данных, но мне нужно переключить источник данных во время выполнения.
Я создаю веб-приложение для отображения сводной информации о нескольких приложениях (white-label). Каждое приложение имеет свою собственную базу данных с одинаковой структурой. У меня есть доступ на чтение ко всей этой базе данных.
Тогда у этого веб-приложения есть собственная база данных для хранения авторизации. Какая база данных доступна каждому пользователю. У некоторых пользователей есть доступ к нескольким базам данных.
Когда пользователи получают доступ к информации из определенной базы данных, сначала это веб-приложение должно проверить, есть ли у этого пользователя полномочия для этой базы данных. Если авторизовано, то хранилище будет выбирать данные из этой базы данных. Но, конечно, для этого нужно сначала изменить источник данных.
Примечание. У меня нет разрешения на создание таблицы для каждой базы данных приложений, только для чтения. И я хочу авторизацию базы данных, хранящейся в одной таблице. Таким образом, проще будет модифицировать или создать форму для управления ею.
Это мой фрагмент кода
Класс CurrentTenantIdentifierResolverImpl: для получения текущего идентификатора арендатора. Ранее я использовал поддомен, чтобы определить, какой идентификатор источника данных будет использоваться.
@Component
public class CurrentTenantIdentifierResolverImpl implements CurrentTenantIdentifierResolver {
private static final String DEFAULT_TENANT_ID = "test";
/*
* (non-Javadoc)
*
* @see org.hibernate.context.spi.CurrentTenantIdentifierResolver#
* resolveCurrentTenantIdentifier()
*/
@Override
public String resolveCurrentTenantIdentifier() {
String tenant = DEFAULT_TENANT_ID;
/*
* RequestAttributes requestAttributes =
* RequestContextHolder.getRequestAttributes(); if (requestAttributes != null) {
* ServletRequestAttributes attr = (ServletRequestAttributes)
* RequestContextHolder.currentRequestAttributes(); HttpServletRequest request =
* attr.getRequest(); tenant = UrlParser.getIlibrary(request);
* TenantContextHolder.setTenantId(tenant); }
*/
// The tenant is stored in a ThreadLocal before the end user's login information
// is submitted for spring security authentication mechanism. Refer to
// CustomAuthenticationFilter
return StringUtils.isNotBlank(tenant) ? tenant : DEFAULT_TENANT_ID;
}
/*
* (non-Javadoc)
*
* @see org.hibernate.context.spi.CurrentTenantIdentifierResolver#
* validateExistingCurrentSessions()
*/
@Override
public boolean validateExistingCurrentSessions() {
return true;
}
}
Этот класс предназначен для получения соответствующих источников данных
@Component
public class DataSourceBasedMultiTenantConnectionProviderImpl
extends AbstractDataSourceBasedMultiTenantConnectionProviderImpl {
private static final long serialVersionUID = 1L;
@Autowired
private Map<String, DataSource> dataSourcesMtApp;
/*
* (non-Javadoc)
*
* @see org.hibernate.engine.jdbc.connections.spi.
* AbstractDataSourceBasedMultiTenantConnectionProviderImpl#selectAnyDataSource(
* )
*/
@Override
protected DataSource selectAnyDataSource() {
return this.dataSourcesMtApp.values().iterator().next();
}
/*
* (non-Javadoc)
*
* @see org.hibernate.engine.jdbc.connections.spi.
* AbstractDataSourceBasedMultiTenantConnectionProviderImpl#selectDataSource(
* java.lang.String)
*/
@Override
protected DataSource selectDataSource(String tenantIdentifier) {
return this.dataSourcesMtApp.get(tenantIdentifier);
}
}
Этот класс предназначен для хранения всех источников данных
@Configuration
@EnableConfigurationProperties({ MultitenancyProperties.class, JpaProperties.class })
@EnableTransactionManagement
public class MultiTenancyJpaConfiguration {
@Autowired
private JpaProperties jpaProperties;
@Autowired
private MultitenancyProperties multitenancyProperties;
/**
* Builds a map of all data sources defined in the application.yml file
*
* @return
*/
@Primary
@Bean(name = "dataSourcesMtApp")
public Map<String, DataSource> dataSourcesMtApp() {
Map<String, DataSource> result = new HashMap<String, DataSource>();
for (DataSourceProperties dsProperties : this.multitenancyProperties.getDataSources()) {
DataSourceBuilder<?> factory = DataSourceBuilder.create().url(dsProperties.getUrl())
.username(dsProperties.getUsername()).password(dsProperties.getPassword())
.driverClassName(dsProperties.getDriverClassName());
result.put(dsProperties.getTenantId(), factory.build());
}
return result;
}
/**
* Autowires the data sources so that they can be used by the Spring JPA to
* access the database
*
* @return
*/
@Bean
public MultiTenantConnectionProvider multiTenantConnectionProvider() {
// Autowires dataSourcesMtApp
return new DataSourceBasedMultiTenantConnectionProviderImpl();
}
/**
* Since this is a multi-tenant application, Hibernate requires that the current
* tenant identifier is resolved for use with
* {@link org.hibernate.context.spi.CurrentSessionContext} and
* {@link org.hibernate.SessionFactory#getCurrentSession()}
*
* @return
*/
@Bean
public CurrentTenantIdentifierResolver currentTenantIdentifierResolver() {
return new CurrentTenantIdentifierResolverImpl();
}
/**
* org.springframework.beans.factory.FactoryBean that creates a JPA
* {@link javax.persistence.EntityManagerFactory} according to JPA's standard
* container bootstrap contract. This is the most powerful way to set up a
* shared JPA EntityManagerFactory in a Spring application context; the
* EntityManagerFactory can then be passed to JPA-based DAOs via dependency
* injection. Note that switching to a JNDI lookup or to a
* {@link org.springframework.orm.jpa.LocalEntityManagerFactoryBean} definition
* is just a matter of configuration!
*
* @param multiTenantConnectionProvider
* @param currentTenantIdentifierResolver
* @return
*/
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(
MultiTenantConnectionProvider multiTenantConnectionProvider,
CurrentTenantIdentifierResolver currentTenantIdentifierResolver) {
Map<String, Object> hibernateProps = new LinkedHashMap<>();
hibernateProps.putAll(this.jpaProperties.getProperties());
hibernateProps.put(Environment.MULTI_TENANT, MultiTenancyStrategy.DATABASE);
hibernateProps.put(Environment.MULTI_TENANT_CONNECTION_PROVIDER, multiTenantConnectionProvider);
hibernateProps.put(Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, currentTenantIdentifierResolver);
// No dataSource is set to resulting entityManagerFactoryBean
LocalContainerEntityManagerFactoryBean result = new LocalContainerEntityManagerFactoryBean();
result.setPackagesToScan(new String[] { Instansi.class.getPackage().getName() });
result.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
result.setJpaPropertyMap(hibernateProps);
return result;
}
/**
* Interface used to interact with the entity manager factory for the
* persistence unit.
*
* @param entityManagerFactoryBean
* @return
*/
@Bean
public EntityManagerFactory entityManagerFactory(LocalContainerEntityManagerFactoryBean entityManagerFactoryBean) {
return entityManagerFactoryBean.getObject();
}
/**
* Creates a new
* {@link org.springframework.orm.jpa.JpaTransactionManager#JpaTransactionManager(EntityManagerFactory emf)}
* instance.
*
* {@link org.springframework.transaction.PlatformTransactionManager} is the
* central interface in Spring's transaction infrastructure. Applications can
* use this directly, but it is not primarily meant as API: Typically,
* applications will work with either TransactionTemplate or declarative
* transaction demarcation through AOP.
*
* @param entityManagerFactory
* @return
*/
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
Для сопоставления конфигурации базы данных
@Configuration
@ConfigurationProperties("multitenancy.mtapp")
public class MultitenancyProperties {
private List<DataSourceProperties> dataSourcesProps;
public List<DataSourceProperties> getDataSources() {
return this.dataSourcesProps;
}
public void setDataSources(List<DataSourceProperties> dataSourcesProps) {
this.dataSourcesProps = dataSourcesProps;
}
public static class DataSourceProperties extends org.springframework.boot.autoconfigure.jdbc.DataSourceProperties {
private String tenantId;
public String getTenantId() {
return tenantId;
}
public void setTenantId(String tenantId) {
this.tenantId = tenantId;
}
}
}