多个 SessionFactory 配置失败并出现 org.springframework.beans.factory.UnsatisfiedDependencyException 异常

Multiple SessionFactory configuration fails with org.springframework.beans.factory.UnsatisfiedDependencyException Exception

我们的应用程序需要处理多个数据库。我们尝试通过 Hibernate 配置来配置多个数据源,并添加了两个配置,一个用于数据库 1,另一个用于数据库 2。此配置失败并出现以下异常

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'DB1TransactionManager' defined in class path resource [org/npcc/ccms/config/db/HibernateConfig4DB1.class]: Unsatisfied dependency expressed through constructor argument with index 0 of type [org.hibernate.SessionFactory]: : No qualifying bean of type [org.hibernate.SessionFactory] is defined: expected single matching bean but found 2: AgrgtrSessionFactory,HRSessionFactory,StageSessionFactory; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.hibernate.SessionFactory] is defined: expected single matching bean but found 3: DB1SessionFactory,DB2SessionFactory
org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:747)
    org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:462)
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1094)
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:989)
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
    org.springframework.beans.factory.support.AbstractBeanFactory.getObject(AbstractBeanFactory.java:302)
    org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
    org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
    org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
    org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:703)
    org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760)
    org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
    org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:658)
    org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:530)
    org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:484)
    org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:136)
    javax.servlet.GenericServlet.init(GenericServlet.java:160)
    org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
    org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
    org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:947)
    org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
    org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1009)
    org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
    org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
    java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    java.lang.Thread.run(Thread.java:745)

root cause

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.hibernate.SessionFactory] is defined: expected single matching bean but found 2: DB1SessionFactory,DB2SessionFactory
    org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:970)
    org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:858)
    org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:811)
    org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:739)
    org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:462)
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1094)
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:989)
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
    org.springframework.beans.factory.support.AbstractBeanFactory.getObject(AbstractBeanFactory.java:302)
    org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
    org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
    org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
    org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:703)
    org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760)
    org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
    org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:658)
    org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:530)
    org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:484)
    org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:136)
    javax.servlet.GenericServlet.init(GenericServlet.java:160)
    org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
    org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
    org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:947)
    org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
    org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1009)
    org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
    org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
    java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    java.lang.Thread.run(Thread.java:745)

第一个数据库配置:

@Configuration
@EnableTransactionManagement
@ComponentScan({ "org.npcc.ccms.config" })
@PropertySource(value = { "classpath:application.properties" })
public class HibernateConfig4DB1 {
    final static Logger logger = LogManager.getLogger(HibernateConfig4DB1.class);

    @Autowired
    private Environment environment;

     @Bean(name="DB1SessionFactory")
    public LocalSessionFactoryBean db1SessionFactory() {
        LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
        sessionFactory.setDataSource(dataSource());
        sessionFactory.setPackagesToScan(new String[] { "org.npcc.ccms.model.db1" });
        sessionFactory.setHibernateProperties(hibernateProperties());
        return sessionFactory;
     }

    @Bean(destroyMethod="")
    public DataSource dataSource() {
        JndiTemplate jndi = new JndiTemplate();
        DataSource dataSource = null;
        try {
            dataSource = (DataSource) jndi.lookup(environment.getRequiredProperty("datasource"));
        } catch (NamingException e) {
            logger.error("NamingException for java:comp/env/jdbc/ccms_cp1_orcl", e);
        }
        return dataSource;
    }

    private Properties hibernateProperties() {
        Properties properties = new Properties();
        properties.put("hibernate.dialect", environment.getRequiredProperty("hibernate.dialect"));
        properties.put("hibernate.show_sql", environment.getRequiredProperty("hibernate.show_sql"));
        properties.put("hibernate.format_sql", environment.getRequiredProperty("hibernate.format_sql"));
        return properties;        
    }

    @Bean(name="DB1TransactionManager")
    @Autowired
    @Qualifier("DB1SessionFactory")
    public HibernateTransactionManager db1TransactionManager(SessionFactory s) {
       HibernateTransactionManager txManager = new HibernateTransactionManager();
       txManager.setSessionFactory(s);
       return txManager;
    }
}

第二个数据库配置:

@Configuration
@EnableTransactionManagement
@ComponentScan({ "org.npcc.ccms.config" })
@PropertySource(value = { "classpath:application.properties" })
public class HibernateConfig4DB2 {
    final static Logger logger = LogManager.getLogger(HibernateConfig4DB2.class);

    @Autowired
    private Environment environment;

     @Bean(name="DB2SessionFactory")
    public LocalSessionFactoryBean db2SessionFactory() {
        LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
        sessionFactory.setDataSource(dataSource());
        sessionFactory.setPackagesToScan(new String[] { "org.npcc.ccms.model.db2" });
        sessionFactory.setHibernateProperties(hibernateProperties());
        return sessionFactory;
     }

    @Bean(destroyMethod="")
    public DataSource dataSource() {
        JndiTemplate jndi = new JndiTemplate();
        DataSource dataSource = null;
        try {
            dataSource = (DataSource) jndi.lookup(environment.getRequiredProperty("datasource"));
        } catch (NamingException e) {
            logger.error("NamingException for java:comp/env/jdbc/ccms_cp1_orcl", e);
        }
        return dataSource;
    }

    private Properties hibernateProperties() {
        Properties properties = new Properties();
        properties.put("hibernate.dialect", environment.getRequiredProperty("hibernate.dialect"));
        properties.put("hibernate.show_sql", environment.getRequiredProperty("hibernate.show_sql"));
        properties.put("hibernate.format_sql", environment.getRequiredProperty("hibernate.format_sql"));
        return properties;        
    }

    @Bean(name="DB2TransactionManager")
    @Autowired
    @Qualifier("DB2SessionFactory")
    public HibernateTransactionManager db2TransactionManager(SessionFactory s) {
       HibernateTransactionManager txManager = new HibernateTransactionManager();
       txManager.setSessionFactory(s);
       return txManager;
    }
}

那是因为您在配置中定义了 2 个 SessionFactory,Spring 猜不出应该选择哪一个。

您可以自动装配您的 sessionFactory 并在您的 @Bean 中使用它:

@Autowired
@Qualifier("DB1SessionFactory")
private SessionFactory sessionFactory;

@Bean(name="DB1TransactionManager")
public HibernateTransactionManager db2TransactionManager() {
   HibernateTransactionManager txManager = new HibernateTransactionManager();
   txManager.setSessionFactory(this.sessionFactory);
   return txManager;
}

明确选择要使用的 bean。

此外,您应该注意只有一个配置需要保存 @EnableTransactionManagement,而且您正在对同一个包进行两次组件扫描,这是不必要的。同样,您有 2 个 @PropertySource 具有相同的属性文件,只需要一个。

在你的情况下,我会创建一个 RootHibernateConfig 包含 @Configuration@EnableTransactionManagement@ComponentScan({ "org.npcc.ccms.config" })@PropertySource(value = { "classpath:application.properties" }) 注释 + @Import HibernateConfig4DB1HibernateConfig4DB2.

您可以从 transactionManager beans 方法中删除 @Autowired@Qualifier 注释。不要忘记在您的代码中显式使用所需的 transactionManager。

参见:http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/transaction/annotation/Transactional.html

Spring - Is it possible to use multiple transaction managers in the same application?

我遇到了同样的问题。解决方法如下

@Bean(name = "rebootSessionFactory")
public LocalSessionFactoryBean sessionFactory() {

    LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
    sessionFactory.setDataSource(dataSource());
    sessionFactory.setPackagesToScan(
            new String[]
                    {
                            "com.ra.reboot.b.persistence.entity",
                            "com.ra.reboot.bl.persistence.entity",
                            "com.ra.reboot.bla.persistence.entity",
                            "com.ra.reboot.blaBla.persistence.entity"

                    });
    sessionFactory.setHibernateProperties(hibernateProperties());
    return sessionFactory;

}


@Bean(name="rebootTransactionManager")
public HibernateTransactionManager transactionManager() {
    HibernateTransactionManager txManager = new HibernateTransactionManager();

    txManager.setSessionFactory(sessionFactory().getObject());
    return txManager;
}


private Properties hibernateProperties() {

    Properties hibernateProperties = new Properties();
    hibernateProperties.setProperty("hibernate.cache.use_second_level_cache", "true");
    hibernateProperties.setProperty("hibernate.cache.region.factory_class", "org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory");
    hibernateProperties.setProperty("hibernate.cache.use_query_cache", "true");
    hibernateProperties.setProperty("org.hibernate.envers.audit_table_prefix", "history");
    hibernateProperties.setProperty("org.hibernate.envers.audit_table_suffix", "_audit");
    hibernateProperties.setProperty("hibernate.jdbc.batch_size", "500");

    return hibernateProperties;
}

   @Bean
public HibernateExceptionTranslator hibernateExceptionTranslator() {
    return new HibernateExceptionTranslator();
}



@Bean
public DataSource dataSource() {
    Properties jdbcProperties = this.getJdbcPropertise();
    DriverManagerDataSource dataSource = new DriverManagerDataSource();
    dataSource.setDriverClassName(jdbcProperties.getProperty("jdbc.drive.class"));
    dataSource.setUrl(jdbcProperties.getProperty("jdbc.database.url"));
    dataSource.setUsername(jdbcProperties.getProperty("jdbc.database.username"));
    dataSource.setPassword(jdbcProperties.getProperty("jdbc.database.password"));
    return dataSource;
}


private Properties getJdbcPropertise() {
    InputStream inputStream = RarebootDataSourceConfig.class.getClassLoader().getResourceAsStream("dev.jdbc.properties");
    Properties jdbcProperties = new Properties();
    try {
        jdbcProperties.load(inputStream);
    } catch (NullPointerException IOException) {
        System.out.println("dev.jdbc.properties not Found");
        throw new RuntimeException();
    } catch (IOException e) {
        System.out.println("dev.jdbc.properties not Found");
        throw new RuntimeException();
    }
    return jdbcProperties;
}

这是配置文件。然后在BaseDao

@Autowired
@Qualifier("rebootSessionFactory")
public SessionFactory sessionFactory;

public SessionFactory getSessionFactory() {
    return sessionFactory;
}

public Session getSession() {
    return this.sessionFactory.getCurrentSession();
}

在我的服务中

import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional(value = "rebootTransactionManager")
public class ClientLeadInfoService {
 ...
}

确保您使用的是这个:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>

所以我有 2 个配置文件(这里我 post 只有一个示例文件)。我必须告诉我的 BaseDao 我正在使用哪个会话工厂。在我的服务中,我必须告诉我正在使用哪个事务管理器。

这是由于继承而发生的。有 class BaseClass(base class)和 class DerrivedClass(从 BaseClass 扩展)。当我使用 @Autowire 注释时,它给出了一个错误,原因是有 2 个对象,即 Class BaseClass 和 Class DerrivedClass。

我使用 @Resource 注释而不是 @Autowire 如下 @Resource(名字="derrivedClass") 派生Class testObj;

似乎Spring boot 创建的bean 的名称与class 的骆驼字母名称相同。例如如果我的 class 是 TestClass 那么 bean 名称将是 "testClass"