java 迁移到Spring 6,多个数据源和实体管理器

gajydyqb  于 2023-08-01  发布在  Java
关注(0)|答案(1)|浏览(100)

我正在将一个项目从Spring 5(Sping Boot 2.x)迁移到Spring 6(Spring Boot 3.1)并升级到Java 17。这最初是在Spring4中完成的,应用程序连接到两个不同的数据库,每个数据库都按照本文中的模式在XML中配置
Configuring Multiple Databases with Multiple EntityManagerFactory in Spring Data
我在开始时遇到了一个问题,就像这个错误一样
Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument
但我不知道该怎么纠正。我的配置看起来像这样

<context:property-placeholder
            location="file:${fvis.home}/conf/fvisconf.properties"
            ignore-unresolvable="false" ignore-resource-not-found="true" />

    <bean id="firstDataSource"
         class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close">
        <property name="driverClassName" value="oracle.jdbc.OracleDriver" />
        <property name="url"
            value="jdbc:oracle:thin:@(description=(address_list=(address=(host=${fvisbt.drpg.host})(protocol=tcp)(port=1521))(load_balance=yes)(failover=yes))(connect_data=(service_name=${fvisbt.drpg.sid})))">
        </property>
        <property name="username" value="firstUser" />
        <property name="password" value="${second.password}" />
    </bean>

    <bean id="secondDataSource"
         class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close">
        <property name="driverClassName" value="oracle.jdbc.OracleDriver" />
        <property name="url"
            value="jdbc:oracle:thin:@(description=(address_list=(address=(host=${fvisbt.drpg.host})(protocol=tcp)(port=1521))(load_balance=yes)(failover=yes))(connect_data=(service_name=${fvisbt.jobdb.sid})))">
        </property>
        <property name="username" value="secondUser" />
        <property name="password" value="${second.password}" />
    </bean>

    <!-- EntityManager injection is by package -->
    <bean id="firstEMF"
          class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
        </property>
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.Oracle12cDialect</prop>
                <prop key="hibernate.ejb.entitymanager_factory_name">firstEMF</prop>
            </props>
        </property>
        <property name="packagesToScan">
            <list>
                <value>com.example.entity.first</value>
            </list>
        </property>
    </bean>

    <bean id="secondEMF"
          class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="secondDataSource" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
        </property>
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.Oracle12cDialect</prop>
                <prop key="hibernate.ejb.entitymanager_factory_name">secondEMF</prop>
            </props>
        </property>
        <property name="packagesToScan">
            <list>
                <value>com.example.entity.second</value>
            </list>
        </property>
    </bean>

字符串
发射日志是这样的

...
INFO  c.e.w.MyApp                              : No active profile set, falling back to 1 default profile: "default"
INFO  .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
INFO  .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 17 ms. Found 0 JPA repository interfaces.
INFO  o.s.b.w.e.t.TomcatWebServer              : Tomcat initialized with port(s): 8080 (http)
INFO  o.a.c.c.StandardService                  : Starting service [Tomcat]
INFO  o.a.c.c.StandardEngine                   : Starting Servlet engine: [Apache Tomcat/10.1.10]
INFO  o.a.j.s.TldScanner                       : At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
INFO  o.a.c.c.C.[.[.[/]                        : Initializing Spring embedded WebApplicationContext
INFO  w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2636 ms
INFO  o.h.j.i.u.LogHelper                      : HHH000204: Processing PersistenceUnitInfo [name: default]
INFO  o.h.Version                              : HHH000412: Hibernate ORM core version 6.2.5.Final
INFO  o.h.c.Environment                        : HHH000406: Using bytecode reflection optimizer
INFO  o.h.b.i.BytecodeProviderInitiator        : HHH000021: Bytecode provider name : bytebuddy
INFO  o.s.o.j.p.SpringPersistenceUnitInfo      : No LoadTimeWeaver setup: ignoring JPA class transformer
WARN  o.h.e.j.c.i.ConnectionProviderInitiator  : HHH000181: No appropriate connection provider encountered, assuming application will be supplying connections
WARN  o.h.e.j.e.i.JdbcEnvironmentInitiator     : HHH000342: Could not obtain connection to query metadata

java.lang.UnsupportedOperationException: The application must supply JDBC connections
    at org.hibernate.engine.jdbc.connections.internal.UserSuppliedConnectionProviderImpl.getConnection(UserSuppliedConnectionProviderImpl.java:44) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess.obtainConnection(JdbcEnvironmentInitiator.java:316) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:152) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:34) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.initiateService(StandardServiceRegistryImpl.java:119) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:264) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:239) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:216) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.hibernate.boot.model.relational.Database.<init>(Database.java:45) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.getDatabase(InFlightMetadataCollectorImpl.java:230) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.<init>(InFlightMetadataCollectorImpl.java:198) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:166) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:1380) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1451) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:75) ~[spring-orm-6.0.10.jar:6.0.10]
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:376) ~[spring-orm-6.0.10.jar:6.0.10]
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) ~[spring-orm-6.0.10.jar:6.0.10]
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396) ~[spring-orm-6.0.10.jar:6.0.10]
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:352) ~[spring-orm-6.0.10.jar:6.0.10]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1816) ~[spring-beans-6.0.10.jar:6.0.10]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1766) ~[spring-beans-6.0.10.jar:6.0.10]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:598) ~[spring-beans-6.0.10.jar:6.0.10]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520) ~[spring-beans-6.0.10.jar:6.0.10]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[spring-beans-6.0.10.jar:6.0.10]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) [spring-beans-6.0.10.jar:6.0.10]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) [spring-beans-6.0.10.jar:6.0.10]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) [spring-beans-6.0.10.jar:6.0.10]
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1154) [spring-context-6.0.10.jar:6.0.10]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:931) [spring-context-6.0.10.jar:6.0.10]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:608) [spring-context-6.0.10.jar:6.0.10]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) [spring-boot-3.1.1.jar:3.1.1]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:734) [spring-boot-3.1.1.jar:3.1.1]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:436) [spring-boot-3.1.1.jar:3.1.1]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:312) [spring-boot-3.1.1.jar:3.1.1]
        ...

INFO  o.h.b.i.BytecodeProviderInitiator        : HHH000021: Bytecode provider name : bytebuddy
INFO  o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
INFO  j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
INFO  o.h.j.i.u.LogHelper                      : HHH000204: Processing PersistenceUnitInfo [name: default]
INFO  o.h.b.i.BytecodeProviderInitiator        : HHH000021: Bytecode provider name : bytebuddy
INFO  o.s.o.j.p.SpringPersistenceUnitInfo      : No LoadTimeWeaver setup: ignoring JPA class transformer
INFO  o.h.b.i.BytecodeProviderInitiator        : HHH000021: Bytecode provider name : bytebuddy
INFO  o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
INFO  j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
INFO  c.e.w.MyApp                              : adding properties resource, /META-INF/MANIFEST.MF
WARN  ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaSharedEM_entityManagerFactory': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument
INFO  j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
INFO  j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'


我有一些DAO对象,它们实现了一些基本的CRUD操作以及一些查找。忽略CRUD操作,它们是在层次结构中完成的,以消除大量重复代码,继承如下所示

public abstract class AbstractDAO<T> implements DAO<T> {
    protected EntityManagerFactory entityManagerFactory;
    protected abstract void setEntityManagerFactory(EntityManagerFactory entityManagerFactory);
    public EntityManagerFactory getEntityManagerFactory() {
        return this.entityManagerFactory;
    }
    ...
}

x

public class FirstAbstractDAO <K> extends AbstractDAO<K> {
    @Override
    @Autowired
    @Qualifier("firstEMF")
    public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) {
        this.entityManagerFactory = entityManagerFactory;
    }
}
public class SecondAbstractDAO <K> extends AbstractDAO<K> {
    @Override
    @Autowired
    @Qualifier("secondEMF")
    public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) {
        this.entityManagerFactory = entityManagerFactory;
    }
}

的一种或多种
日志显示了初始化的对象,如果我在setEntityManagerFactory中设置断点,我可以清楚地看到它们正在初始化。
我不明白的是,Sping Boot 3试图初始化的是什么,导致了这个追溯中的错误

java.lang.UnsupportedOperationException: The application must supply JDBC connections
    at org.hibernate.engine.jdbc.connections.internal.UserSuppliedConnectionProviderImpl.getConnection(UserSuppliedConnectionProviderImpl.java:44) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess.obtainConnection(JdbcEnvironmentInitiator.java:316) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:152) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:34) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.initiateService(StandardServiceRegistryImpl.java:119) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:264) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:239) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:216) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.hibernate.boot.model.relational.Database.<init>(Database.java:45) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.getDatabase(InFlightMetadataCollectorImpl.java:230) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.<init>(InFlightMetadataCollectorImpl.java:198) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:166) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:1380) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1451) ~[hibernate-core-6.2.5.Final.jar:6.2.5.Final]
    at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:75) ~[spring-orm-6.0.10.jar:6.0.10]
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:376) ~[spring-orm-6.0.10.jar:6.0.10]
...


如果我在createContainerEntityManagerFactory中放置一个断点,我看到它正在尝试初始化firstEMF。我怀疑它这样做“太早了”,因为当我进入www.example.com时EntityManager.build,firstEMF的配置值都设置好了,但dataSource为null。
这 * 似乎 * 对我来说,我在我的配置中缺少了一些东西,但我不知道是什么。在日志中发出异常 * 后的“Initialized JPA EntityManagerFactory”* 的噪音。

zdwk9cvp

zdwk9cvp1#

在Sping Boot 3中,JPA的自动配置已更改,现在它希望在创建EntityManagerFactory之前预先提供数据源。
在以前的Sping Boot 2设置中,首先创建EntityManagerFactory Bean,然后它将查找DataSource Bean。但是现在这失败了,因为在构建EMF时DataSource还没有初始化。
解决方案是将LocalContainerEntityManagerFactoryBean配置为依赖于数据源Bean,以便在构建EMF之前首先对其进行初始化。

@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {

  LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
  
  emf.setDataSource(dataSource()); // add this

  // others configs
  
  return emf;
}

个字符
必须在EntityManagerFactory之前初始化数据源。使用setDataSource()将EMF Bean配置为依赖于数据源
关键的修复是确保在构建EntityManagerFactory之前数据源可用

相关问题