使用 inMemory 数据库时出现 R2dbc H2 问题
R2dbc H2 issues when using inMemory database
我尝试尝试 R2dbc 并使用嵌入式 H2,例如:
public ConnectionFactory connectionFactory() {
//ConnectionFactory factory = ConnectionFactories.get("r2dbc:h2:mem:///test?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE");
return new H2ConnectionFactory(
H2ConnectionConfiguration.builder()
//.inMemory("testdb")
.file("./testdb")
.username("user")
.password("password").build()
);
}
我定义了一个 bean 来创建 tables 和初始化数据。
@Bean
public ConnectionFactoryInitializer initializer(ConnectionFactory connectionFactory) {
ConnectionFactoryInitializer initializer = new ConnectionFactoryInitializer();
initializer.setConnectionFactory(connectionFactory);
CompositeDatabasePopulator populator = new CompositeDatabasePopulator();
populator.addPopulators(new ResourceDatabasePopulator(new ClassPathResource("schema.sql")));
populator.addPopulators(new ResourceDatabasePopulator(new ClassPathResource("data.sql")));
initializer.setDatabasePopulator(populator);
return initializer;
}
而且我还定义了另一个组件来通过java代码设置数据。
@Component
@Slf4j
class DataInitializer {
private final DatabaseClient databaseClient;
public DataInitializer(DatabaseClient databaseClient) {
this.databaseClient = databaseClient;
}
@EventListener(value = ContextRefreshedEvent.class)
public void init() {
log.info("start data initialization ...");
this.databaseClient.insert()
.into("posts")
//.nullValue("id", Integer.class)
.value("title", "First post title")
.value("content", "Content of my first post")
.map((r, m) -> r.get("id", Integer.class))
.all()
.log()
.thenMany(
this.databaseClient.select()
.from("posts")
.orderBy(Sort.by(desc("id")))
.as(Post.class)
.fetch()
.all()
.log()
)
.subscribe(null, null, () -> log.info("initialization done..."));
}
}
如果我在ConnectionFactory
bean定义中使用了.inMemory("testdb")
,当初始化Spring ApplicationContext
时,它失败了,因为找不到table POSTS
初始化时DataInitializer
。从启动日志中,ConnectionFactoryInitializer
初始化成功,table POSTS 被创建并通过执行 schema.sql 和 data.sql。
但是切换到使用 .file("./testdb")
,它奏效了。
完整代码为here.
来自官方文档
@Configuration
public class ApplicationConfiguration extends AbstractR2dbcConfiguration {
@Override
@Bean
public ConnectionFactory connectionFactory() {
return …;
}
}
This approach lets you use the standard io.r2dbc.spi.ConnectionFactory
instance, with the container using Spring’s
AbstractR2dbcConfiguration. As compared to registering a
ConnectionFactory instance directly, the configuration support has the
added advantage of also providing the container with an
ExceptionTranslator implementation that translates R2DBC exceptions to
exceptions in Spring’s portable DataAccessException hierarchy for data
access classes annotated with the @Repository annotation.
(...)
AbstractR2dbcConfiguration also registers DatabaseClient, which is required for database interaction and for Repository implementation.
我写了一篇博客post关于如何设置R2DBC入门here. Here是一个使用H2数据库的例子
我的猜测是您没有正确初始化连接工厂,因此没有获得正确的 DatabaseClient。
我从 Spring Data R2dc 开发人员 @mp911de 那里得到了答案。参见 #issue269
The issue is related to H2‘s behavior to close the database when the last connection gets closed. Please configure the DB_CLOSE_DELAY=-1 option so H2 retains the database. Alternatively, use the H2ConnextionFactory.inMemory(...) factory method to create a Closeable connection factory that does not depend on the in-use connection count.
将我的代码更改为以下,有效:
public ConnectionFactory connectionFactory() {
return H2ConnectionFactory.inMemory("testdb");
}
我尝试尝试 R2dbc 并使用嵌入式 H2,例如:
public ConnectionFactory connectionFactory() {
//ConnectionFactory factory = ConnectionFactories.get("r2dbc:h2:mem:///test?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE");
return new H2ConnectionFactory(
H2ConnectionConfiguration.builder()
//.inMemory("testdb")
.file("./testdb")
.username("user")
.password("password").build()
);
}
我定义了一个 bean 来创建 tables 和初始化数据。
@Bean
public ConnectionFactoryInitializer initializer(ConnectionFactory connectionFactory) {
ConnectionFactoryInitializer initializer = new ConnectionFactoryInitializer();
initializer.setConnectionFactory(connectionFactory);
CompositeDatabasePopulator populator = new CompositeDatabasePopulator();
populator.addPopulators(new ResourceDatabasePopulator(new ClassPathResource("schema.sql")));
populator.addPopulators(new ResourceDatabasePopulator(new ClassPathResource("data.sql")));
initializer.setDatabasePopulator(populator);
return initializer;
}
而且我还定义了另一个组件来通过java代码设置数据。
@Component
@Slf4j
class DataInitializer {
private final DatabaseClient databaseClient;
public DataInitializer(DatabaseClient databaseClient) {
this.databaseClient = databaseClient;
}
@EventListener(value = ContextRefreshedEvent.class)
public void init() {
log.info("start data initialization ...");
this.databaseClient.insert()
.into("posts")
//.nullValue("id", Integer.class)
.value("title", "First post title")
.value("content", "Content of my first post")
.map((r, m) -> r.get("id", Integer.class))
.all()
.log()
.thenMany(
this.databaseClient.select()
.from("posts")
.orderBy(Sort.by(desc("id")))
.as(Post.class)
.fetch()
.all()
.log()
)
.subscribe(null, null, () -> log.info("initialization done..."));
}
}
如果我在ConnectionFactory
bean定义中使用了.inMemory("testdb")
,当初始化Spring ApplicationContext
时,它失败了,因为找不到table POSTS
初始化时DataInitializer
。从启动日志中,ConnectionFactoryInitializer
初始化成功,table POSTS 被创建并通过执行 schema.sql 和 data.sql。
但是切换到使用 .file("./testdb")
,它奏效了。
完整代码为here.
来自官方文档
@Configuration
public class ApplicationConfiguration extends AbstractR2dbcConfiguration {
@Override
@Bean
public ConnectionFactory connectionFactory() {
return …;
}
}
This approach lets you use the standard io.r2dbc.spi.ConnectionFactory instance, with the container using Spring’s AbstractR2dbcConfiguration. As compared to registering a ConnectionFactory instance directly, the configuration support has the added advantage of also providing the container with an ExceptionTranslator implementation that translates R2DBC exceptions to exceptions in Spring’s portable DataAccessException hierarchy for data access classes annotated with the @Repository annotation.
(...)
AbstractR2dbcConfiguration also registers DatabaseClient, which is required for database interaction and for Repository implementation.
我写了一篇博客post关于如何设置R2DBC入门here. Here是一个使用H2数据库的例子
我的猜测是您没有正确初始化连接工厂,因此没有获得正确的 DatabaseClient。
我从 Spring Data R2dc 开发人员 @mp911de 那里得到了答案。参见 #issue269
The issue is related to H2‘s behavior to close the database when the last connection gets closed. Please configure the DB_CLOSE_DELAY=-1 option so H2 retains the database. Alternatively, use the H2ConnextionFactory.inMemory(...) factory method to create a Closeable connection factory that does not depend on the in-use connection count.
将我的代码更改为以下,有效:
public ConnectionFactory connectionFactory() {
return H2ConnectionFactory.inMemory("testdb");
}