Spring/Hibernate 多个数据源之间有外键
Spring/Hibernate multiple data sources with foreign key between them
假设我有两个不同模式的表。因此,我为每个数据源创建一个实体和事务管理器:
@Configuration
@EnableJpaRepositories(
basePackages = "com.(omitted).endpoints.all.endpoint.crm",
entityManagerFactoryRef = "crmEntityManager",
transactionManagerRef = "crmTransactionManager"
)
public class PersistenceCrmConfig {
@Autowired
private Environment environment;
public PersistenceCrmConfig() {
super();
}
@Primary
@Bean
public LocalContainerEntityManagerFactoryBean crmEntityManager() {
final LocalContainerEntityManagerFactoryBean bean = new LocalContainerEntityManagerFactoryBean();
bean.setDataSource(crmDataSource());
bean.setPackagesToScan("com.(omitted).data.all.model.crm");
...
}
@Primary
@Bean
public PlatformTransactionManager crmTransactionManager() {
...
}
@Primary
@Bean
public DataSource crmDataSource() {
...
}
}
@Configuration
@EnableJpaRepositories(
basePackages = "com.(omitted).endpoints.all.endpoint.user",
entityManagerFactoryRef = "userEntityManager",
transactionManagerRef = "userTransactionManager"
)
public class PersistenceUserConfig {
@Autowired
private Environment environment;
public PersistenceUserConfig() {
super();
}
@Primary
@Bean
public LocalContainerEntityManagerFactoryBean userEntityManager() {
final LocalContainerEntityManagerFactoryBean bean = new LocalContainerEntityManagerFactoryBean();
bean.setDataSource(userDataSource());
bean.setPackagesToScan("com.(omitted).data.all.model.user");
...
}
@Primary
@Bean
public PlatformTransactionManager userTransactionManager() {
...
}
@Primary
@Bean
public DataSource userDataSource() {
...
}
}
在 user
数据源(模式)中,我有实体 User
,它与实体 Person
在crm
数据源(架构)。
@JoinColumn(name = "person_id", nullable = false)
@OneToOne(fetch = FetchType.LAZY)
private Person person;
这会导致异常:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userEntityManager' defined in class path resource [com/(omitted)/endpoints/all/spring/PersistenceUserConfig.class]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: @OneToOne or @ManyToOne on com.(omitted).data.all.model.user.User.person references an unknown entity: com.(omitted).data.all.model.crm.Person
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1786) ~[spring-beans-5.3.8.jar:5.3.8]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:602) ~[spring-beans-5.3.8.jar:5.3.8]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524) ~[spring-beans-5.3.8.jar:5.3.8]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean[=14=](AbstractBeanFactory.java:335) ~[spring-beans-5.3.8.jar:5.3.8]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.8.jar:5.3.8]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.8.jar:5.3.8]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.8.jar:5.3.8]
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1154) ~[spring-context-5.3.8.jar:5.3.8]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:908) ~[spring-context-5.3.8.jar:5.3.8]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.8.jar:5.3.8]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145) ~[spring-boot-2.5.1.jar:2.5.1]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-2.5.1.jar:2.5.1]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:434) ~[spring-boot-2.5.1.jar:2.5.1]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:338) ~[spring-boot-2.5.1.jar:2.5.1]
at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:144) ~[spring-boot-2.5.1.jar:2.5.1]
at com.(omitted).endpoints.all.Application.main(Application.java:11) ~[classes/:na]
Caused by: org.hibernate.AnnotationException: @OneToOne or @ManyToOne on com.(omitted).data.all.model.user.User.person references an unknown entity: com.(omitted).data.all.model.crm.Person
at org.hibernate.cfg.ToOneFkSecondPass.doSecondPass(ToOneFkSecondPass.java:100) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processEndOfQueue(InFlightMetadataCollectorImpl.java:1823) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processFkSecondPassesInOrder(InFlightMetadataCollectorImpl.java:1767) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1655) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:295) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:1224) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1255) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:58) ~[spring-orm-5.3.8.jar:5.3.8]
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365) ~[spring-orm-5.3.8.jar:5.3.8]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) ~[spring-orm-5.3.8.jar:5.3.8]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396) ~[spring-orm-5.3.8.jar:5.3.8]
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:341) ~[spring-orm-5.3.8.jar:5.3.8]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1845) ~[spring-beans-5.3.8.jar:5.3.8]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1782) ~[spring-beans-5.3.8.jar:5.3.8]
... 15 common frames omitted
我明白为什么会出现此错误。 Spring 必须生成两个 Hibernate 会话工厂,因此,两个实体列表彼此一无所知。
如何使一对一映射起作用?
谢谢。
无法让它与两个 Hibernate 会话工厂一起工作。
我也不建议尝试强制 Hibernate 为这种情况创建一个。
我看到的简单解决方法是在数据库方面。
考虑在 user
架构中创建 person
table 的视图,并将 Person
实体映射到视图,而不是 table.
此方法不允许您使用 userEntityManager
更新 person
table(除非您使用的是 Oracle 数据库)。如果你需要在某个时候做,你可以使用你的 crmEntityManager
在 table 中找到相应的 person
并更新它。
请记住,如果由于您提到的相同原因(不同的会话工厂)而必须将 person
和 user
作为单个操作的一部分进行更新,则事务性可能会受到损害.
假设我有两个不同模式的表。因此,我为每个数据源创建一个实体和事务管理器:
@Configuration
@EnableJpaRepositories(
basePackages = "com.(omitted).endpoints.all.endpoint.crm",
entityManagerFactoryRef = "crmEntityManager",
transactionManagerRef = "crmTransactionManager"
)
public class PersistenceCrmConfig {
@Autowired
private Environment environment;
public PersistenceCrmConfig() {
super();
}
@Primary
@Bean
public LocalContainerEntityManagerFactoryBean crmEntityManager() {
final LocalContainerEntityManagerFactoryBean bean = new LocalContainerEntityManagerFactoryBean();
bean.setDataSource(crmDataSource());
bean.setPackagesToScan("com.(omitted).data.all.model.crm");
...
}
@Primary
@Bean
public PlatformTransactionManager crmTransactionManager() {
...
}
@Primary
@Bean
public DataSource crmDataSource() {
...
}
}
@Configuration
@EnableJpaRepositories(
basePackages = "com.(omitted).endpoints.all.endpoint.user",
entityManagerFactoryRef = "userEntityManager",
transactionManagerRef = "userTransactionManager"
)
public class PersistenceUserConfig {
@Autowired
private Environment environment;
public PersistenceUserConfig() {
super();
}
@Primary
@Bean
public LocalContainerEntityManagerFactoryBean userEntityManager() {
final LocalContainerEntityManagerFactoryBean bean = new LocalContainerEntityManagerFactoryBean();
bean.setDataSource(userDataSource());
bean.setPackagesToScan("com.(omitted).data.all.model.user");
...
}
@Primary
@Bean
public PlatformTransactionManager userTransactionManager() {
...
}
@Primary
@Bean
public DataSource userDataSource() {
...
}
}
在 user
数据源(模式)中,我有实体 User
,它与实体 Person
在crm
数据源(架构)。
@JoinColumn(name = "person_id", nullable = false)
@OneToOne(fetch = FetchType.LAZY)
private Person person;
这会导致异常:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userEntityManager' defined in class path resource [com/(omitted)/endpoints/all/spring/PersistenceUserConfig.class]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: @OneToOne or @ManyToOne on com.(omitted).data.all.model.user.User.person references an unknown entity: com.(omitted).data.all.model.crm.Person
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1786) ~[spring-beans-5.3.8.jar:5.3.8]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:602) ~[spring-beans-5.3.8.jar:5.3.8]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524) ~[spring-beans-5.3.8.jar:5.3.8]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean[=14=](AbstractBeanFactory.java:335) ~[spring-beans-5.3.8.jar:5.3.8]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.8.jar:5.3.8]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.8.jar:5.3.8]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.8.jar:5.3.8]
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1154) ~[spring-context-5.3.8.jar:5.3.8]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:908) ~[spring-context-5.3.8.jar:5.3.8]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.8.jar:5.3.8]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145) ~[spring-boot-2.5.1.jar:2.5.1]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-2.5.1.jar:2.5.1]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:434) ~[spring-boot-2.5.1.jar:2.5.1]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:338) ~[spring-boot-2.5.1.jar:2.5.1]
at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:144) ~[spring-boot-2.5.1.jar:2.5.1]
at com.(omitted).endpoints.all.Application.main(Application.java:11) ~[classes/:na]
Caused by: org.hibernate.AnnotationException: @OneToOne or @ManyToOne on com.(omitted).data.all.model.user.User.person references an unknown entity: com.(omitted).data.all.model.crm.Person
at org.hibernate.cfg.ToOneFkSecondPass.doSecondPass(ToOneFkSecondPass.java:100) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processEndOfQueue(InFlightMetadataCollectorImpl.java:1823) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processFkSecondPassesInOrder(InFlightMetadataCollectorImpl.java:1767) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1655) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:295) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:1224) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1255) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:58) ~[spring-orm-5.3.8.jar:5.3.8]
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365) ~[spring-orm-5.3.8.jar:5.3.8]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) ~[spring-orm-5.3.8.jar:5.3.8]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396) ~[spring-orm-5.3.8.jar:5.3.8]
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:341) ~[spring-orm-5.3.8.jar:5.3.8]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1845) ~[spring-beans-5.3.8.jar:5.3.8]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1782) ~[spring-beans-5.3.8.jar:5.3.8]
... 15 common frames omitted
我明白为什么会出现此错误。 Spring 必须生成两个 Hibernate 会话工厂,因此,两个实体列表彼此一无所知。
如何使一对一映射起作用?
谢谢。
无法让它与两个 Hibernate 会话工厂一起工作。 我也不建议尝试强制 Hibernate 为这种情况创建一个。
我看到的简单解决方法是在数据库方面。
考虑在 user
架构中创建 person
table 的视图,并将 Person
实体映射到视图,而不是 table.
此方法不允许您使用 userEntityManager
更新 person
table(除非您使用的是 Oracle 数据库)。如果你需要在某个时候做,你可以使用你的 crmEntityManager
在 table 中找到相应的 person
并更新它。
请记住,如果由于您提到的相同原因(不同的会话工厂)而必须将 person
和 user
作为单个操作的一部分进行更新,则事务性可能会受到损害.