注意:这是一个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。