Consider the following foreign key relationship between the user and email tables, which are part of different MySQL schemas USER and EMAIL, respectively, of the same MySQL instance. See image for an illustration.
For simplicity, I want to represent this data model in Spring JPA/Hibernate using a unidirectional one-to-one association. For the setup of Spring, I followed this Baeldung resource. The User model class in Kotlin reads
// User.kt
package com.networkapplications.test.model.user
@Entity
@Table(name = "user", catalog="USER", schema ="USER")
@SecondaryTable(name = "email", catalog = "EMAIL", schema = "EMAIL")
data class User(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(nullable = false, unique = true)
val id: Long = 0,
val name: String,
@OneToOne(targetEntity = Email::class, cascade = [CascadeType.ALL])
@JoinColumn(name = "email_id", table="email", referencedColumnName = "id")
val email: Email,
)
and the Email one similarly
// Email.kt
package com.networkapplications.test.model.email
@Entity
@Table(name = "email", catalog="EMAIL", schema ="EMAIL")
data class Email(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(nullable = false, unique = true)
val id: Long = 0,
val name: String,
)
The persistence configuration for the user context (and analogously the email context) is
abstract class AbstractPersistenceAutoConfiguration {
@Bean
@ConfigurationProperties("spring.jpa")
open fun jpaProperties(): JpaProperties {
return JpaProperties()
}
fun getCommonProperties(jpaProperties: JpaProperties): Map<String, String> =
jpaProperties.properties + mapOf(
"hibernate.physical_naming_strategy" to CustomPhysicalNamingStrategy::class.java.name
)
}
const val userModelPackage: String = "com.networkapplications.test.model.user"
const val userPersistencePackage: String = "com.networkapplications.test.persistence.user"
@Configuration
@PropertySource("classpath:persistence-multiple-db-boot.properties")
@EnableJpaRepositories(
basePackages = [userModelPackage, userPersistencePackage],
entityManagerFactoryRef = "userEntityManagerFactory",
transactionManagerRef = "userTransactionManager"
)
class PersistenceMondAutoConfiguration : AbstractPersistenceAutoConfiguration() {
@Bean
@ConfigurationProperties("app.datasource.user")
fun userDataSourceProperties(): DataSourceProperties {
return DataSourceProperties()
}
@Primary
@Bean
@ConfigurationProperties("app.datasource.user.configuration")
fun userDataSource(userDataSourceProperties: DataSourceProperties): HikariDataSource {
return userDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource::class.java).build()
}
@Primary
@Bean
fun userEntityManagerFactory(builder: EntityManagerFactoryBuilder,
beanFactory: ConfigurableListableBeanFactory,
jpaProperties: JpaProperties)
: LocalContainerEntityManagerFactoryBean {
val em = builder.dataSource(userDataSource(userDataSourceProperties()))
.packages(userModelPackage)
.properties(getCommonProperties(jpaProperties))
.build()
val vendorAdapter = HibernateJpaVendorAdapter()
em.jpaVendorAdapter = vendorAdapter
em.jpaPropertyMap[AvailableSettings.BEAN_CONTAINER] = SpringBeanContainer(beanFactory)
return em
}
@Primary
@Bean
fun userTransactionManager(builder: EntityManagerFactoryBuilder,
beanFactory: ConfigurableListableBeanFactory,
jpaProperties: JpaProperties)
: PlatformTransactionManager {
val transactionManager = JpaTransactionManager()
transactionManager.entityManagerFactory = userEntityManagerFactory(builder, beanFactory, jpaProperties).getObject()
return transactionManager
}
}
The setup of multiple schemas as data sources works in the sense that, after removing the one-to-one association modeling the foreign key relationship, entities can be saved to the individual tables and retrieved as expected. However, as soon as some kind of relation between the two is established via a one-to-one, many-to-one, ... association, the following error occurs:
org.hibernate.AnnotationException: Association '...model.user.User.email' targets an unknown entity named '...model.email.Email'
To me, this suggests that the persistence configuration for the user package is incomplete as Spring does not seem to know how to resolve the Email entity.
Unfortunately, documentation and guides on this issue are rare. Any hint or idea would be much appreciated.