春季 JPA ߝ具有相同存储库的多个数据库

发布于 2025-01-16 22:31:48 字数 469 浏览 5 评论 0原文

按照此示例,我已将 Spring JPA 配置为使用多个数据源。 Spring JPA – 多个数据库

它按预期工作,但因为我想使用不同的数据源但使用相同的存储库存在问题。当我尝试在我的服务上使用“声明式事务管理”并指定我将使用主事务或辅助事务的事务时,事务注释会忽略它。所以在这种情况下它使用第二个。但是,两个 bean“PlatformTransactionManager”均已创建,但在“@Transactional”中使用时,我无法使事务与我指定的 bean 一起使用。因此,@Transactional 似乎忽略了 bean 名称,因为它们具有相同的存储库。当我尝试这样做时,有什么方法可以使用声明性事务吗?正如我所见,这可以通过程序化事务管理来完成,但由于我只使用声明性事务,因此更改服务上的整个代码将花费我的成本。

I have configured Spring JPA to work with multiple data sources following this example.
Spring JPA – Multiple Databases

It works as expected but since I want to use different data sources but with the same repositories there is an issue. When I try to use "Declarative Transaction Management" on my services and specify which transaction I am going to use the primary transaction or secondary one the transaction annotation is ignoring it. So in this case it is using the second one. However, both of the beans "PlatformTransactionManager" are created but when it comes to using in "@Transactional" I am not able to make the transaction work with the bean I have specified. So it seems that @Transactional is ignoring the bean name since they have the same repositories. Is there any way how can I use declarative transactions as I am trying to do it? As I have seen it can be done with Programmatic Transaction Management but it will cost me to change the whole code on my services since I have been using only declarative transactions.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

源来凯始玺欢你 2025-01-23 22:31:48

理想情况下,最好有两个独立的微服务连接到两个不同的数据源。

Ideally, it is better to have two separate microservices connecting to two different data sources.

我一向站在原地 2025-01-23 22:31:48

在我们的例子中,我们还有两个数据库,都使用 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 数据库,在内存中创建,根据您的用例进行调整):

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

In our case, we also have two databases, both using JPA and repositories. However, they do use different repositories, but I think our approach should also work in your case. We define an EntityManagerFactory and a TransactionManager per database. I cannot confirm this works, but I believe it should.

Primary Datasource:

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

Secondary Datasource (SQLite Database, created in-memory, adjust to your 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);
    }
}
别在捏我脸啦 2025-01-23 22:31:48

答案找到了:可以通过扩展spring的AbstractRoutingDataSource类来完成。

The answer was found: It can be done by extending AbstractRoutingDataSource class of spring.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文