使用 r2dbc 和 flyway 在 spring 启动应用程序中设置 h2
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.
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 (, )]
org.springframework.data.r2dbc.BadSqlGrammarException: executeMany; bad SQL grammar [INSERT INTO customer (first_name, last_name) VALUES (, )]; 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 (, ) [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:
有什么想法遗漏了什么或设置有什么问题吗?如果需要更多信息,请告诉我。
Addition/Solution:
url 模式在 jdbc 和 r2dbc 之间是不同的。我的工作解决方案如下:
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"))
);
}
}
我目前在将 r2dbc 与 liquibase 结合使用时遇到同样的问题。我怀疑 JDBC url 指向不同的数据库,因为 R2DB 和 JDBC 之间的语法略有不同。不过,我可以设法从文件系统中获取 h2 运行...
url: r2dbc:h2:file:///~/db/testdb
...
url: jdbc:h2:file:~/db/testdb
编辑:
在 non-reactive Spring 数据中,我通常使用模式将模式填充到 H2 内存数据库中。sql/data.sql 对。使用 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;
}
Flyway 目前仅支持阻塞 JDBC API,如果可能不将它们混合在同一个应用程序中,它与反应式 r2dbc 不兼容。
尝试注册一个 ConnectionFactoryInitializer
以启动数据库模式和数据,正如@Chris 发布的那样,my working example 可以在这里找到。
尝试 nkonev/r2dbc-migrate,它正在尝试将 flyway 迁移到 R2dbc 世界。
我在设置和访问内存中的 h2 数据库进行测试时遇到了同样的问题:
- 使用 JDBC 驱动程序进行数据库迁移的 Liquibase
- 使用 R2DBC 驱动程序测试 Reactive 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
我能够让它工作。
首先我创建了以下测试配置class(因为我只想再次对 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 的范围仅限于“测试”配置文件。正如您可以想象的那样,我在常规 ApplicationConfiguration class 中有几乎相同的 bean,但注释为 @Profile("default")
并配置为使用 PostgreSQL。
第二件事是我创建了结合其他几个注释的注释,以便不重复我自己并轻松拾取在 TestConfig
class:
中声明的 beans
@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");
我正在玩 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 (, )]
org.springframework.data.r2dbc.BadSqlGrammarException: executeMany; bad SQL grammar [INSERT INTO customer (first_name, last_name) VALUES (, )]; 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 (, ) [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:
有什么想法遗漏了什么或设置有什么问题吗?如果需要更多信息,请告诉我。
Addition/Solution:
url 模式在 jdbc 和 r2dbc 之间是不同的。我的工作解决方案如下:
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"))
);
}
}
我目前在将 r2dbc 与 liquibase 结合使用时遇到同样的问题。我怀疑 JDBC url 指向不同的数据库,因为 R2DB 和 JDBC 之间的语法略有不同。不过,我可以设法从文件系统中获取 h2 运行...
url: r2dbc:h2:file:///~/db/testdb
...
url: jdbc:h2:file:~/db/testdb
编辑:
在 non-reactive Spring 数据中,我通常使用模式将模式填充到 H2 内存数据库中。sql/data.sql 对。使用 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;
}
Flyway 目前仅支持阻塞 JDBC API,如果可能不将它们混合在同一个应用程序中,它与反应式 r2dbc 不兼容。
尝试注册一个
ConnectionFactoryInitializer
以启动数据库模式和数据,正如@Chris 发布的那样,my working example 可以在这里找到。尝试 nkonev/r2dbc-migrate,它正在尝试将 flyway 迁移到 R2dbc 世界。
我在设置和访问内存中的 h2 数据库进行测试时遇到了同样的问题:
- 使用 JDBC 驱动程序进行数据库迁移的 Liquibase
- 使用 R2DBC 驱动程序测试 Reactive 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
我能够让它工作。
首先我创建了以下测试配置class(因为我只想再次对 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 的范围仅限于“测试”配置文件。正如您可以想象的那样,我在常规 ApplicationConfiguration class 中有几乎相同的 bean,但注释为 @Profile("default")
并配置为使用 PostgreSQL。
第二件事是我创建了结合其他几个注释的注释,以便不重复我自己并轻松拾取在 TestConfig
class:
@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");