在Spring Boot应用程序中使用R2DBC和Flyway设置H2

17

我正在学习Spring Boot和名为r2dbc的响应式JDBC驱动程序。在我的主要应用程序中,我使用Postgres作为数据库,现在我想在测试中使用h2。虽然Flyway迁移设置正常工作,但当Spring应用程序能够插入记录时发生问题。

以下是我的设置和代码:

@SpringBootTest
class CustomerRepositoryTest {

    @Autowired
    CustomerRepository repository;

    @Test
    void insertToDatabase() {
        repository.saveAll(List.of(new Customer("Jack", "Bauer"),
                new Customer("Chloe", "O'Brian"),
                new Customer("Kim", "Bauer"),
                new Customer("David", "Palmer"),
                new Customer("Michelle", "Dessler")))
                .blockLast(Duration.ofSeconds(10));
    }
}

这是我得到的错误

 :: Spring Boot ::        (v2.3.4.RELEASE)

2020-10-14 15:59:18.538  INFO 25279 --- [           main] i.g.i.repository.CustomerRepositoryTest  : Starting CustomerRepositoryTest on imalik8088.fritz.box with PID 25279 (started by imalik in /Users/imalik/code/private/explore-java/spring-example)
2020-10-14 15:59:18.540  INFO 25279 --- [           main] i.g.i.repository.CustomerRepositoryTest  : No active profile set, falling back to default profiles: default
2020-10-14 15:59:19.108  INFO 25279 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data R2DBC repositories in DEFAULT mode.
2020-10-14 15:59:19.273  INFO 25279 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 160ms. Found 1 R2DBC repository interfaces.
2020-10-14 15:59:19.894  INFO 25279 --- [           main] o.f.c.internal.license.VersionPrinter    : Flyway Community Edition 6.5.0 by Redgate
2020-10-14 15:59:20.052  INFO 25279 --- [           main] o.f.c.internal.database.DatabaseFactory  : Database: jdbc:h2:mem:///DBNAME (H2 1.4)
2020-10-14 15:59:20.118  INFO 25279 --- [           main] o.f.core.internal.command.DbValidate     : Successfully validated 1 migration (execution time 00:00.022s)
2020-10-14 15:59:20.131  INFO 25279 --- [           main] o.f.c.i.s.JdbcTableSchemaHistory         : Creating Schema History table "PUBLIC"."flyway_schema_history" ...
2020-10-14 15:59:20.175  INFO 25279 --- [           main] o.f.core.internal.command.DbMigrate      : Current version of schema "PUBLIC": << Empty Schema >>
2020-10-14 15:59:20.178  INFO 25279 --- [           main] o.f.core.internal.command.DbMigrate      : Migrating schema "PUBLIC" to version 1.0.0 - schma
2020-10-14 15:59:20.204  INFO 25279 --- [           main] o.f.core.internal.command.DbMigrate      : Successfully applied 1 migration to schema "PUBLIC" (execution time 00:00.036s)
2020-10-14 15:59:20.689  INFO 25279 --- [           main] i.g.i.repository.CustomerRepositoryTest  : Started CustomerRepositoryTest in 2.466 seconds (JVM running for 3.326)

2020-10-14 15:59:21.115 DEBUG 25279 --- [           main] o.s.d.r2dbc.core.DefaultDatabaseClient   : Executing SQL statement [INSERT INTO customer (first_name, last_name) VALUES ($1, $2)]


org.springframework.data.r2dbc.BadSqlGrammarException: executeMany; bad SQL grammar [INSERT INTO customer (first_name, last_name) VALUES ($1, $2)]; nested exception is io.r2dbc.spi.R2dbcBadGrammarException: [42102] [42S02] Tabelle "CUSTOMER" nicht gefunden
Table "CUSTOMER" not found; SQL statement:
INSERT INTO customer (first_name, last_name) VALUES ($1, $2) [42102-200]

我的 src/test/resources/application.yaml 文件看起来是这样的:

spring:
  r2dbc:
    url: r2dbc:h2:mem:///DBNAME?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
    username: sa
    password:

  flyway:
    url: jdbc:h2:mem:///DBNAME
    baseline-on-migrate: true
    user: sa
    password:

有什么遗漏或设置有问题吗?如果需要更多信息,请告诉我。

添加/解决方案:

jdbc和r2dbc之间的url模式不同。 对我来说正常工作的解决方案如下:

url: r2dbc:h2:file:///./tmp/test-database
url: jdbc:h2:file:./tmp/test-database

为了设置Flyway,您需要配置Flyway:

// Flyway is not compatible with r2dbc yet, therefore this config class is created
@Configuration
public class FlywayConfig {

    private final Environment env;

    public FlywayConfig(final Environment env) {
        this.env = env;
    }

    @Bean(initMethod = "migrate")
    public Flyway flyway() {
        return new Flyway(Flyway.configure()
                .baselineOnMigrate(true)
                .dataSource(
                        env.getRequiredProperty("spring.flyway.url"),
                        env.getRequiredProperty("spring.flyway.user"),
                        env.getRequiredProperty("spring.flyway.password"))
        );
    }
}

我按照Spring Boot 2.4.3的入门R2DBC教程进行了操作,但是发现ConnectionFactoryInitializer Bean已经被弃用。据此链接所示,我担心Spring Boot对于R2DBC中schema.sql/data.sql的支持将会被移除。建议从应用程序类中删除其Bean配置,并像您一样使用第三方工具,如FlywayLiquibase - dbaltor
5个回答

14

我曾经面临过相同的问题,需要设置并访问内存中的h2数据库进行测试:

  • 使用JDBC驱动程序的Liquibase进行数据库迁移
  • 使用R2DBC驱动程序的反应式CRUD存储库进行测试

遇到的错误:

org.springframework.data.r2dbc.BadSqlGrammarException:executeMany;bad SQL grammar [INSERT INTO MY_TABLE... Table "MY_TABLE" not found ...

受Chris解决方案的启发,我将我的src/testresources/application.properties文件配置如下:

spring.r2dbc.url=r2dbc:h2:mem:///~/db/testdb
spring.r2dbc.username=sa
spring.r2dbc.password=

spring.liquibase.url=jdbc:h2:mem:~/db/testdb;DB_CLOSE_DELAY=-1
spring.liquibase.user=sa
spring.liquibase.password=
spring.liquibase.enabled=true

8

我目前使用r2dbc和liquibase遇到了类似的问题。我怀疑由于R2DB和JDBC之间略有不同的语法,JDBC URL指向不同的数据库。不过,我可以通过文件系统运行h2...

    url: r2dbc:h2:file:///~/db/testdb
...
    url: jdbc:h2:file:~/db/testdb

编辑:

在非响应式的Spring Data中,我通常会使用schema.sql/data.sql一对文件将Schema填充到H2内存数据库中。这在R2DBC中也是可行的,但您必须自己配置填充程序。

入门R2DBC教程中也有相关说明。基本上,您需要注册一个ConnectionFactoryInitializer bean。

  @Bean
  public ConnectionFactoryInitializer initializer(@Qualifier("connectionFactory") ConnectionFactory connectionFactory) {
    var initializer = new ConnectionFactoryInitializer();
    initializer.setConnectionFactory(connectionFactory);

    var populator = new CompositeDatabasePopulator();
    populator.addPopulators(new ResourceDatabasePopulator(new ClassPathResource("schema.sql")));
    populator.addPopulators(new ResourceDatabasePopulator(new ClassPathResource("data.sql")));
    initializer.setDatabasePopulator(populator);

    return initializer;
  }

1
没错,它起作用了,谢谢。问题在于URL模式的不同语法,我无法将其配置为内存中的文件数据库,而是将其放在同一根文件夹中而不是家目录中。我会用解决方案扩展我的问题。 - imalik8088
我按照Spring Boot 2.4.3的入门R2DBC教程进行了操作,但是发现ConnectionFactoryInitializer Bean已经被弃用。我担心Spring Boot对于使用R2DBC的schema.sql/data.sql支持将会被移除,正如这里所指出的那样。 - dbaltor

2

我已经成功让它工作了。

首先,我创建了以下测试配置类(因为我只想针对H2执行测试,在生产模式下我使用PostgreSQL):

@TestConfiguration
public class TestConfig {
    @Bean
    @Profile("test")
    public ConnectionFactory connectionFactory() {
        System.out.println(">>>>>>>>>> Using H2 in mem R2DBC connection factory");
        return H2ConnectionFactory.inMemory("testdb");
    }

    @Bean(initMethod = "migrate")
    @Profile("test")
    public Flyway flyway() {
        System.out.println("####### Using H2 in mem Flyway connection");
        return new Flyway(Flyway.configure()
                .baselineOnMigrate(true)
                .dataSource(
                        "jdbc:h2:mem:testdb",
                        "sa",
                        "")
        );
    }
}

如您在上述代码中所见,这两个bean仅适用于“test”配置文件。可以想象,在常规的ApplicationConfiguration类中,我有几乎相同的bean,但是使用@Profile("default")进行注释,并配置为使用PostgreSQL。

第二件事是我创建了一个注释,将几个其他注释组合在一起,以避免重复并轻松获取在TestConfig类中声明的bean:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootTest
@ActiveProfiles("test")
@Import(TestConfig.class)
public @interface IntegrationTest {
}

现在进行测试本身:

@IntegrationTest
class CartsIntegrationTest {
 // test methods here ....
}

我相信主要的提示是使用H2ConnectionFactory.inMemory("testdb");


确实,关键是使用inMemory连接。我的解决方案没有包括2个不同的配置。但是总体思路是正确的:在主配置中定义Flyway bean,并在测试上下文中覆盖ConnectionFactory。 - anydoby

1

我在我的项目中遇到了两个问题。

  1. 我需要包含以下依赖:

    <dependency>
        <groupId>io.r2dbc</groupId>
        <artifactId>r2dbc-h2</artifactId>
        <scope>test</scope>
    </dependency>
    
  2. 我需要将 spring.r2dbc.url 的值更改为 r2dbc:h2:mem:///test_db

通过这些更改,rd2bc 可以使用内存中的 h2 数据库进行测试。另请参阅:

https://github.com/r2dbc/r2dbc-h2


0

Flyway目前仅支持阻塞式JDBC API,并且与响应式R2dbc不兼容。如果可能,请不要在同一应用程序中混合使用它们。

  1. 尝试注册ConnectionFactoryInitializer以启动数据库模式和数据,如@Chris发布的我的工作示例,可以在此处找到。

  2. 尝试nkonev/r2dbc-migrate,该项目正在尝试将Flyway迁移到R2dbc世界。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接