所以我有一个相当标准的Spring Boot应用程序,它使用JavaConfig和JPA来连接服务和存储库。但是,该应用程序的一个非标准方面是需要根据需要启动隔离的云数据库,以便出于法律原因将客户数据分开。
我有一个简单的ClientService注入了一些存储库,我的目标是创建某种工厂,我可以请求特定于每个客户端的此ClientService版本,该版本具有注入所有存储库的自定义DataSource。 Spring Boot通常非常适合用于连接,它使事情变得更加混乱,因为很难看到幕后发生的事情。
最好的方法是什么?我的第一个想法是一个名为ClientServiceFactory的bean,其方法是getClientService(客户端客户端)。我很容易为这个客户端创建自定义DataSource - 困难的部分是我如何返回ClientService的实例,并自动注入所有其他内容,但强制所有存储库bean使用此数据源。自然ClientService将不再是单身人士,而是我会在客户端内部存储地图> ClientService。
非常感谢任何帮助或建议。
答案 0 :(得分:2)
我认为您需要实施多租户方法。
我在Multi-tenant applications using Spring Boot, JPA, Hibernate and Postgres
上发表了关于此主题的博文基本上,这是为多租户配置持久层的步骤:
<强> application.yml 强>
...
multitenancy:
dvdrental:
dataSources:
-
tenantId: TENANT_01
url: jdbc:postgresql://172.16.69.133:5432/db_dvdrental
username: user_dvdrental
password: changeit
driverClassName: org.postgresql.Driver
-
tenantId: TENANT_02
url: jdbc:postgresql://172.16.69.133:5532/db_dvdrental
username: user_dvdrental
password: changeit
driverClassName: org.postgresql.Driver
...
<强> MultiTenantJpaConfiguration.java 强>
...
@Configuration
@EnableConfigurationProperties({ MultiTenantDvdRentalProperties.class, JpaProperties.class })
@ImportResource(locations = { "classpath:applicationContent.xml" })
@EnableTransactionManagement
public class MultiTenantJpaConfiguration {
@Autowired
private JpaProperties jpaProperties;
@Autowired
private MultiTenantDvdRentalProperties multiTenantDvdRentalProperties;
...
}
<强> MultiTenantDvdRentalProperties.java 强>
...
@Configuration
@ConfigurationProperties(prefix = "multitenancy.dvdrental")
public class MultiTenantDvdRentalProperties {
private List<DataSourceProperties> dataSourcesProps;
// Getters and Setters
public static class DataSourceProperties extends org.springframework.boot.autoconfigure.jdbc.DataSourceProperties {
private String tenantId;
// Getters and Setters
}
}
<强> MultiTenantJpaConfiguration.java 强>
...
public class MultiTenantJpaConfiguration {
...
@Bean(name = "dataSourcesDvdRental" )
public Map<String, DataSource> dataSourcesDvdRental() {
...
}
...
}
<强> MultiTenantJpaConfiguration.java 强>
...
public class MultiTenantJpaConfiguration {
...
@Bean
public MultiTenantConnectionProvider multiTenantConnectionProvider() {
...
}
@Bean
public CurrentTenantIdentifierResolver currentTenantIdentifierResolver() {
...
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(MultiTenantConnectionProvider multiTenantConnectionProvider,
CurrentTenantIdentifierResolver currentTenantIdentifierResolver) {
...
}
...
}
<强> MultiTenantJpaConfiguration.java 强>
...
public class MultiTenantJpaConfiguration {
...
@Bean
public EntityManagerFactory entityManagerFactory(LocalContainerEntityManagerFactoryBean entityManagerFactoryBean) {
...
}
@Bean
public PlatformTransactionManager txManager(EntityManagerFactory entityManagerFactory) {
...
}
...
}
<强> applicationContent.xml 强>
...
<jpa:repositories base-package="com.asimio.dvdrental.dao" transaction-manager-ref="txManager" />
<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true" />
...
<强> ActorDao.java 强>
public interface ActorDao extends JpaRepository<Actor, Integer> {
}
根据您的需要,可以这样做:
...
@Autowired
private ActorDao actorDao;
...
DvdRentalTenantContext.setTenantId("TENANT_01");
this.actorDao.findOne(...);
...
// Or
DvdRentalTenantContext.setTenantId("TENANT_02");
this.actorDao.save(...);
...
设置tenantId可以通过将要执行JPA操作等的ThreadLocal在servlet过滤器/ Spring MVC拦截器/线程中完成。
答案 1 :(得分:1)
您可能无法直接实现这一目标。由于存储库与EntityManagers紧密耦合,并且EntityManagers与数据源紧密耦合,暗示存储库与数据源紧密耦合。您可能无法在spring启动时创建存储库。
您可以在开始时为不同的数据源创建多个JPARepositorySets。让我们说你需要一个UserRepository。您有2个数据源DS1,DS2。
您可以为两个数据源创建两个实体管理器EM1,EM2。
public LocalContainerEntityManagerFactoryBean em1() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(ds1());
em.setPackagesToScan(new String[] { "com.pkg1.entities.user" });
return em;
}
public LocalContainerEntityManagerFactoryBean em2() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(ds2());
em.setPackagesToScan(new String[] { "com.pkg1.entities.user" });
return em;
}
在运行时,取决于客户端,工厂bean可以为您创建存储库,并按如下方式返回存储库。
public UserRepository getClientSpecificUserRepository(Client client){
SimpleJpaRepository<User, Long> userRepository = null;
if(client.name.equals("ABC")){
userRepository = new SimpleJpaRepository<User, Serializable>(
User.class, em1);
} else (client.name.equals("ABC")){
userRepository = new SimpleJpaRepository<User, Serializable>(
User.class, em2);
}
return userRepository;
}
因此,最终在上下文启动时为所有客户创建多个EntityManagers。并且Factory在运行时根据客户端
返回Repository bean