通过外部配置通知命名策略

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