我设法提出了一些解决方案来完成此任务。
选项1-多租户
我认为多租户方法似乎是最干净的方法,同时仍使每个租户都拥有自己的数据库。
目录结构
org.company.project - ApplicationMain |_config - DatasourceConfiguration - WebMvcConfig |_routing - TenantContext - TenantInterceptor - TenantSourceRouter |_domain - Person |_repository |_ PersonRepository |_web -APIController
数据源配置
@Configuration@EnableTransactionManagementpublic class DatasourceConfiguration { @Resource private Environment env; @Bean public DataSource dataSource() { AbstractRoutingDataSource dataSource = new TenantSourceRouter(); Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put("ALBANY", albanyDatasource()); targetDataSources.put("BUFFALO", buffaloDatasource()); dataSource.setTargetDataSources(targetDataSources); dataSource.setDefaultTargetDataSource(albanyDatasource()); return dataSource; } public DataSource albanyDatasource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName(env.getProperty("company.datasource.driver-class-name")); dataSource.setUrl(env.getProperty("company.datasource.albany.jdbc-url")); dataSource.setUsername(env.getProperty("company.datasource.albany.username")); dataSource.setPassword(env.getProperty("company.datasource.albany.password")); return dataSource; } public DataSource buffaloDatasource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName(env.getProperty("company.datasource.driver-class-name")); dataSource.setUrl(env.getProperty("company.datasource.buffalo.jdbc-url")); dataSource.setUsername(env.getProperty("company.datasource.buffalo.username")); dataSource.setPassword(env.getProperty("company.datasource.buffalo.password")); return dataSource; }}域实体-人
@Entitypublic class Person { @Id private String id; private String name;}人资料库
public interface PersonRepository extends JpaRepository<Person, String> {}租户上下文
public class TenantContext { private static final ThreadLocal<String> currentTenant = new ThreadLocal<>(); public static void setCurrentTenant(String tenant) { Assert.notNull(tenant, "clientDatabase cannot be null"); currentTenant.set(tenant); } public static String getClientDatabase() { return currentTenant .get(); } public static void clear() { currentTenant .remove(); }}租户上下文
public class TenantSourceRouter extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return TenantContext.getClientDatabase(); }}TenantInterceptor- 我决定添加一个全局拦截器,在该拦截器中,您可以将请求标头“ X-TenantID”设置为所需的租户“
ALBANY”或“ BUFFALO”,而不必在逐个控制器的基础上进行处理。
@Componentpublic class TenantInterceptor extends HandlerInterceptorAdapter { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String tenantId = request.getHeader("X-TenantID"); TenantContext.setCurrentTenant(tenantId); return true; } @Override public void postHandle( HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { TenantContext.clear(); }}WebMvcConfig- 现在我们必须向WebMvc注册拦截器
@Configurationpublic class WebMvcConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new TenantInterceptor()); }}APIController- 最后,我们创建控制器,在该控制器中访问存储库。
@RestController@RequestMapping("/api")public class APIController { @Autowired private PersonRepository personRepository; @GetMapping("/{id}") public Optional<Person> get(@PathVariable String id) { return personRepository.findById(id); } @GetMapping("/") public List<Person> getAll() { return personRepository.findAll(); }}application.yml
company: datasource: driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver albany: jdbc-url: *** username: *** password: *** buffalo: jdbc-url: *** username: *** password: ***
选项2-具有多个存储库的更传统的多租户
目录结构
org.company.project - ApplicationMain |_config - AlbanyDbConfiguration (datasource 1) - BuffaloDbConfiguration (datasource 2) |_domain - Person |_repository |_ albany - PersonRepositoryAlbany (repository for datasource 1) |_ buffalo - PersonRepositoryBuffalo (repository for datasource 2) |_web -APIController
application.yml
spring: datasource: jdbc-url: *** username: *** password: ***buffalo: datasource: jdbc-url: *** username: *** password: ***
域实体-人
@Entitypublic class Person { @Id private String id; private String name;}存储库-PersonRepositoryAlbany *
public interface PersonRepositoryAlbany extends JpaRepository<Person, String>, JpaSpecificationExecutor<Person> {}存储库-PersonRepositoryBuffalo *
public interface PersonRepositoryBuffalo extends JpaRepository<Person, String>, JpaSpecificationExecutor<Person> {}数据源配置-AlbanyDbConfiguration
@Configuration@EnableJpaRepositories( basePackages = { "org.company.project.repository.albany"}, entityManagerFactoryRef = "albanyEntityManagerFactory", transactionManagerRef = "albanyTransactionManager")public class AlbanyDbConfiguration { @Primary @Bean(name = "dataSource") @ConfigurationProperties(prefix = "spring.datasource") public DataSource dataSource() { return DataSourceBuilder.create().build(); } @Primary @Bean(name = "albanyEntityManagerFactory") public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder, @Qualifier("dataSource") DataSource dataSource) { return builder .dataSource(dataSource) .packages("org.company.project.domain") .properties(jpaProperties()) .build(); } public Map<String, Object> jpaProperties() { Map<String, Object> props = new HashMap<>(); props.put("hibernate.physical_naming_strategy", SpringPhysicalNamingStrategy.class.getName()); props.put("hibernate.implicit_naming_strategy", SpringImplicitNamingStrategy.class.getName()); return props; } @Primary @Bean(name = "albanyTransactionManager") public PlatformTransactionManager transactionManager(@Qualifier("albanyEntityManagerFactory") EntityManagerFactory entityManagerFactory) { return new JpaTransactionManager(entityManagerFactory); }}数据源配置-BuffaloDbConfiguration
@Configuration@EnableJpaRepositories( basePackages = { "org.company.project.repository.buffalo"}, entityManagerFactoryRef = "buffaloEntityManagerFactory", transactionManagerRef = "buffaloTransactionManager")public class BuffaloDbConfiguration { @Bean(name = "buffaloDataSource") @ConfigurationProperties(prefix = "buffalo.datasource") public DataSource dataSource() { return DataSourceBuilder.create().build(); } @Bean(name = "buffaloEntityManagerFactory") public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder, @Qualifier("buffaloDataSource") DataSource dataSource) { return builder .dataSource(dataSource) .packages("org.company.project.domain") .properties(jpaProperties()) .build(); } public Map<String, Object> jpaProperties() { Map<String, Object> props = new HashMap<>(); props.put("hibernate.physical_naming_strategy", SpringPhysicalNamingStrategy.class.getName()); props.put("hibernate.implicit_naming_strategy", SpringImplicitNamingStrategy.class.getName()); return props; } @Bean(name = "buffaloTransactionManager") public PlatformTransactionManager transactionManager(@Qualifier("buffaloEntityManagerFactory") EntityManagerFactory entityManagerFactory) { return new JpaTransactionManager(entityManagerFactory); }}Web控制器-APIController
@EnableTransactionManagement@RestController@RequestMapping("/api")public class APIController { @Autowired private PersonRepositoryAlbany personRepositoryAlbany; @Autowired private PersonRepositoryBuffalo personRepositoryBuffalo; @GetMapping("/albany") public List<Person> albany() { return getPersonsAlbany(); } @GetMapping("/buffalo") public List<Person> buffalo() { return getPersonsBuffalo(); } @Transactional("albanyTransactionManager") public List<Person> getPersonsAlbany() { return personRepositoryAlbany.findAll(); } @Transactional("buffaloTransactionManager") public List<Person> getPersonsBuffalo() { return personRepositoryBuffalo.findAll(); }}


