通过外部配置通知命名策略
注意:这是一个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
(用于身份验证信息)。
我的应用程序有一个可配置的映射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> )
希望是我可以实现一个PhysicalNamingStrategy,可以重命名目录,基于我的Spring应用程序配置中指定的覆盖映射:
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() } }
但是这在启动过程中太晚了。 我的所有目录都是由默认的SpringPhysicalNamingStrategy处理的,在我的hibernate SessionFactory bean被构建之前。
那么: 在处理任何目录之前 ,如何将Spring属性注入到PhysicalNamingStrategy中 ?
我正在使用Spring Boot 1.5.4和Hibernate 5.0.12。