简体   繁体   English

SpringBoot:为测试配置 Spring DataSource

[英]SpringBoot: Configuring Spring DataSource for Tests

I have a SpringBoot app.我有一个 SpringBoot 应用程序。

I have created this test:我创建了这个测试:

@ContextConfiguration(classes={TestConfig.class})
@RunWith(SpringRunner.class)
@SpringBootTest
public class SuncionServiceITTest {
    @Test
    public void should_Find_2() {
        // TODO
    }
}

where在哪里

@Configuration
@EnableJpaRepositories(basePackages = "com.plats.bruts.repository")
@PropertySource("local-configuration.properties")
@EnableTransactionManagement
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class})
public class TestConfig {
}

and local configuration.properties :和本地configuration.properties

spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=sa

but when I run the test.但是当我运行测试时。 I got this error:我收到此错误:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'entityManagerFactory' available引起:org.springframework.beans.factory.NoSuchBeanDefinitionException:没有名为“entityManagerFactory”的bean可用

I also tried with:我也试过:

@EnableJpaRepositories(basePackages = "com.plats.bruts.repository", entityManagerFactoryRef="emf")

but then I have the error:但后来我有错误:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'emf' available引起:org.springframework.beans.factory.NoSuchBeanDefinitionException:没有可用的名为“emf”的bean

Looks like you are missing below starter dependency.看起来您缺少启动器依赖项。 This starter dependency has all the necessary dependencies needed to configure the jpa repositories.此启动依赖项具有配置jpa存储库所需的所有必要依赖项。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

This is an approach of how to configure several data sources in one application.这是一种如何在一个应用程序中配置多个数据源的方法。 I have tested it for spring-webmvc and graphql-java , but I think it can be useful for spring-boot as well.我已经对spring-webmvcgraphql-java进行了测试,但我认为它对spring-boot也很有用。


Configure several data sources with spring-data-jpa使用spring-data-jpa配置多个数据源

For each database we should enable JPA repositories and specify the base packages for the corresponding interfaces.对于每个数据库,我们应该启用 JPA 存储库并为相应的接口指定基本包。 Also for each database we should specify the entity manager factory and the base packages for corresponding entities, as well as the transaction manager and the data source, of course.当然,对于每个数据库,我们还应该为相应的实体指定实体管理器工厂和基本包,以及事务管理器和数据源。

To do this, we'll include in our application one configuration class for data JPA and three inner classes for each database.为此,我们将在我们的应用程序中包含一个用于数据 JPA 的配置类和三个用于每个数据库的内部类。 See the Simple GraphQL implementation .请参阅简单的 GraphQL 实现

DataJpaConfig.java数据配置文件

package org.drakonoved.graphql;

@Configuration
@PropertySource(value = "classpath:resources/application.properties", encoding = "UTF-8")
public class DataJpaConfig {
    private final String basePackage = "org.drakonoved.graphql";

    @EnableJpaRepositories(
            basePackages = basePackage + ".repository.usersdb",
            entityManagerFactoryRef = "usersdbEntityManagerFactory",
            transactionManagerRef = "usersdbTransactionManager")
    public class UsersDBJpaConfig {
        @Bean("usersdbEntityManagerFactory")
        public LocalContainerEntityManagerFactoryBean usersDBEntityManagerFactoryBean(
                @Value("${datasource.usersdb.script}") String script) {
            return createEntityManagerFactoryBean(
                    script, "usersdb", basePackage + ".dto.usersdb");
        }

        @Bean("usersdbTransactionManager")
        public PlatformTransactionManager usersDBTransactionManager(
                @Qualifier("usersdbEntityManagerFactory")
                        LocalContainerEntityManagerFactoryBean factoryBean) {
            return new JpaTransactionManager(factoryBean.getNativeEntityManagerFactory());
        }
    }

    @EnableJpaRepositories(
            basePackages = basePackage + ".repository.rolesdb",
            entityManagerFactoryRef = "rolesdbEntityManagerFactory",
            transactionManagerRef = "rolesdbTransactionManager")
    public class RolesDBJpaConfig {
        @Bean("rolesdbEntityManagerFactory")
        public LocalContainerEntityManagerFactoryBean rolesDBEntityManagerFactoryBean(
                @Value("${datasource.rolesdb.script}") String script) {
            return createEntityManagerFactoryBean(
                    script, "rolesdb", basePackage + ".dto.rolesdb");
        }

        @Bean("rolesdbTransactionManager")
        public PlatformTransactionManager rolesDBTransactionManager(
                @Qualifier("rolesdbEntityManagerFactory")
                        LocalContainerEntityManagerFactoryBean factoryBean) {
            return new JpaTransactionManager(factoryBean.getNativeEntityManagerFactory());
        }
    }

    @EnableJpaRepositories(
            basePackages = basePackage + ".repository.usersnrolesdb",
            entityManagerFactoryRef = "usersnrolesdbEntityManagerFactory",
            transactionManagerRef = "usersnrolesdbTransactionManager")
    public class UsersNRolesDBJpaConfig {
        @Bean("usersnrolesdbEntityManagerFactory")
        public LocalContainerEntityManagerFactoryBean usersNRolesDBEntityManagerFactoryBean(
                @Value("${datasource.usersnrolesdb.script}") String script) {
            return createEntityManagerFactoryBean(
                    script, "usersnrolesdb", basePackage + ".dto.usersnrolesdb");
        }

        @Bean("usersnrolesdbTransactionManager")
        public PlatformTransactionManager usersNRolesDBTransactionManager(
                @Qualifier("usersnrolesdbEntityManagerFactory")
                        LocalContainerEntityManagerFactoryBean factoryBean) {
            return new JpaTransactionManager(factoryBean.getNativeEntityManagerFactory());
        }
    }

    //////// //////// //////// //////// //////// //////// //////// ////////

    private LocalContainerEntityManagerFactoryBean createEntityManagerFactoryBean(
            String script, String dbname, String packagesToScan) {
        var factoryBean = new LocalContainerEntityManagerFactoryBean();
        factoryBean.setDataSource(new EmbeddedDatabaseBuilder()
                .setType(EmbeddedDatabaseType.H2)
                .setName(dbname)
                .addScript(script)
                .build());
        factoryBean.setPersistenceUnitName(dbname + "EntityManagerFactory");
        factoryBean.setPackagesToScan(packagesToScan);
        factoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());

        var properties = new HashMap<String, Object>();
        properties.put("hibernate.hbm2ddl.auto", "none");
        properties.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
        factoryBean.setJpaPropertyMap(properties);

        return factoryBean;
    }
}

I prefer to use following approach (i don't like to create own bean configurator).我更喜欢使用以下方法(我不喜欢创建自己的 bean 配置器)。 A s @svr correctly noticed (see previous answer) you don't add starter package for beans auto configure. A s @svr 正确地注意到(请参阅上一个答案)您没有为 bean 自动配置添加启动包。 I usually create different profiles: for local app running, for dev, prod and finally last one for tests.我通常创建不同的配置文件:用于本地应用程序运行,用于开发、生产,最后一个用于测试。 In my test profile (application-functests.yml) i configure all settings that needs for my functional tests (datasources, hibernate, cache and so on), ie my application-functests.yml of one of my projects:在我的测试配置文件 (application-functests.yml) 中,我配置了功能测试所需的所有设置(数据源、休眠、缓存等),即我的一个项目的 application-functests.yml:

spring:
  http:
    encoding:
      charset: UTF-8
      enabled: true
  profiles: functests
  jpa:
    show-sql: true
    properties:
      hibernate:
        format_sql: true
        enable_lazy_load_no_trans: true
        naming:
            physical-strategy: com.goodt.drive.orgstructure.application.utils.SnakePhysicalNamingStrategy
    hibernate:
      ddl-auto: none
    database-platform: org.hibernate.dialect.PostgreSQL9Dialect    
  datasource:
    driver-class-name: org.postgresql.Driver
    url: jdbc:postgresql://localhost:5432/monitor_service_functests
    username: developer
    password: 123
    sql-script-encoding: UTF-8
  liquibase:
    change-log: classpath:db/changelog/changelog.xml

I have only specify profile for running test, therefore all of my functional tests are using functests profile, ie:我只为运行测试指定了配置文件,因此我所有的功能测试都使用 functests 配置文件,即:

@SpringBootTest
@ActiveProfiles("functests")
public class TestEventRepository extends FunctionalTestBase {
    
    @Test
    public void testGetAll() {
        Iterable<EventEntity> eventIterable = dbContext.getEventDataSource().findAll();
        Iterator<EventEntity> it = eventIterable.iterator();
        List<EventEntity> actualEvents = new ArrayList<>();
        while (it.hasNext()) {
            actualEvents.add(it.next());   
        }
        List<EventCheckData> expectedEvents = new ArrayList<>() {{
            add(new EventCheckData(1L, 1L, "body 1", 1L, 1L));
            add(new EventCheckData(2L, 2L, "body 2", 2L, 2L));
            add(new EventCheckData(3L, 3L, "body 3", 3L, 1L));
            add(new EventCheckData(4L, 1L, "body 4", 2L, 1L));
            add(new EventCheckData(5L, 2L, "body 5", 1L, 2L));
        }};
        EventSimpleChecker.check(expectedEvents, actualEvents);
    }
}

In my code example i have base test class - FunctionalTestBase which is used for interact with liquibase (toggle it to apply migrations)在我的代码示例中,我有基本测试类 - FunctionalTestBase 用于与 liquibase 交互(切换它以应用迁移)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM