在Spring Boot应用中使用R2DBC和Flyway设置h2

huangapple go评论89阅读模式
英文:

Setup h2 in spring boot application with r2dbc and flyway

问题

以下是翻译好的内容:

我正在尝试使用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.
...(以下为更多日志)

我的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目前不与r2dbc兼容,因此创建了这个配置类
@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"))
        );
    }
}
英文:

I'm playing around with Spring Boot and the reactive jdbc driver called r2dbc. In my main application I'm using Postgres as a database and now I want to the use h2 for the tests. And the Flyway migration is working with the setup but when the Spring application is able to insert records.

Here is my setup and code

@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));
    }
}

Here is the error that I'm getting

 :: 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]

My src/test/resources/application.yaml is looking like this:

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:

Any ideas whats missing missing or whats wrong with the setup? If further information is needed please let me know.

Addition/Solution:

The url pattern is different between jdbc and r2dbc. The working solution for me is as follows:

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

And In order to setup Flyway you have to Configure 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"))
        );
    }
}

答案1

得分: 12

我在设置和访问用于测试的内存中的 h2 数据库时遇到了相同的问题:

  • 使用 JDBC 驱动程序的 Liquibase 进行数据库迁移
  • 使用 R2DBC 驱动程序测试响应式 Crud Repository

遇到的错误如下:
> org.springframework.data.r2dbc.BadSqlGrammarException: executeMany;坏的 SQL 语法 [INSERT INTO MY_TABLE... 表 "MY_TABLE" 未找到...

受到 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
英文:

I've faced the same issue to setup and access to h2 database in memory for tests:

  • Liquibase for database migration using JDBC driver
  • Tests Reactive Crud Repository using R2DBC driver

Error encoutred:<br />
> org.springframework.data.r2dbc.BadSqlGrammarException: executeMany; bad SQL grammar [INSERT INTO MY_TABLE... Table "MY_TABLE" not found ...

Inspired by Chris's solution, i configured my src/testresources/application.properties file as follow:

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

答案2

得分: 8

我目前正在使用r2dbc与liquibase遇到相同的问题。我怀疑JDBC URL指向不同的数据库,因为R2DB和JDBC之间的语法略有不同。不过,我可以从文件系统中成功运行h2...

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

编辑

在非响应式Spring Data中,我通常会使用schema.sql/data.sql对填充H2内存数据库的模式进行配置。这在R2DBC中也是可能的,但你必须自己配置填充器。

这也在Getting Started R2DBC Tutorial中有说明。基本上,你需要注册一个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;
  }
英文:

I am currently having the same problem using r2dbc with liquibase. I am suspecting that the JDBC url points to a different database due to a slightly different syntax between R2DB and JDBC. I can manage to get h2 running from the file system though...

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

EDIT:

In non-reactive Spring Data I'd usually populate the Schema into the H2 memory database using a schema.sql/data.sql pair. This is also possible with R2DBC, but you have to configure the populator yourself.

It's also in the Getting Started R2DBC Tutorial. Basically you have to register a ConnectionFactoryInitializer bean.

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

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

    return initializer;
  }

答案3

得分: 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 都仅适用于"profile"为 "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 {
    // 这里是测试方法....
}

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

英文:

I was able to get it working.

First of all I created following test configuration class (because I want to execute tests only agains H2, on production mode I am using PostgreSQL):

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

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

As you can see in the code above, both beans are scoped to the "test" profile only. As you can imagine I have pretty much the same beans in a regular ApplicationConfiguration class but annotated as a @Profile(&quot;default&quot;) and configured to use a PostgreSQL.

Second thing is that I created annotation which combines several other annotations to not repeat myself and to easily pickup beans declared in the TestConfig class:

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

Now the test itself:

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

I believe the main hint is to use H2ConnectionFactory.inMemory(&quot;testdb&quot;);

答案4

得分: 0

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

  1. 尝试注册ConnectionFactoryInitializer来初始化数据库模式和数据,就像@Chris发布的那样,我的工作示例可以在这里找到。

  2. 试试nkonev/r2dbc-migrate,它正试图将Flyway迁移到R2DBC世界。

英文:

Flyway currently only supports the blocking JDBC APIs, and it is not compatible with the reactive r2dbc if possbile do not mix them in the same application.

  1. Try to register a ConnectionFactoryInitializer to initiate the database schema and data as @Chris posted, my working example can be found here.

  2. Try nkonev/r2dbc-migrate which is trying to migrate the flyway to the R2dbc world.

答案5

得分: 0

有两个问题在我的项目中出现:

  1. 我需要包含这个依赖项:
<dependency>
    <groupId>io.r2dbc</groupId>
    <artifactId>r2dbc-h2</artifactId>
    <scope>test</scope>
</dependency>
  1. 我需要将 spring.r2dbc.url 的值更改为 r2dbc:h2:mem:///test_db

通过这些更改,rd2bc 与内存中的 h2 数据库一起工作,用于测试。另请参阅:

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

英文:

There were 2 issues I was experiencing in my project.

  1. I needed to include the dependency:

    &lt;dependency&gt;
    	&lt;groupId&gt;io.r2dbc&lt;/groupId&gt;
    	&lt;artifactId&gt;r2dbc-h2&lt;/artifactId&gt;
    	&lt;scope&gt;test&lt;/scope&gt;
    &lt;/dependency&gt;
    
  2. I needed to change the value for spring.r2dbc.url to r2dbc:h2:mem:///test_db

With these changes, rd2bc worked with an in memory h2 database for testing. See also:

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

huangapple
  • 本文由 发表于 2020年10月14日 22:07:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/64355106.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定