Spring Boot 2 with Multiple DataSource
有时候,即使拥有最好的数据库(PostgreSQL,Oracle,Sql等),调优也无法像应用程序级别那样将读写分开。
具有多个数据源的Spring Boot 2.2.2
Postgres Setup
对于此演示,您需要2个单独的Postgres数据库,其中一个作为Master,另一个作为副本。
为简单起见,只需运行:
docker-组成--force-recreate
的docker-compose.yml已经在包含2个不同端口的2个Postgresql的项目中,演示数据库
Note: you can always uninstall it as: docker-compose down if you needed to.
you can clone this project from my github on https://github.com/ehsaniara/spring-boot-multi-data-source
Spring Boot Setup
From https://start.spring.io/ select web, data-jpa, lombok, postgresDriver
生成并下载zip文件后,您应该拥有与以下类似的POM文件:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
对于这个演示,我使用HikariDataSource作为Spring Boot 2.2.2的默认连接池库 我们需要有2个单独的DataSource和EntityManager,一个用于Writes(主服务器/主服务器),另一个用于Reads(从机服务器/第二服务器)。
spring:
datasource-write:
driver-class-name: org.postgresql.Driver
jdbc-url: jdbc:postgresql://localhost:5432/demo
username: 'postgres_user_for_db_write'
password: 'you_password'
platform: postgresql
hikari:
idle-timeout: 10000
maximum-pool-size: 10
minimum-idle: 5
pool-name: WriteHikariPool
datasource-read:
driver-class-name: org.postgresql.Driver
jdbc-url: jdbc:postgresql://localhost:5433/demo
username: 'postgres_user_for_db_read'
password: 'you_password'
platform: postgresql
hikari:
idle-timeout: 10000
maximum-pool-size: 10
minimum-idle: 5
pool-name: ReadHikariPool
如您所见,我有2个数据源,分别是:具有自己的凭据的datasource-write和datasource-read。
WriteDB的数据源配置:
@Configuration
@ConfigurationProperties("spring.datasource-write")
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactoryWrite",
transactionManagerRef = "transactionManagerWrite",
basePackages = {"com.ehsaniara.multidatasource.repository.writeRepository"}
)
public class DataSourceConfigWrite extends HikariConfig {
public final static String PERSISTENCE_UNIT_NAME = "write";
@Bean
public HikariDataSource dataSourceWrite() {
return new HikariDataSource(this);
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryWrite(
final HikariDataSource dataSourceWrite) {
return new LocalContainerEntityManagerFactoryBean() {{
setDataSource(dataSourceWrite);
setPersistenceProviderClass(HibernatePersistenceProvider.class);
setPersistenceUnitName(PERSISTENCE_UNIT_NAME);
setPackagesToScan(MODEL_PACKAGE);
setJpaProperties(JPA_PROPERTIES);
}};
}
@Bean
public PlatformTransactionManager transactionManagerWrite(EntityManagerFactory entityManagerFactoryWrite) {
return new JpaTransactionManager(entityManagerFactoryWrite);
}
}
ReadDB的数据源配置:
@Configuration
@ConfigurationProperties("spring.datasource-read")
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactoryRead",
transactionManagerRef = "transactionManagerRead",
basePackages = {"com.ehsaniara.multidatasource.repository.readRepository"}
)
public class DataSourceConfigRead extends HikariConfig {
public final static String PERSISTENCE_UNIT_NAME = "read";
@Bean
public HikariDataSource dataSourceRead() {
return new HikariDataSource(this);
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryRead(
final HikariDataSource dataSourceRead) {
return new LocalContainerEntityManagerFactoryBean() {{
setDataSource(dataSourceRead);
setPersistenceProviderClass(HibernatePersistenceProvider.class);
setPersistenceUnitName(PERSISTENCE_UNIT_NAME);
setPackagesToScan(MODEL_PACKAGE);
setJpaProperties(JPA_PROPERTIES);
}};
}
@Bean
public PlatformTransactionManager transactionManagerRead(EntityManagerFactory entityManagerFactoryRead) {
return new JpaTransactionManager(entityManagerFactoryRead);
}
}
读写存储库应放在单独的程序包中:
写:com.ehsaniara.multidatasource.repository.writeRepository
读:com.ehsaniara.multidatasource.repository.readRepository
您还需要设置:
public final static String MODEL_PACKAGE = "com.ehsaniara.multidatasource.model";
public final static Properties JPA_PROPERTIES = new Properties() {{
put("hibernate.dialect", "org.hibernate.dialect.PostgreSQL10Dialect");
put("hibernate.hbm2ddl.auto", "update");
put("hibernate.ddl-auto", "update");
put("show-sql", "true");
}};
和实际的逻辑在服务层中:
@Service
public class CustomerServiceImpl implements CustomerService {
private final CustomerReadRepository customerReadRepository;
private final CustomerWriteRepository customerWriteRepository;
public CustomerServiceImpl(CustomerReadRepository customerReadRepository, CustomerWriteRepository customerWriteRepository) {
this.customerReadRepository = customerReadRepository;
this.customerWriteRepository = customerWriteRepository;
}
public Optional<Customer> getCustomer(Long id) {
return customerReadRepository.findById(id);
}
public Customer createCustomer(Customer customer) {
Assert.notNull(customer, "Invalid customer");
Assert.isNull(customer.getId(), "customer id should be null");
Assert.notNull(customer.getName(), "Invalid customer name");
return customerWriteRepository.save(customer);
}
public Customer updateCustomer(Customer customer) {
Assert.notNull(customer, "Invalid customer");
Assert.notNull(customer.getId(), "Invalid customer id");
return customerWriteRepository.save(customer);
}
}
现在,如果运行此行,您将在DB1中创建客户:
curl -H "Content-Type: application/json" --request POST --data '{"name":"Jay"}' http://localhost:8080/customer
要么
curl -H "Content-Type: application/json" --request PUT --data '{"id":1 , "name":"Jay ehsaniara"}' http://localhost:8080/customer
但是,如果运行此行,则可以从DB2获取数据:
curl --request GET http://localhost:8080/customer/1
注意:您需要在DB2中手动插入客户,因为它没有前客户。 并且我们还没有设置Postgres Replication