-1

我正在尝试使用两个数据源建立一个 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
4

1 回答 1

0

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

请记住,我的答案主要来自那个Baeldung 教程

考虑拥有这样的数据源:

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]

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

@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 – 多个数据库

于 2021-01-12T10:54:13.807 回答