【问题标题】:Generic spring data repositories for two database connections用于两个数据库连接的通用 spring 数据存储库
【发布时间】:2022-01-05 18:28:38
【问题描述】:

我有一个应用程序需要连接到两个数据库,但两个数据库的实体重复。

我设法让应用程序连接并在两个数据库中保存实体,但我必须创建两个存储库,每个数据库一个,我发现这种方法不正确。

有一种更好的方法来进行这种管理,例如,我创建一个存储库并为它定义它应该基于一个标志使用哪个连接。

第一个数据库配置

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
basePackages = ["xxx.xxx.domains.portadapter.repository.firstRepository"],
entityManagerFactoryRef = "firstEntityManegerFactory",
transactionManagerRef = "firstTransactionManager"
)
class FirstDatabaseConfiguration {

@Bean(name = ["firstDatasource"])
@ConfigurationProperties(prefix = "first.datasource")
fun dataSource(): DataSource? {
    return DataSourceBuilder.create().build()
}

@Bean(name = ["firstEntityManegerFactory"])
fun firstEntityManegerFactory(
    builder: EntityManagerFactoryBuilder, @Qualifier("firstDatasource") dataSource: DataSource?
): LocalContainerEntityManagerFactoryBean? {
    return builder.dataSource(dataSource).packages("xxx").persistenceUnit("xxx")
        .build()
}

@Bean(name = ["firstTransactionManager"])
fun firstTransactionManager(
    @Qualifier("firstEntityManegerFactory") barEntityManagerFactory: EntityManagerFactory?
): PlatformTransactionManager? {
    return JpaTransactionManager(barEntityManagerFactory!!)
 }
}

第二个数据库配置

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
basePackages = ["xxx.xxx.domains.portadapter.repository.secondRepository"],
entityManagerFactoryRef = "secondEntityManegerFactory",
transactionManagerRef = "secondTransactionManeger"
)
class secondDatabaseConfiguration {

@Primary
@Bean(name = ["secondDatasource"])
@ConfigurationProperties(prefix = "spring.datasource")
fun dataSource(): DataSource? {
    return DataSourceBuilder.create().build()
}

@Primary
@Bean(name = ["secondEntityManegerFactory"])
fun secondEntityManegerFactory(
    builder: EntityManagerFactoryBuilder, @Qualifier("secondDatasource") dataSource: DataSource?
): LocalContainerEntityManagerFactoryBean? {
    return builder.dataSource(dataSource).packages("xxx").persistenceUnit("xxx")
        .build()
}

@Primary
@Bean(name = ["secondTransactionManager"])
fun secondTransactionManager(
    @Qualifier("secondEntityManegerFactory") barEntityManagerFactory: EntityManagerFactory?
): PlatformTransactionManager? {
     return JpaTransactionManager(barEntityManagerFactory!!)
 }
}

将实体持久保存在第一个数据库上的年龄组存储库

interface FirstAgeGroupsRepository : JpaRepository<AgeGroups, Int> {
fun findBySispacId(sispacId : String): Optional<AgeGroups>
}

将实体保存在第二个数据库上的年龄组存储库

interface SecondAgeGroupsRepository : JpaRepository<AgeGroups, Int> {
fun findBySispacId(sispacId : String): Optional<AgeGroups>
}

例如,在这个控制器中,我必须实例化每个存储库并通过条件定义我应该使用哪个存储库

@Service
class AgeGroupsController(
 @Autowired val scoobyAgeGroupRepository: ScoobyAgeGroupsRepository,
 @Autowired val fisiaAgeGroupRepository: FisiaAgeGroupsRepository
) {

fun saveAgeGroup(body: AgeGroupDto): ResponseDto {
    return try {
        val ageGroupFound: Optional<AgeGroups> = fisiaAgeGroupRepository.findBySispacId(body.sispacId)

        if (!ageGroupFound.isEmpty) {
            return ResponseDto(HttpStatus.FOUND, "Faixa etária ja existe, persistencia não realizada.")
        }

        val formatter = SimpleDateFormat("yyyy-MM-dd")
        val ageGroup = AgeGroups(
            body.ageGroupName, formatter.parse(body.createdAt), body.sispacId,
            formatter.parse(body.updatedAt)
        )
        body.ageGroupIndexDescription?.let { ageGroup.setAgeGroupIndexDescription(it) }

        fisiaAgeGroupRepository.save(ageGroup)

        ResponseDto(HttpStatus.OK, "Faixa etária criada com sucesso!")
    } catch (erro: Exception) {
        print(erro.message)
        ResponseDto(HttpStatus.BAD_REQUEST, erro.message)
    }
 }
}

如果有人知道任何可以帮助我改进此代码的教程或相关内容,因为如果有一天我想添加另一个数据库,我将不得不复制存储库,然后在我的每个控制器中再添加一个条件。

我愿意接受建议

【问题讨论】:

  • 您是如何得到两个等效数据库的?您不能将它们合并到单个数据库中吗?为什么您认为您将需要具有相同结构的第三个数据库?
  • 这是我工作的一家大型零售公司的旧数据库,当我必须与其他一些系统集成时,我必须访问该数据库并将数据保存在两者中。这不是我喜欢的方法,但这是我必须使用的方法。

标签: java spring spring-boot kotlin spring-data


【解决方案1】:

将此添加为答案,因为我没有足够的声誉来发表评论。以下是一些改进此代码的方法:

  • Spring 使用控制器/服务/存储库模式。为了使此代码更具可读性/可维护性,请将控制器逻辑切割成单独的 @Service 类。将控制器用于严格的前端逻辑。作为一般规则,如果您需要访问代码中的存储库,它很可能属于服务。
  • Nitpick:@Autowired 依赖项可以是私有的

编辑:删除微服务参考

【讨论】:

  • 关于微服务的问题没有任何内容。不是我不同意你的结论,而是OP的方法存在更严重的问题。
  • 顺便说一句,如果您编辑您的帖子,我可以用赞成票代替我的反对票,这样您就可以获得一些声誉并可以发表评论。所以禁止我改变它。
  • @AlexeyVeleshko 把微服务点去掉了,是这个意思吗?
  • 任何编辑都很好。
  • 感谢您的回复,但这并不是我想要的。我找到了更合适的解决方案,我会将其标记为答案。
【解决方案2】:

如果您使用所有额外方法创建单个存储库接口,然后在专用包中扩展它,以便不同的 JPA 配置可以从那里获取它们,那么您可以减少一些代码。

说,在 JPA 不搜索存储库的包中:

interface AgeGroupsRepository : JpaRepository<AgeGroups, Int> {
    fun findBySispacId(sispacId : String): Optional<AgeGroups>
}

在第一个数据库存储库的包中:

interface FirstAgeGroupsRepository : AgeGroupsRepository {
}

在第二个数据库存储库的包中:

interface SecondAgeGroupsRepository : AgeGroupsRepository {
}

但是,我认为您在描述中遗漏了一些重要的内容。实际上,您不仅需要单独的存储库,还需要单独的服务。这是因为您需要根据使用的存储库使用@Transactional("firstTransactionManager")@Transactional("secondTransactionManager") 来注释服务方法。因此,您要么放弃 Spring 事务管理并推出自己的服务,要么也复制该服务。

如您所见,Spring Boot 架构针对单一数据源用例进行了优化。由于您的用例不同,因此使用比 Spring Data JPA 更低级别的技术可能会更好:jOOQ、Spring JdbcTemplate 或普通 JDBC。当然,您将有更多可能消除代码重复。

【讨论】:

  • 我在寻找解决方案时可以看到这个问题。 Spring Boot 不能处理 N 个数据库真的很烦人。但是非常感谢您的时间和回复!我会把我的解决方案放在这个话题上
【解决方案3】:

事实证明,我需要的是使用多租户使我的应用程序根据标志连接到两个不同的数据库。

我使用本教程来解决我的问题: https://medium.com/swlh/multi-tenancy-implementation-using-spring-boot-hibernate-6a8e3ecb251a

正如@Alexey Veleshko 所说,spring boot 不是为处理 N 个数据库而设计的,因此需要一种更“原始”的方法

【讨论】:

    猜你喜欢
    • 2016-02-08
    • 1970-01-01
    • 2020-01-23
    • 2017-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-17
    • 2018-02-02
    相关资源
    最近更新 更多