通过外部化配置提供的命名策略

时间:2017-08-24 18:12:17

标签: java spring hibernate jpa spring-boot

注意:这是一个Spring / Hibernate问题。我碰巧使用Kotlin,但也欢迎Java答案。

JPA实体具有以具有常量表和目录名称:

User.kt

@Entity
@Table(name="users", catalog="auth")
data class User(
        @Id
        val username: String,
        var password: String
)

这对我来说是个坏消息。

我的MySQL数据库托管同一应用程序堆栈的几个不同实例。结果是:我有各种各样的模式,由环境命名:

SHOW DATABASES;
sandbox_billing
sandbox_auth
staging_billing
staging_auth
preproduction_billing
preproduction_auth

每个应用程序上下文都有一个数据源,可以访问两个目录:*_billing(对于域实体)和*_auth(对于auth信息)。

我的应用程序有一个可配置的映射com.stackoverflow.persistence.catalog-overrides,其中可以指定要用于该特定应用程序实例的目录名称:

application.yml

spring:
  datasource:
    url: 'jdbc:mysql://mycooldatabase.com:3306'
com.stackoverflow:
  persistence:
    catalog-overrides:
      billing: sandbox_billing
      auth: sandbox_auth

此配置填充到POKO:

PersistenceProperties.kt

@Component
@ConfigurationProperties("com.stackoverflow.persistence")
@NoArgConstructor
data class PersistenceProperties(
        var catalogOverrides: Map<String, String>
)

希望我可以根据Spring应用程序配置中指定的覆盖映射实现可以重命名目录的PhysicalNamingStrategy:

PhysicalNamingStrategyImpl.kt

@Component
class PhysicalNamingStrategyImpl(
        val persistenceProperties : PersistenceProperties
) : PhysicalNamingStrategyStandardImpl(), Serializable {

    companion object {
        val INSTANCE = PhysicalNamingStrategyStandardImpl()
    }

    override fun toPhysicalCatalogName(name: Identifier?, context: JdbcEnvironment?): Identifier? {
        if (name == null) {
            return null
        }
        val nominalName = name.text
        var parsedName = if(persistenceProperties.catalogOverrides.containsKey(nominalName)) {
            persistenceProperties.catalogOverrides.get(nominalName)
        } else {
            nominalName
        }
        return Identifier(parsedName, name.isQuoted)
    }
}

我遇到的问题是Hibernate负责构建NamingStrategy,因此很难将Spring属性注入其中。

即。我不可能使用通常的技术:

application.yml (假设):

spring.jpa.hibernate.naming.physical-strategy: com.stackoverflow.config.PhysicalNamingStrategyImpl

因为这不使用Spring的依赖注入器(因此我无法获得PersistenceProperties的实例)。

尝试使用服务定位器来获取依赖项:

PhysicalNamingStrategyImpl.kt,ApplicationContextProvider.kt (假设):

@Component
class PhysicalNamingStrategyImpl : PhysicalNamingStrategyStandardImpl(), Serializable {

    private val persistenceProperties : PersistenceProperties = ApplicationContextProvider.CONTEXT.getBean(PersistenceProperties::class.java)

    //...

@Component
class ApplicationContextProvider: ApplicationContextAware {
    companion object {
        lateinit var CONTEXT : ApplicationContext private set
    }

    override fun setApplicationContext(applicationContext: ApplicationContext?) {
        CONTEXT = applicationContext!!
    }
}

但是Hibernate很早就构建了它的PhysicalNamingStrategy - 在setApplicationContext()触发之前。所以我无法使用服务定位器来获取应用程序上下文。

所以,我需要一种方法来使用Spring注入PhysicalNamingStrategy。我走近了:

HibernateConfig.kt

@Configuration
class HibernateConfig(
        val physicalNamingStrategyImpl: PhysicalNamingStrategyImpl
) {
    @Bean
    fun sessionFactory(dataSource: DataSource): SessionFactory {
        val sessionFactoryBuilder = LocalSessionFactoryBuilder(dataSource)
        sessionFactoryBuilder
                .setPhysicalNamingStrategy(physicalNamingStrategyImpl)
        return sessionFactoryBuilder.buildSessionFactory()
    }
}

但这在启动过程中起得太晚了。在构造hibernate SessionFactory bean之前,我的所有目录都由默认的SpringPhysicalNamingStrategy处理。

所以:如何在处理任何目录之前将Spring属性注入PhysicalNamingStrategy

我使用的是Spring Boot 1.5.4和Hibernate 5.0.12。

0 个答案:

没有答案