Spring JPA – 具有相同存储库的多个数据库
Spring JPA – Multiple Databases with the same Repositories
我已按照此示例配置 Spring JPA 以使用多个数据源。
Spring JPA – Multiple Databases
它按预期工作,但由于我想使用不同的数据源但使用相同的存储库,所以出现了问题。当我尝试在我的服务上使用“声明式事务管理”并指定我将使用主要事务或次要事务时,事务注释忽略了它。所以在这种情况下,它使用的是第二个。但是,创建了两个 bean“PlatformTransactionManager”,但是当涉及到在“@Transactional”中使用时,我无法使事务与我指定的 bean 一起工作。因此,@Transactional 似乎忽略了 bean 名称,因为它们具有相同的存储库。有什么办法可以让我在尝试时使用声明性交易吗?正如我所看到的,它可以通过程序化事务管理来完成,但由于我一直只使用声明式事务,因此更改服务的整个代码会花费我很多钱。
在我们的例子中,我们还有两个数据库,都使用 JPA 和存储库。但是,他们确实使用不同的存储库,但我认为我们的方法也适用于您的情况。我们为每个数据库定义一个 EntityManagerFactory
和一个 TransactionManager
。我无法确认这是否有效,但我相信它应该有效。
主要数据源:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
basePackages = {"ch.sac.data.primary.repository"},
entityManagerFactoryRef = "entityManagerFactory",
transactionManagerRef = "transactionManager")
public class ImporterPrimaryDataSourceConfiguration {
@Primary
@Bean(name = "dataSourceProperties")
@ConfigurationProperties("spring.datasource")
public DataSourceProperties dataSourceProperties() {
return new DataSourceProperties();
}
@Primary
@Bean(name = "primaryDataSource")
public DataSource dataSource(final DataSourceProperties dataSourceProperties) {
return dataSourceProperties
.initializeDataSourceBuilder()
.type(HikariDataSource.class)
.build();
}
@Primary
@Bean
public Flyway primaryFlyway(
final DataSource dataSource,
@Value("${spring.flyway.locations}") final String[] flywayLocations,
@Value("${spring.flyway.validate-on-migrate:true}")
final boolean flywayValidateOnMigrate) {
final var flyway =
Flyway.configure()
.dataSource(dataSource)
.locations(flywayLocations)
.validateOnMigrate(flywayValidateOnMigrate)
.load();
flyway.migrate();
return flyway;
}
@Primary
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
final EntityManagerFactoryBuilder entityManagerFactoryBuilder,
final DataSource dataSource) {
final var jpaProperties =
Map.ofEntries(
Map.entry(
"hibernate.dialect",
"org.hibernate.spatial.dialect.postgis.PostgisDialect"));
return entityManagerFactoryBuilder
.dataSource(dataSource)
.packages("ch.sac.model.primary.entity")
.persistenceUnit("dataSource")
.properties(jpaProperties)
.build();
}
@Primary
@Bean
public PlatformTransactionManager transactionManager(
final EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
辅助数据源(SQLite 数据库,已创建 in-memory,调整为您的 use-case):
@Configuration
@EnableJpaRepositories(
basePackages = {"ch.sac.data.sqlite.repository"},
entityManagerFactoryRef = "sqliteEntityManagerFactory",
transactionManagerRef = "sqliteTransactionManager")
public class ImporterSQLiteDataSourceConfiguration {
@Bean(name = "sqliteDBFilePath")
public String sqliteDBFilePath() throws IOException {
return TempFileManager.createEmptyTempFile("metadata.sqlite").toString();
}
@Bean(name = "sqliteDataSourceProperties")
public DataSourceProperties sqliteDataSourceProperties() {
return new DataSourceProperties();
}
@Bean(name = "sqliteDataSource")
public DataSource sqliteDataSource(
@Qualifier("sqliteDataSourceProperties")
final DataSourceProperties sqliteDataSourceProperties,
final String sqliteDBFilePath) {
return sqliteDataSourceProperties
.initializeDataSourceBuilder()
.driverClassName("org.sqlite.JDBC")
.url("jdbc:sqlite:" + sqliteDBFilePath)
.type(HikariDataSource.class)
.build();
}
@Bean
public Flyway sqliteFlyway(
@Qualifier("sqliteDataSource") final DataSource sqliteDataSource,
@Value("${sqlite.flyway.locations}") final String[] flywayLocations) {
final var flyway =
Flyway.configure().dataSource(sqliteDataSource).locations(flywayLocations).load();
flyway.migrate();
return flyway;
}
@Bean(name = "sqliteEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean sqliteEntityManagerFactory(
final EntityManagerFactoryBuilder sqliteEntityManagerFactoryBuilder,
@Qualifier("sqliteDataSource") final DataSource sqliteDataSource) {
final var sqliteJpaProperties =
Map.ofEntries(
Map.entry(
"hibernate.dialect", "org.sqlite.hibernate.dialect.SQLiteDialect"));
return sqliteEntityManagerFactoryBuilder
.dataSource(sqliteDataSource)
.packages("ch.sac.model.sqlite.entity")
.persistenceUnit("sqliteDataSource")
.properties(sqliteJpaProperties)
.build();
}
@Bean(name = "sqliteTransactionManager")
public PlatformTransactionManager sqliteTransactionManager(
@Qualifier("sqliteEntityManagerFactory")
final EntityManagerFactory sqliteEntityManagerFactory) {
return new JpaTransactionManager(sqliteEntityManagerFactory);
}
}
找到答案:可以通过扩展 spring 的 AbstractRoutingDataSource class 来完成。
理想情况下,最好有两个独立的微服务连接到两个不同的数据源。
我已按照此示例配置 Spring JPA 以使用多个数据源。 Spring JPA – Multiple Databases
它按预期工作,但由于我想使用不同的数据源但使用相同的存储库,所以出现了问题。当我尝试在我的服务上使用“声明式事务管理”并指定我将使用主要事务或次要事务时,事务注释忽略了它。所以在这种情况下,它使用的是第二个。但是,创建了两个 bean“PlatformTransactionManager”,但是当涉及到在“@Transactional”中使用时,我无法使事务与我指定的 bean 一起工作。因此,@Transactional 似乎忽略了 bean 名称,因为它们具有相同的存储库。有什么办法可以让我在尝试时使用声明性交易吗?正如我所看到的,它可以通过程序化事务管理来完成,但由于我一直只使用声明式事务,因此更改服务的整个代码会花费我很多钱。
在我们的例子中,我们还有两个数据库,都使用 JPA 和存储库。但是,他们确实使用不同的存储库,但我认为我们的方法也适用于您的情况。我们为每个数据库定义一个 EntityManagerFactory
和一个 TransactionManager
。我无法确认这是否有效,但我相信它应该有效。
主要数据源:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
basePackages = {"ch.sac.data.primary.repository"},
entityManagerFactoryRef = "entityManagerFactory",
transactionManagerRef = "transactionManager")
public class ImporterPrimaryDataSourceConfiguration {
@Primary
@Bean(name = "dataSourceProperties")
@ConfigurationProperties("spring.datasource")
public DataSourceProperties dataSourceProperties() {
return new DataSourceProperties();
}
@Primary
@Bean(name = "primaryDataSource")
public DataSource dataSource(final DataSourceProperties dataSourceProperties) {
return dataSourceProperties
.initializeDataSourceBuilder()
.type(HikariDataSource.class)
.build();
}
@Primary
@Bean
public Flyway primaryFlyway(
final DataSource dataSource,
@Value("${spring.flyway.locations}") final String[] flywayLocations,
@Value("${spring.flyway.validate-on-migrate:true}")
final boolean flywayValidateOnMigrate) {
final var flyway =
Flyway.configure()
.dataSource(dataSource)
.locations(flywayLocations)
.validateOnMigrate(flywayValidateOnMigrate)
.load();
flyway.migrate();
return flyway;
}
@Primary
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
final EntityManagerFactoryBuilder entityManagerFactoryBuilder,
final DataSource dataSource) {
final var jpaProperties =
Map.ofEntries(
Map.entry(
"hibernate.dialect",
"org.hibernate.spatial.dialect.postgis.PostgisDialect"));
return entityManagerFactoryBuilder
.dataSource(dataSource)
.packages("ch.sac.model.primary.entity")
.persistenceUnit("dataSource")
.properties(jpaProperties)
.build();
}
@Primary
@Bean
public PlatformTransactionManager transactionManager(
final EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
辅助数据源(SQLite 数据库,已创建 in-memory,调整为您的 use-case):
@Configuration
@EnableJpaRepositories(
basePackages = {"ch.sac.data.sqlite.repository"},
entityManagerFactoryRef = "sqliteEntityManagerFactory",
transactionManagerRef = "sqliteTransactionManager")
public class ImporterSQLiteDataSourceConfiguration {
@Bean(name = "sqliteDBFilePath")
public String sqliteDBFilePath() throws IOException {
return TempFileManager.createEmptyTempFile("metadata.sqlite").toString();
}
@Bean(name = "sqliteDataSourceProperties")
public DataSourceProperties sqliteDataSourceProperties() {
return new DataSourceProperties();
}
@Bean(name = "sqliteDataSource")
public DataSource sqliteDataSource(
@Qualifier("sqliteDataSourceProperties")
final DataSourceProperties sqliteDataSourceProperties,
final String sqliteDBFilePath) {
return sqliteDataSourceProperties
.initializeDataSourceBuilder()
.driverClassName("org.sqlite.JDBC")
.url("jdbc:sqlite:" + sqliteDBFilePath)
.type(HikariDataSource.class)
.build();
}
@Bean
public Flyway sqliteFlyway(
@Qualifier("sqliteDataSource") final DataSource sqliteDataSource,
@Value("${sqlite.flyway.locations}") final String[] flywayLocations) {
final var flyway =
Flyway.configure().dataSource(sqliteDataSource).locations(flywayLocations).load();
flyway.migrate();
return flyway;
}
@Bean(name = "sqliteEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean sqliteEntityManagerFactory(
final EntityManagerFactoryBuilder sqliteEntityManagerFactoryBuilder,
@Qualifier("sqliteDataSource") final DataSource sqliteDataSource) {
final var sqliteJpaProperties =
Map.ofEntries(
Map.entry(
"hibernate.dialect", "org.sqlite.hibernate.dialect.SQLiteDialect"));
return sqliteEntityManagerFactoryBuilder
.dataSource(sqliteDataSource)
.packages("ch.sac.model.sqlite.entity")
.persistenceUnit("sqliteDataSource")
.properties(sqliteJpaProperties)
.build();
}
@Bean(name = "sqliteTransactionManager")
public PlatformTransactionManager sqliteTransactionManager(
@Qualifier("sqliteEntityManagerFactory")
final EntityManagerFactory sqliteEntityManagerFactory) {
return new JpaTransactionManager(sqliteEntityManagerFactory);
}
}
找到答案:可以通过扩展 spring 的 AbstractRoutingDataSource class 来完成。
理想情况下,最好有两个独立的微服务连接到两个不同的数据源。