DataSource
的 Spring Data JPA
spring
在一個 Spring Boot 專案中同時使用兩個以上的 DataSource
並為每個 DataSource
配置 Spring Data JPA。
DataSource
為 PostgreSQL,以 Apache Tomcat JDBC 為 connection pool。DataSource
為 MariaDB,以 Hikari 為 connection pool。├─ pom.xml
└─ src/
└─ main/
├─ java/
│ └─ tw/
│ └─ codethefuckup/
│ └─ multijpa/
│ ├─ Application.java
│ ├─ DatasourceConfiguration.java
│ ├─ PrimaryDatasourceConfiguration.java
│ ├─ SecondaryDatasourceConfiguration.java
│ ├─ WebMvcConfigurerImpl.java
│ ├─ controller/
│ │ └─ WelcomeController.java
│ ├─ entity/
│ │ ├─ primary/
│ │ | └─ MercedesBenz.java
│ │ └─ secondary/
│ │ └─ BayerischeMotorenWerke.java
│ └─ repository/
│ ├─ primary/
│ │ └─ MercedesRepository.java
│ └─ secondary/
│ └─ BmwRepository.java
└─ resources/
└─ application.properties
src/main/resources/application.properties
logging.level.root=${LOG_LEVEL:info}
server.compression.enabled=true
server.compression.min-response-size=1KB
server.error.whitelabel.enabled=false
server.servlet.register-default-servlet=true
server.tomcat.accesslog.enabled=true
server.tomcat.accesslog.pattern=combined
spring.devtools.restart.enabled=false
spring.application.name=multijpa
spring.jackson.serialization.indent-output=false
spring.output.ansi.enabled=always
# Hibernate/JPA 相關配置
spring.jpa.hibernate.ddl-auto=none
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.open-in-view=false
spring.jpa.show-sql=true
# 主要 DataSource 的配置
datasource.primary.driverClassName=${PRIMARY_DRIVER_CLASS_NAME}
datasource.primary.password=${PRIMARY_PASSWORD}
datasource.primary.port=${PRIMARY_PORT}
datasource.primary.type=org.apache.tomcat.jdbc.pool.DataSource
datasource.primary.url=jdbc:postgresql://${PRIMARY_HOST}:${PRIMARY_PORT}/${PRIMARY_CATALOG}?user=${PRIMARY_USERNAME}&password=${PRIMARY_PASSWORD}
datasource.primary.username=${PRIMARY_USERNAME}
# 次要 DataSource 的配置
datasource.secondary.driverClassName=${SECONDARY_DRIVER_CLASS_NAME}
datasource.secondary.password=${SECONDARY_PASSWORD}
datasource.secondary.port=${SECONDARY_PORT}
datasource.secondary.type=org.mariadb.jdbc.MariaDbPoolDataSource
datasource.secondary.url=jdbc:mariadb://${SECONDARY_HOST}:${SECONDARY_PORT}/${SECONDARY_CATALOG}?user=${SECONDARY_USERNAME}&password=${SECONDARY_PASSWORD}
datasource.secondary.username=${SECONDARY_USERNAME}
DataSource
共用的配置若有共用的設定(如 connection pools),若無則可省略。
public class DatasourceConfiguration {
public static final int INITIAL_SIZE = Integer.parseInt(
System.getenv("DATASOURCE_INITIAL_SIZE")
);
public static final int MAX_ACTIVE = Integer.parseInt(
System.getenv("DATASOURCE_MAX_ACTIVE")
);
public static final int MAX_IDLE = Integer.parseInt(
System.getenv("DATASOURCE_MAX_IDLE")
);
public static final int MAX_WAIT = Integer.parseInt(
System.getenv("DATASOURCE_MAX_WAIT")
);
public static final int MIN_IDLE = Integer.parseInt(
System.getenv("DATASOURCE_MIN_IDLE")
);
public static final boolean REMOVE_ABANDONED = Boolean.parseBoolean(
System.getenv("DATASOURCE_MIN_IDLE")
);
public static final boolean ROLLBACK_ON_RETURN = Boolean.parseBoolean(
System.getenv("DATASOURCE_MIN_IDLE")
);
public static final String VALIDATION_QUERY = System.getenv(
"DATASOURCE_VALIDATION_QUERY"
);
}
DataSource
的配置
@Configuration
@EnableJpaRepositories(
basePackages = {"tw.codethefuckup.multijpa.repository.primary"},
entityManagerFactoryRef = "primaryEntityManagerFactory",
transactionManagerRef = "primaryTransactionManager"
)
@EnableTransactionManagement
public class PrimaryDatasourceConfiguration {
private static final Logger LOGGER = LoggerFactory.getLogger(PrimaryDatasourceConfiguration.class);
/**
* 從系統環境變數取得主 database 名稱
*/
private static final String DATASOURCE_CATALOG = System.getenv("PRIMARY_CATALOG");
/**
* 從 application.properties 讀取主 DataSource 的設定
*
* @return 主 DataSource 的設定
*/
@Bean
@ConfigurationProperties("datasource.primary")
@Primary
public DataSourceProperties primaryDataSourceProperties() {
return new DataSourceProperties();
}
/**
* 初始化主 DataSource
*
* @return 主 DataSource
*/
@Bean(name = "primaryDataSource")
@ConfigurationProperties("datasource.primary.configuration")
@Primary
public DataSource primaryDataSource() {
org.apache.tomcat.jdbc.pool.DataSource dataSource = primaryDataSourceProperties().
initializeDataSourceBuilder().
type(org.apache.tomcat.jdbc.pool.DataSource.class).
build();
dataSource.setDefaultAutoCommit(false);
dataSource.setDefaultCatalog(DATASOURCE_CATALOG);
dataSource.setInitialSize(DatasourceConfiguration.INITIAL_SIZE);
dataSource.setMaxActive(DatasourceConfiguration.MAX_ACTIVE);
dataSource.setMaxIdle(DatasourceConfiguration.MAX_IDLE);
dataSource.setMaxWait(DatasourceConfiguration.MAX_WAIT);
dataSource.setMinIdle(DatasourceConfiguration.MIN_IDLE);
dataSource.setRemoveAbandoned(DatasourceConfiguration.REMOVE_ABANDONED);
dataSource.setRollbackOnReturn(DatasourceConfiguration.ROLLBACK_ON_RETURN);
dataSource.setValidationQuery(DatasourceConfiguration.VALIDATION_QUERY);
return dataSource;
}
/**
* 初始化主 DataSource 的 EntityManager
*
* @param entityManagerFactoryBuilder 供主 EntityManager 用的工廠建造器
* @return 主 EntityManager 工廠
*/
@Bean(name = "primaryEntityManagerFactory")
@Primary
public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(
EntityManagerFactoryBuilder entityManagerFactoryBuilder
) {
return entityManagerFactoryBuilder.
dataSource(primaryDataSource()).
packages("tw.codethefuckup.multijpa.entity.primary").
build();
}
/**
* 初始化主 EntityManager 的交易管理
*
* @param localContainerEntityManagerFactoryBean 主 EntityManager 工廠
* @return 主 EntityManager 的交易管理
*/
@Bean
@Primary
public PlatformTransactionManager primaryTransactionManager(
@Qualifier("primaryEntityManagerFactory") final LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean
) {
return new JpaTransactionManager(
localContainerEntityManagerFactoryBean.getObject()
);
}
}
DataSource
的配置
@Configuration
@EnableJpaRepositories(
basePackages = {"tw.codethefuckup.multijpa.repository.secondary"},
entityManagerFactoryRef = "secondaryEntityManagerFactory",
transactionManagerRef = "secondaryTransactionManager"
)
@EnableTransactionManagement
public class SecondaryDatasourceConfiguration {
private static final Logger LOGGER = LoggerFactory.getLogger(SecondaryDatasourceConfiguration.class);
/**
* 從系統環境變數取得次要 database 名稱
*/
private static final String DATASOURCE_CATALOG = System.getenv("SECONDARY_CATALOG");
/**
* 從 application.properties 讀取次要 DataSource 的設定
*
* @return 次 DataSource 的設定
*/
@Bean
@ConfigurationProperties("datasource.secondary")
public DataSourceProperties secondaryDataSourceProperties() {
return new DataSourceProperties();
}
/**
* 初始化次要 DataSource
*
* @return 次要 DataSource
*/
@Bean(name = "secondaryDataSource")
@ConfigurationProperties("datasource.secondary.configuration")
public DataSource secondaryDataSource() {
HikariDataSource dataSource = secondaryDataSourceProperties().
initializeDataSourceBuilder().
type(HikariDataSource.class).
build();
dataSource.setAutoCommit(false);
dataSource.setCatalog(DATASOURCE_CATALOG);
dataSource.setIdleTimeout(DatasourceConfiguration.MAX_IDLE);
dataSource.setMaximumPoolSize(DatasourceConfiguration.MAX_ACTIVE);
dataSource.setMaxLifetime(DatasourceConfiguration.MAX_WAIT);
dataSource.setMinimumIdle(DatasourceConfiguration.MIN_IDLE);
return secondaryDataSourceProperties().
initializeDataSourceBuilder().
type(HikariDataSource.class).
build();
}
/**
* 初始化次要 DataSource 的 EntityManager
*
* @param entityManagerFactoryBuilder 供次要 EntityManager 用的工廠建造器
* @return 次要 EntityManager 工廠
*/
@Bean(name = "secondaryEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory(
EntityManagerFactoryBuilder entityManagerFactoryBuilder
) {
return entityManagerFactoryBuilder.
dataSource(secondaryDataSource()).
packages("tw.codethefuckup.multijpa.entity.secondary").
build();
}
/**
* 初始化次要 EntityManager 的交易管理
*
* @param localContainerEntityManagerFactoryBean 次要 EntityManager 工廠
* @return 次要 EntityManager 的交易管理
*/
@Bean
public PlatformTransactionManager secondaryTransactionManager(
@Qualifier("secondaryEntityManagerFactory") final LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean
) {
return new JpaTransactionManager(
localContainerEntityManagerFactoryBean.getObject()
);
}
}
@Configuration
public class WebMvcConfigurerImpl implements WebMvcConfigurer {
private static final Logger LOGGER = LoggerFactory.getLogger(WebMvcConfigurerImpl.class);
@Bean(name = "primaryJdbcTemplate")
public JdbcTemplate primaryJdbcTemplate(
@Qualifier("primaryDataSource") DataSource dataSource
) {
return new JdbcTemplate(dataSource);
}
@Bean(name = "secondaryJdbcTemplate")
public JdbcTemplate secondaryJdbcTemplate(
@Qualifier("secondaryDataSource") DataSource dataSource
) {
return new JdbcTemplate(dataSource);
}
}
src/main/java/tw/codethefuckup/multijpa/controller/WelcomeController.java
@RestController
@RequestMapping("/")
public class WelcomeController {
private static final Logger LOGGER = LoggerFactory.getLogger(WelcomeController.class);
@Autowired
private BmwRepository bmwRepository;
@Autowired
private MercedesRepository mercedesRepository;
@GetMapping("benz")
@ResponseBody
List<MercedesBenz> somethingFromPrimaryDataSource() {
return mercedesRepository.findAll();
}
@GetMapping("bmw")
@ResponseBody
List<BayerischeMotorenWerke> somethingFromSecondaryDataSource() {
return bmwRepository.findAll();
}
}
:::info Abbreviations EB = AWS Elastic Beanstalk ECS = Amazon Elastic Container Service ::: 使用 Docker Compose 管理映像 docker-compose.yml Dockerfile
Jun 2, 2025Uninstall old versions for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; \ do sudo apt remove $pkg; done Install using the apt repository Add Docker's official GPG key sudo apt update && sudo apt install -y \ ca-certificates \ curl sudo install -m 0755 -d /etc/apt/keyrings \
Mar 19, 2025Uninstall old versions for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; \ do sudo apt-get remove $pkg; done Install using the apt repository Add Docker's official GPG key sudo apt update && sudo apt install -y \ ca-certificates \ curl sudo install -m 0755 -d /etc/apt/keyrings \
Mar 19, 2025Amazon Linux 2023 下安裝
Mar 15, 2025or
By clicking below, you agree to our terms of service.
New to HackMD? Sign up