使用 inMemory 数据库时出现 R2dbc H2 问题
Posted
技术标签:
【中文标题】使用 inMemory 数据库时出现 R2dbc H2 问题【英文标题】:R2dbc H2 issues when using inMemory database 【发布时间】:2020-04-23 02:25:36 【问题描述】:我尝试体验 R2dbc 并使用 Embedded 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 来创建表和初始化数据。
@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
时,它会失败,因为在初始化DataInitializer
时找不到表POSTS
。从启动日志看,ConnectionFactoryInitializer
初始化成功,创建表 POSTS 并通过执行 schema.sql 和 data.sql 按预期插入数据。
但是切换到使用.file("./testdb")
,它起作用了。
完整代码为here。
【问题讨论】:
【参考方案1】:我从 Spring Data R2dc 开发人员@mp911de 那里得到了答案。见#issue269
该问题与 H2 在关闭最后一个连接时关闭数据库的行为有关。请配置 DB_CLOSE_DELAY=-1 选项,以便 H2 保留数据库。或者,使用 H2ConnextionFactory.inMemory(...) 工厂方法创建一个可关闭的连接工厂,它不依赖于正在使用的连接数。
将我的代码更改为以下内容,它可以工作:
public ConnectionFactory connectionFactory()
return H2ConnectionFactory.inMemory("testdb");
【讨论】:
【参考方案2】:来自官方文档
ConnectionFactory
@Configuration
public class ApplicationConfiguration extends AbstractR2dbcConfiguration
@Override
@Bean
public ConnectionFactory connectionFactory()
return …;
这种方法让您可以使用标准的 io.r2dbc.spi.ConnectionFactory 例如,容器使用 Spring 的 AbstractR2dbc 配置。相比注册一个 ConnectionFactory 实例直接,配置支持有 还为容器提供了一个额外的优势 将 R2DBC 异常转换为的 ExceptionTranslator 实现 Spring 的可移植 DataAccessException 数据层次结构中的异常 访问使用@Repository 注解注解的类。
(...)
AbstractR2dbcConfiguration 还注册了 DatabaseClient,这是数据库交互和 Repository 实现所必需的。
我写了一篇关于如何设置 R2DBC 入门的博客文章here。 Here是一个使用H2数据库的例子
我的猜测是您没有正确初始化连接工厂,因此没有获得正确的 DatabaseClient。
【讨论】:
我不这么认为。从启动日志看,ConnectionFactory
没问题,ConnectionFactoryInitializer
初始化成功(创建和插入数据),但是当初始化 DataInitializer
插入另一个数据时,由于找不到表“posts”而失败。我提到使用.file("./testdb")
而不是InMemory
,它可以工作。
好吧显然不是这样,否则你不会在这里写。
@ThomasAndolf ,在您的文章中您提到了使用 postgresql 的 application.properties,但是:我们如何将 application.properties 与 H2DB 一起使用?
如果不设置属性,应用将默认使用H2DB以上是关于使用 inMemory 数据库时出现 R2dbc H2 问题的主要内容,如果未能解决你的问题,请参考以下文章
从 H2 数据库中读取 TINYINT 值作为布尔值/布尔值时出现 MappingException
何时使用 apollo-link-state 以及何时使用 apollo-cache-inmemory
带有 JPA 和 R2DBC 的 Spring Boot 2.4 混合项目无法启动
spring security oauth2 (2.0.8) 使用 InMemory tokenstore 获取无效访问令牌