java - 具有多个数据源的 hibernate 配置类

标签 java spring hibernate mysql-workbench

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

WARNING: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'mainController': Unsatisfied dependency expressed through field 'dataDAO'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'dataDAO': Unsatisfied dependency expressed through field 'sessionFactory'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.hibernate.SessionFactory' available: expected single matching bean but found 2: sessionFactory1,sessionFactory2
Nov 29, 2019 5:08:11 PM org.springframework.web.context.ContextLoader initWebApplicationContext
SEVERE: Context initialization failed
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'mainController': Unsatisfied dependency expressed through field 'dataDAO'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'dataDAO': Unsatisfied dependency expressed through field 'sessionFactory'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.hibernate.SessionFactory' available: expected single matching bean but found 2: sessionFactory1,sessionFactory2
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:596)

这是我的配置类:
package com.pack1.config;

import java.beans.PropertyVetoException;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Logger;

import javax.sql.DataSource;

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;
import org.springframework.core.env.Environment;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import com.pack1.routing.MyRoutingDataSource;

@Configuration
@ComponentScan("com.pack1")
@EnableWebMvc
@EnableTransactionManagement

// Load to Environment
@PropertySources({@PropertySource("classpath:ds/datasource-cfg.properties")})

public class ApplicationContextConfig implements WebMvcConfigurer
{


    // The Environment class serves as the property holder
    // and stores all the properties loaded by the @PropertySource
    @Autowired
    private Environment env;

    private Logger logger = Logger.getLogger(getClass().getName());

    @Bean(name = "viewResolver")
    public InternalResourceViewResolver getViewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();

        viewResolver.setPrefix("/WEB-INF/pages/");
        viewResolver.setSuffix(".jsp");

        return viewResolver;
    }

    // Returns Routing DataSource (MyRoutingDataSource)
    @Autowired
    @Bean(name = "dataSource")
    public DataSource getDataSource(DataSource dataSource1, DataSource dataSource2) {

        System.out.println("## Create DataSource from dataSource1 & dataSource2");

        MyRoutingDataSource dataSource = new MyRoutingDataSource();

        Map<Object, Object> dsMap = new HashMap<Object, Object>();
        dsMap.put("PUBLISHER_DS", dataSource1);
        dsMap.put("ADVERTISER_DS", dataSource2);

        dataSource.setTargetDataSources(dsMap);

        return dataSource;
    }

    @Bean(name = "dataSource1")
    public DataSource getDataSource1() throws SQLException, PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();

        // See: datasouce-cfg.properties
        dataSource.setDriverClass(env.getProperty("ds.database-driver1"));
        dataSource.setJdbcUrl(env.getProperty("ds.url1"));
        dataSource.setUser(env.getProperty("ds.username1"));
        dataSource.setPassword(env.getProperty("ds.password1"));

        System.out.println("## getDataSource1: " + dataSource);

        return dataSource;
    }

    private Properties getHibernateProperties1() 
    {
        // set hibernate properties
        Properties props = new Properties();

        props.setProperty("ds.hibernate.dialect1", env.getProperty("ds.hibernate.dialect1"));
        props.setProperty("ds.hibernate.show_sql1", env.getProperty("ds.hibernate.show_sql1"));

        return props;
    }

 // need a helper method 
    // read environment property and convert to int

    private int getIntProperty1(String propName) {

        String propVal = env.getProperty(propName);

        // now convert to int
        int intPropVal = Integer.parseInt(propVal);

        return intPropVal;
    }

    @Bean
    public LocalSessionFactoryBean sessionFactory1() throws SQLException, PropertyVetoException{

        // create session factorys
        LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();

        // set the properties
        sessionFactory.setDataSource(getDataSource1());
        sessionFactory.setPackagesToScan(env.getProperty("ds.hibernate.packagesToScan1"));
        sessionFactory.setHibernateProperties(getHibernateProperties1());

        return sessionFactory;
    }

    @Autowired
    @Bean(name = "transactionManager")
    public HibernateTransactionManager getTransactionManager1(SessionFactory sessionFactory1) {

        // setup transaction manager based on session factory 
        HibernateTransactionManager txManager = new HibernateTransactionManager();

        txManager.setSessionFactory(sessionFactory1);

        return txManager;
    }


    @Bean(name = "dataSource2")
    public DataSource getDataSource2() throws SQLException, PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();

        // See: datasouce-cfg.properties
        dataSource.setDriverClass(env.getProperty("ds.database-driver2"));
        dataSource.setJdbcUrl(env.getProperty("ds.url2"));
        dataSource.setUser(env.getProperty("ds.username2"));
        dataSource.setPassword(env.getProperty("ds.password2"));

        System.out.println("## getDataSource2: " + dataSource);

        return dataSource;
    }

    private Properties getHibernateProperties2() 
    {
        // set hibernate properties
        Properties props = new Properties();

        props.setProperty("ds.hibernate.dialect2", env.getProperty("ds.hibernate.dialect2"));
        props.setProperty("ds.hibernate.show_sql2", env.getProperty("ds.hibernate.show_sql2"));

        return props;
    }

 // need a helper method 
    // read environment property and convert to int

    private int getIntProperty2(String propName) {

        String propVal = env.getProperty(propName);

        // now convert to int
        int intPropVal = Integer.parseInt(propVal);

        return intPropVal;
    }


    @Bean
    public LocalSessionFactoryBean sessionFactory2() throws SQLException, PropertyVetoException{

        // create session factorys
        LocalSessionFactoryBean sessionFactory1 = new LocalSessionFactoryBean();

        // set the properties
        sessionFactory1.setDataSource(getDataSource2());
        sessionFactory1.setPackagesToScan(env.getProperty("ds.hibernate.packagesToScan2"));
        sessionFactory1.setHibernateProperties(getHibernateProperties2());

        return sessionFactory1;
    }


    @Autowired
    @Bean(name = "transactionManager")
    public HibernateTransactionManager getTransactionManage2(SessionFactory sessionFactory1) {

        // setup transaction manager based on session factory 
        HibernateTransactionManager txManager = new HibernateTransactionManager();

        txManager.setSessionFactory(sessionFactory1);

        return txManager;
    }

}

道类:
package com.pack1.dao;

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.stereotype.Repository;

@Repository
public class DataDAO extends JdbcDaoSupport {


     @Autowired
     @Qualifier(value="t1")
     private SessionFactory firstDBSessionFactory;


       public List<Publisher> queryPublishers() {

            // get the current hibernate session
            Session currentSession = firstDBSessionFactory.getCurrentSession();

            // create a query 
            Query<Publisher> theQuery = 
                    currentSession.createQuery("from Publisher", Publisher.class);

            // execute query and get result list
            List<Publisher> customers = theQuery.getResultList();

            // return the results
            return customers;

            return customers;
          }


        @Autowired
        @Qualifier(value="t2")
        private SessionFactory secondDBSessionFactory;


        @Transactional(propagation= Propagation.REQUIRED, readOnly=true, value="t2")
        public List<Advertiser> queryAdvertisers() {

            // get the current hibernate session
            Session currentSession = secondDBSessionFactory.getCurrentSession();

            // create a query 
            Query<Advertiser> theQuery = 
                    currentSession.createQuery("from Advertiser", Advertiser.class);

            // execute query and get result list
            List<Advertiser> customers = theQuery.getResultList();

            // return the results
            return customers;

        }

        }

        public List<String> queryDashboard() {

            return null;
        }
}

属性文件:
# DataSource (PUBLISHER System).

ds.database-driver1=com.mysql.jdbc.Driver
ds.url1=jdbc:mysql://127.0.0.1:3306/pan_db
ds.username1=hbstudent
ds.password1=hbstudent


# DataSource (ADVERTISER System).

ds.database-driver2=com.mysql.jdbc.Driver
ds.url2=jdbc:mysql://127.0.0.1:3306/voter_db
ds.username2=hbstudent
ds.password2=hbstudent


#
# Hibernate properties1
#
ds.hibernate.dialect1=org.hibernate.dialect.MySQLDialect
ds.hibernate.show_sql1=true
ds.hibernate.hbm2ddl.auto1=update


#
# Hibernate properties2
#
ds.hibernate.dialect2=org.hibernate.dialect.MySQLDialect
ds.hibernate.show_sql2=true
ds.hibernate.hbm2ddl.auto2=update

SpringWebAppInitializer 类:
package com.pack1.config;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration.Dynamic;

import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.DispatcherServlet;

public class SpringWebAppInitializer implements WebApplicationInitializer
{

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {


            AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
            ctx.register(ApplicationContextConfig.class);
            ctx.setServletContext(servletContext); // ②

            Dynamic servlet = servletContext.addServlet("dispatcher",new DispatcherServlet(ctx)); // ③
            servlet.addMapping("/*");
            servlet.setLoadOnStartup(1);

            servletContext.addFilter("name", CharacterEncodingFilter.class)
            .addMappingForUrlPatterns(null, false, "/*");

        }



    /*@Override
    public void onStartup(ServletContext servletContext) throws ServletException
    {
        AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext();
        appContext.register(ApplicationContextConfig.class);

        ServletRegistration.Dynamic dispatcher = servletContext.addServlet("SpringDispatcher",
                new DispatcherServlet(appContext));
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("/");       
        // 
        dispatcher.setInitParameter("contextClass", appContext.getClass().getName());

        servletContext.addListener(new ContextLoaderListener(appContext));

        // UTF8 Charactor Filter.
        FilterRegistration.Dynamic fr = servletContext.addFilter("encodingFilter", CharacterEncodingFilter.class);

        fr.setInitParameter("encoding", "UTF-8");
        fr.setInitParameter("forceEncoding", "true");
        fr.addMappingForUrlPatterns(null, true, "/*");
    }*/

    }

WebMvcConfigurerAdapter 类:
package com.pack1.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import com.pack1.intercepter.DataSourceIntercepter;


@Configuration
@EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter{

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) 
    {

        // Default..
    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    //
    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        registry.addInterceptor(new DataSourceIntercepter())//
                .addPathPatterns("/publisher/*", "/advertiser/*");
    }
}

以下异常(exception)是:
INFO: Server startup in [8,376] milliseconds
Dec 05, 2019 12:05:18 PM org.springframework.web.servlet.DispatcherServlet noHandlerFound
WARNING: No mapping for GET /multiple_DB1/
Dec 05, 2019 12:05:22 PM org.springframework.web.servlet.DispatcherServlet noHandlerFound
WARNING: No mapping for GET /multiple_DB1/
Dec 05, 2019 12:05:23 PM org.springframework.web.servlet.DispatcherServlet noHandlerFound
WARNING: No mapping for GET /multiple_DB1/

我是在 Spring MVC 中使用多个数据源的新手。请帮助我在 Hibernate 配置类中编码。

最佳答案

每个数据库需要多个配置文件,其中之一需要声明为 Primary .我为您提供了一些示例,以了解您需要什么。
PrimaryDB被声明为项目的主数据库。 Primary 意味着默认情况下将在主数据库上执行 JPA。

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "primaryEntityManagerFactory",
    basePackages = {"com.example.repositories.primary"})
public class PrimaryDBConfig {

  @Primary
  @Bean(name = "primaryDatasource")
  @ConfigurationProperties(prefix = "primary.datasource")
  public DataSource dataSource() {
    return DataSourceBuilder.create().build();
  }

  @Primary
  @Bean(name = "primaryEntityManagerFactory")
  public LocalContainerEntityManagerFactoryBean entityManagerFactory(
      EntityManagerFactoryBuilder builder, @Qualifier("primaryDatasource") DataSource dataSource) {
    return builder.dataSource(dataSource).packages("com.example.entities.primary")
        .persistenceUnit("primary").build();
  }

  @Primary
  @Bean(name = "primaryTransactionManager")
  public PlatformTransactionManager transactionManager(
      @Qualifier("primaryEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
    return new JpaTransactionManager(entityManagerFactory);

  }
}

关于我们的第二个数据库的第二个配置文件 FooDB
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "fooEntityManagerFactory",
    basePackages = {"com.example.repositories.foo"})
public class FooDBConfig {

  @Bean(name = "fooDatasource")
  @ConfigurationProperties(prefix = "foo.datasource")
  public DataSource dataSource() {
    return DataSourceBuilder.create().build();
  }

  @Bean(name = "fooEntityManagerFactory")
  public LocalContainerEntityManagerFactoryBean entityManagerFactory(
      EntityManagerFactoryBuilder builder, @Qualifier("fooDatasource") DataSource dataSource) {
    return builder.dataSource(dataSource).packages("com.example.entities.crf")
        .persistenceUnit("foo").build();
  }

  @Bean(name = "fooTransactionManager")
  public PlatformTransactionManager transactionManager(
      @Qualifier("fooEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
    return new JpaTransactionManager(entityManagerFactory);
  }
}

此外,您需要在 application.properties 上添加以下内容,因为Spring会监听默认的数据源配置。
primary.datasource.url= URL
primary.datasource.username=username
primary.datasource.password=password


foo.datasource.url=URL
foo.datasource.username=username
foo.datasource.password=password

关于java - 具有多个数据源的 hibernate 配置类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59116197/

相关文章:

java - 使用 JPA 持久化父对象时如何防止保存子对象?报告 TransientPropertyValueException

java - 标准 java 库的 java-doc 问题

java - 我应该如何使用 servlet 和 Ajax?

java - 如何初始化 Spring Data JPA 规范?

java - Spring - Web 应用程序的 Junit

java - Spring - Hibernate 使用 FlushMode 提高事务性能

java - 如何通过 spring data JPA 处理分层查询(Oracle)?其中 SQL 包括 START WITH... CONNECT BY

java - 我可以从 Java 使用 Mountain Lion 的通知中心吗?

java - Maven 不支持 Maven 依赖插件

hibernate - "Table not found" hibernate 异常