如何使用两个数据源MapRepository 和 H2 JPARepository设置 Spring Boot?

问题描述

我正在尝试使用两个数据源设置一个 Spring Boot 项目。 第一个数据源是 H2 数据库,第二个是 MapRepository。 两个存储库将共享相同的实体。

我可以设法设置一个包含两个 H2 数据库的项目,但是当我尝试设置一个 MapRepository 而不是第二个 H2 数据源时,我收到以下错误

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__,| / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.4.0)

2021-01-12 10:57:16.610  INFO 26672 --- [           main] ch.getonline.springtestapp.App           : Starting App using Java 15.0.1 on nbbetina1 with PID 26672 (C:\Users\BetinaHiestand\eclipse20-workspace\spring-test-app\target\classes started by BetinaHiestand in C:\Users\BetinaHiestand\eclipse20-workspace\spring-test-app)
2021-01-12 10:57:16.612  INFO 26672 --- [           main] ch.getonline.springtestapp.App           : The following profiles are active: dev
2021-01-12 10:57:17.070  INFO 26672 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found,entering strict repository configuration mode!
2021-01-12 10:57:17.070  INFO 26672 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Map repositories in DEFAULT mode.
2021-01-12 10:57:17.092  INFO 26672 --- [           main] .RepositoryConfigurationExtensionSupport : Spring Data Map - Could not safely identify store assignment for repository candidate interface ch.getonline.springtestapp.storage.repositories.map.MapRepository. If you want this repository to be a Map repository,consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.keyvalueRepository.
2021-01-12 10:57:17.092  INFO 26672 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 15 ms. Found 0 Map repository interfaces.
2021-01-12 10:57:17.094  INFO 26672 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found,entering strict repository configuration mode!
2021-01-12 10:57:17.094  INFO 26672 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2021-01-12 10:57:17.111  INFO 26672 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 14 ms. Found 1 JPA repository interfaces.
2021-01-12 10:57:17.654  INFO 26672 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2021-01-12 10:57:17.661  INFO 26672 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2021-01-12 10:57:17.661  INFO 26672 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.39]
2021-01-12 10:57:17.758  INFO 26672 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2021-01-12 10:57:17.758  INFO 26672 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1105 ms
2021-01-12 10:57:17.976  INFO 26672 --- [           main] o.s.b.a.h2.H2ConsoleAutoConfiguration    : H2 console available at '/dbadmin'. Database available at 'jdbc:h2:mem:db1dev'
2021-01-12 10:57:18.058  INFO 26672 --- [           main] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]
2021-01-12 10:57:18.099  INFO 26672 --- [           main] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 5.4.23.Final
2021-01-12 10:57:18.198  INFO 26672 --- [           main] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
2021-01-12 10:57:18.324  INFO 26672 --- [           main] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
Hibernate: 
    
    drop table if exists "BasicEntity" CASCADE 
Hibernate: 
    
    create table "BasicEntity" (
       "DNA" binary not null,"id" varchar(255),"type" varchar(255),primary key ("DNA")
    )
2021-01-12 10:57:18.759  INFO 26672 --- [           main] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2021-01-12 10:57:18.765  INFO 26672 --- [           main] j.LocalContainerEntityManagerfactorybean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2021-01-12 10:57:18.787  INFO 26672 --- [           main] ch.getonline.springtestapp.App           : SpringTestApplication is starting...
2021-01-12 10:57:18.931  WARN 26672 --- [           main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'appContext': Unsatisfied dependency expressed through field 'entityStorage'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'entityStorageHandler' defined in file [C:\Users\BetinaHiestand\eclipse20-workspace\spring-test-app\target\classes\ch\getonline\springtestapp\storage\handlers\EntityStorageHandler.class]: Unsatisfied dependency expressed through constructor parameter 2; nested exception is org.springframework.beans.factory.NoSuchBeanDeFinitionException: No qualifying bean of type 'ch.getonline.springtestapp.storage.repositories.map.MapRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
2021-01-12 10:57:18.931  INFO 26672 --- [           main] j.LocalContainerEntityManagerfactorybean : Closing JPA EntityManagerFactory for persistence unit 'default'
2021-01-12 10:57:18.933  INFO 26672 --- [           main] o.apache.catalina.core.StandardService   : Stopping service [Tomcat]
2021-01-12 10:57:18.944  INFO 26672 --- [           main] ConditionEvaluationReportLoggingListener : 

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2021-01-12 10:57:18.956 ERROR 26672 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION Failed TO START
***************************

Description:

Parameter 2 of constructor in ch.getonline.springtestapp.storage.handlers.EntityStorageHandler required a bean of type 'ch.getonline.springtestapp.storage.repositories.map.MapRepository' that Could not be found.

The injection point has the following annotations:
    - @org.springframework.beans.factory.annotation.Autowired(required=true)


Action:

Consider defining a bean of type 'ch.getonline.springtestapp.storage.repositories.map.MapRepository' in your configuration.

我已经尝试添加 ComponentScan 并向 MapRepository 添加存储库注释,但无法弄清楚为什么没有为其创建 bean。两个存储库都在单独的包中,这些包被设置为 EnableMapRepositories/EnableJpaRepositories 注释的 basePackages。对于 sqlRepository,我创建了一个包含驱动程序属性等的配置类。我不确定 MapRepositories 是否也需要这样的东西,并且找不到有关它的有用文档。

我对 Spring Boot 并没有真正的经验,因此第一个问题是是否可以进行这样的设置?如果是,我应该如何配置它?

应用启动:

@SpringBootApplication
@ComponentScan (basePackages = {"ch.getonline.springtestapp"})
@EntityScan("ch.getonline.springtestapp.entity.types")
@EnableMapRepositories(basePackages = "ch.getonline.springtestapp.storage.repositories.map")
@EnableJpaRepositories(basePackages = "ch.getonline.springtestapp.storage.repositories.sql",entityManagerFactoryRef = "sqlDatabaseEntityManager",transactionManagerRef = "sqlDatabaseTransactionManager")
public class App  {
    
    // Logger setup (Per class) 
    private static final Logger log = LoggerFactory.getLogger(App.class);
            
    /*
     *  Application start
     */
    public static void main(String[] args) {
        ApplicationContext ctx = SpringApplication.run(App.class,args);
        System.out.println("App context in main: " + ctx.getdisplayName());
    }
    

地图存储库:

package ch.getonline.springtestapp.storage.repositories.map;



import org.springframework.stereotype.Repository;

import ch.getonline.springtestapp.storage.repositories.EntityRepository;

@Repository("mapRepository")
public interface MapRepository extends EntityRepository {

}

实体存储库:

package ch.getonline.springtestapp.storage.repositories;

import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.norepositoryBean;

import ch.getonline.springtestapp.entity.types.BasicEntity;

@norepositoryBean
public interface EntityRepository extends CrudRepository<BasicEntity,Long>{
    //Entity findByUuid(UUID id);
}

我试图访问两个存储库的StorageHandler:

    package ch.getonline.springtestapp.storage.handlers;
    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Map.Entry;
    import java.util.UUID;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    
    
    import ch.getonline.springtestapp.AppContext;
    import ch.getonline.springtestapp.entity.attribute.Attribute;
    import ch.getonline.springtestapp.entity.types.BasicEntity;
    import ch.getonline.springtestapp.storage.StorageHandler;
    import ch.getonline.springtestapp.storage.repositories.EntityRepository;
    import ch.getonline.springtestapp.storage.repositories.map.MapRepository;
    import ch.getonline.springtestapp.storage.repositories.sql.sqlRepository;
    
    
    
    
    
    /** Entity Storage
     * <br>
     * 
     * - Coordinates saving,loading,updating of Entities over different Repositories
     * 
     * 
     * @author sigi
     *
     */
    
    @Component
    public class EntityStorageHandler implements StorageHandler<BasicEntity,Long> {
        
        
        // Logger
        private static final Logger log = LoggerFactory.getLogger(EntityStorageHandler.class);
    
        private final AppContext app;
        private final Map<String,EntityRepository> repos;
        EntityStorageHandler(AppContext app,sqlRepository sqlRepo,MapRepository mapRepo) {
            this.app = app;
            this.repos = new HashMap<String,EntityRepository>();
            this.repos.put("sql",sqlRepo);
            this.repos.put("map",mapRepo);
        }
        
        
        
        //StorageHandler start hook
        public void run(String... args) throws Exception {
            
            //Print all configs for the key app in the config
            StringBuilder appConfig = new StringBuilder();
            for(Entry<String,Object> entry : this.app.getConfig().entrySet()) {
                appConfig.append("\nkey: " + entry.getKey() + " value: " + entry.getValue());
            }
            log.info(appConfig.toString());
            
            
            //Write demo Entity into db 
            BasicEntity e1 = new BasicEntity();
            e1.setId("1");
            e1.setType("Type1");
            this.repos.get("sql").save(e1);
            
            BasicEntity e2 = new BasicEntity();
            e2.setId("2");
            e2.setType("Type2");
            this.repos.get("sql").save(e2);
            
            BasicEntity e3 = new BasicEntity();
            e3.setId("3");
            e3.setType("Type3");
            this.repos.get("map").save(e2);
        }
    }
        

基本实体:

    package ch.getonline.springtestapp.entity.types;
    
    import java.util.UUID;
    
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    
    import org.springframework.data.keyvalue.annotation.KeySpace;
    
    import ch.getonline.springtestapp.entity.GenericEntity;
    
    
    
    /**
     * Basic Entity Implementation
     * 
     * @author sigi
     *
     */
    
    @Entity
    @KeySpace("basicEntities")
    public class BasicEntity extends GenericEntity {
    
        @Id
        @GeneratedValue(strategy=GenerationType.AUTO)
        UUID DNA;
        
    }
        

sql 配置:

package ch.getonline.springtestapp.configuration;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerfactorybean;
import org.springframework.orm.jpa.vendor.HibernateJpavendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;
import java.util.HashMap;


@Configuration
public class sqlConfiguration {
    @Autowired
    private Environment env;

    public sqlConfiguration() {
        super();
    }

    @Primary
    @Bean
    public LocalContainerEntityManagerfactorybean sqlDatabaseEntityManager() {
        final LocalContainerEntityManagerfactorybean em = new LocalContainerEntityManagerfactorybean();
        em.setDataSource(sqlDataSource());
        em.setPackagesToScan("ch.getonline.springtestapp.entity.types");

        final HibernateJpavendorAdapter vendorAdapter = new HibernateJpavendorAdapter();
        em.setJpavendorAdapter(vendorAdapter);
        final HashMap<String,Object> properties = new HashMap<String,Object>();
        properties.put("hibernate.hbm2ddl.auto",env.getProperty("spring.jpa.hibernate.ddl-auto"));
        properties.put("hibernate.dialect",env.getProperty("spring.jpa.database-platform"));
        properties.put("hibernate.jdbc.batch_size",env.getProperty("spring.jpa.properties.hibernate.jdbc.batch_size"));
        properties.put("hibernate.order_inserts",env.getProperty("spring.jpa.properties.hibernate.order_inserts"));
        properties.put("hibernate.order_updates",env.getProperty("spring.jpa.properties.hibernate.order_updates"));
        properties.put("hibernate.jdbc.batch_versioned_data",env.getProperty("spring.jpa.properties.hibernate.jdbc.batch_versioned_data"));
        properties.put("hibernate.generate_statistics",env.getProperty("spring.jpa.properties.hibernate.generate_statistics"));
        properties.put("hibernate.id.new_generator_mappings",env.getProperty("spring.jpa.properties.hibernate.id.new_generator_mappings"));
        properties.put("hhibernate.cache.use_second_level_cache",env.getProperty("spring.jpa.properties.hibernate.cache.use_second_level_cache"));
        properties.put("hibernate.globally_quoted_identifiers",env.getProperty("spring.jpa.properties.hibernate.globally_quoted_identifiers"));
        properties.put("hibernate.format_sql",env.getProperty("spring.jpa.properties.hibernate.format_sql"));
        properties.put("hibernate.show_sql",env.getProperty("spring.jpa.properties.hibernate.show_sql"));
        properties.put("hibernate.use_sql_comments",env.getProperty("spring.jpa.properties.hibernate.use_sql_comments"));
        properties.put("hibernate.type",env.getProperty("spring.jpa.properties.hibernate.type"));
        properties.put("hibernate.naming.physical-strategy",env.getProperty("spring.jpa.hibernate.naming.physical-strategy"));
        em.setJpaPropertyMap(properties);

        return em;
    }

    @Primary
    @Bean
    public DataSource sqlDataSource() {
        final DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(env.getProperty("spring.datasource.driverClassName"));
        dataSource.setUrl(env.getProperty("spring.datasource.url"));
        dataSource.setUsername(env.getProperty("spring.datasource.username"));
        dataSource.setPassword(env.getProperty("spring.datasource.password"));

        return dataSource;
    }

    @Primary
    @Bean
    public PlatformTransactionManager sqlDatabaseTransactionManager() {
        final JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(sqlDatabaseEntityManager().getobject());
        return transactionManager;
    }

}

sql 存储库

package ch.getonline.springtestapp.storage.repositories.sql;

import ch.getonline.springtestapp.storage.repositories.EntityRepository;


public interface sqlRepository extends EntityRepository {

}

应用程序.yml

#Debug mode
debug: false



#External config
spring:
  #Basic setup
  profiles.active: dev
  config:
    import: optional:classpath:config/app.properties,optional:classpath:config/config.yml
  
  #Localization
  messages:
    basename: config.i18n.messages
  
  #db
  h2: 
    console:
      path: /dbadmin
      enabled: true
      settings:
        web-allow-others: true

  datasource:
      username: inmemory
      password: inmemory
      driverClassName: org.h2.Driver
      port: 8080



  #jpa
  jpa:
    hibernate:
      ddl-auto: create
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
    properties:
      hibernate:
        id:
          new_generator_mappings: true
        cache: 
          use_second_level_cache: true
        order_inserts: true
        order_updates: true
        globally_quoted_identifiers: true
        generate_statistics: false
        show_sql: true
        format_sql: true
        use_sql_comments: true
        type: trace
        jdbc:
          batch_size: 500
          batch_versioned_data: false
        tmp:
          use_jdbc_Metadata_defaults: false
    database-platform: org.hibernate.dialect.H2Dialect

解决方法

正如我在评论中所写的那样,将两个不同的数据源与一个 Spring Boot 服务一起使用是很不寻常的。但在某些情况下可能需要这样做,以下是如何以干净的方式实现它:

请记住,我的回答主要来自Baeldung tutorial

考虑拥有这样的数据源:

spring.datasource.jdbcUrl = [url]
spring.datasource.username = [username]
spring.datasource.password = [password]

现在,我们要添加第二个,最好使用相同的语法:

spring.second-datasource.jdbcUrl = [url]
spring.second-datasource.username = [username]
spring.second-datasource.password = [password]

要同时使用这两个配置,我们只需使用 Datasource Bean 创建两个配置类 - 注意前缀注释:

@Configuration
@PropertySource({"classpath:persistence-multiple-db-boot.properties"})
@EnableJpaRepositories(
  basePackages = "com.baeldung.multipledb.dao.user",entityManagerFactoryRef = "userEntityManager",transactionManagerRef = "userTransactionManager")
public class PersistenceUserAutoConfiguration {
    
    @Primary
    @Bean
    @ConfigurationProperties(prefix="spring.datasource")
    public DataSource userDataSource() {
        return DataSourceBuilder.create().build();
    }
    // userEntityManager bean 

    // userTransactionManager bean
}


@Configuration
@PropertySource({"classpath:persistence-multiple-db-boot.properties"})
@EnableJpaRepositories(
  basePackages = "com.baeldung.multipledb.dao.product",entityManagerFactoryRef = "productEntityManager",transactionManagerRef = "productTransactionManager")
public class PersistenceProductAutoConfiguration {
   
    @Bean
    @ConfigurationProperties(prefix="spring.second-datasource")
    public DataSource productDataSource() {
        return DataSourceBuilder.create().build();
    }
   
    // productEntityManager bean 

    // productTransactionManager bean
}

您可以仅创建一个配置类并在其中提供两个 bean。

有关详细信息,请参阅 Spring JPA – Multiple Databases

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...