gpt4 book ai didi

java - 具有单个域对象和多个数据源的 Spring Boot

转载 作者:塔克拉玛干 更新时间:2023-11-02 08:31:33 27 4
gpt4 key购买 nike

我已经阅读了很多关于拥有多个数据源的帖子,但我觉得我的情况可能有点独特,因为我不是在寻求设置多个数据源的帮助,而是寻求帮助配置多个数据源使用单个域(实体)对象。

用例场景

我们有两个相同的财务系统,但我组织中的数据除外,每个系统代表公司的不同部门。每个部门都有一个完全独立的数据库,具有相同的模式。我必须构建一个应用程序来连接两个数据库。当用户登录时,他们将选择他们需要访问的公司部门并继续他们的数据请求。基于包含划分的查询参数,应用程序将需要在域对象中选择正确的数据源并拉回适当的数据。

在 groovy/grails 中,我能够拥有一个包含多个数据源的域。

示例。

static mapping = {
datasources (['datasourceA','datasourceB'])
}

基于查询参数,我能够确定要使用的数据源。

示例

Person."${division.datasource}".findAllByRunId

我想知道如何在 SpringBoot 2.2.0 中实现同样的行为?

数据库

Finance_System_A (datasourceA)
- Person:
- Name: John
- ID: 1

Finance_System_B (datasourceB)
- Person:
- Name: Dave
- ID: 1

SpringBoot应用

SpringBoot Person Domain
- Person:
- Name:
- ID:

查询示例(grails风格)

Person.{"datasourceA"}.findById(1) = John
Person.{"datasourceB"}.findById(1) = Dave

最佳答案

我已经想出了几个解决方案来完成这项任务。

Option 1 - Multitenancy

在我看来, Multi-Tenancy 方法似乎是最简洁的方法,同时仍然允许每个租户拥有自己的数据库。

目录结构

org.company.project
- ApplicationMain
|_config
- DatasourceConfiguration
- WebMvcConfig
|_routing
- TenantContext
- TenantInterceptor
- TenantSourceRouter
|_domain
- Person
|_repository
|_ PersonRepository
|_web
-APIController

数据源配置

@Configuration
@EnableTransactionManagement
public 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;
}

}

域实体 - 人

@Entity
public 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 - 我决定添加一个全局拦截器,您可以在其中使用所需租户“ALBANY”或“BUFFALO”设置请求 header “X-TenantID”,而不必处理这个在 Controller 逐个操作的基础上。

@Component
public 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 注册拦截器

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TenantInterceptor());
}

}

APIController - 最后我们创建我们的 Controller ,我们将在其中访问我们的存储库。

@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();
}

}

应用程序.yml

company:
datasource:
driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
albany:
jdbc-url: ***
username: ***
password: ***
buffalo:
jdbc-url: ***
username: ***
password: ***

Option 2 - A more traditional multitenancy with multiple repositories

目录结构

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: ***

域实体 - 人

@Entity
public 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);
}
}

网络 Controller - 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();
}

}

关于java - 具有单个域对象和多个数据源的 Spring Boot,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54131562/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com