是否可以在运行时切换jpa数据源? 我已经在我的应用程序上注册了一些数据源,但是我需要在运行时切换数据源。
我正在构建一个Web应用程序以显示多个应用程序的摘要信息(白色标签)。每个应用程序都有其自己的具有相同结构的数据库。我具有对所有此数据库的读取权限。
然后,此Web应用程序具有其自己的数据库来存储授权。每个用户都可以访问哪个数据库。某些用户可以访问多个数据库。
当用户从某些数据库访问信息时,此Web应用程序首先需要检查该用户是否对该数据库具有授权。如果获得授权,则存储库将从该数据库中选择数据。但是,当然,这件事需要首先更改数据源。
注意:我无权在每个应用程序数据库上创建表,只读。我想授权数据库存储在一个表中。因此,进行修改或创建表单来管理它会更容易。
这是我的代码段
Class CurrentTenantIdentifierResolverImpl:获取当前租户ID。以前,我使用子域来定义将使用的数据源ID。
@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;
}
}
}