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,它与实体 Personcrm 数据源(架构)。

@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 并更新它。

请记住,如果由于您提到的相同原因(不同的会话工厂)而必须将 personuser 作为单个操作的一部分进行更新,则事务性可能会受到损害.