6

我试图品尝 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..."));
    }

}

如果我.inMemory("testdb")ConnectionFactorybean定义中使用,当SpringApplicationContext初始化时,它会失败,因为初始化时找不到表POSTSDataInitializer。从启动日志开始,ConnectionFactoryInitializer初始化成功,并通过执行 schema.sql 和 data.sql 创建表POSTS并按预期插入数据。

但是切换到使用.file("./testdb"),它起作用了。

完整的代码在这里

4

2 回答 2

4

我从 Spring Data R2dc 开发人员@mp911de 那里得到了答案。见#issue269

该问题与 H2 在关闭最后一个连接时关闭数据库的行为有关。请配置 DB_CLOSE_DELAY=-1 选项,以便 H2 保留数据库。或者,使用 H2ConnextionFactory.inMemory(...) 工厂方法创建不依赖于使用中连接计数的可关闭连接工厂。

将我的代码更改为以下内容,它可以工作:


 public ConnectionFactory connectionFactory() {       
     return H2ConnectionFactory.inMemory("testdb");
 }
于 2020-01-05T11:28:38.563 回答
4

来自官方文档

连接工厂

@Configuration
public class ApplicationConfiguration extends AbstractR2dbcConfiguration {

  @Override
  @Bean
  public ConnectionFactory connectionFactory() {
    return …;
  }
}

这种方法允许您使用标准 io.r2dbc.spi.ConnectionFactory 实例,容器使用 Spring 的 AbstractR2dbcConfiguration。与直接注册 ConnectionFactory 实例相比,配置支持还具有额外的优势,即还为容器提供了 ExceptionTranslator 实现,该实现将 R2DBC 异常转换为 Spring 的可移植 DataAccessException 层次结构中的异常,用于使用 @Repository 注释注释的数据访问类。

(...)

AbstractR2dbcConfiguration 还注册了 DatabaseClient,这是数据库交互和 Repository 实现所必需的。

我在这里写了一篇关于如何设置 R2DBC 入门的博客文章。是一个使用 H2 数据库的示例

我的猜测是您没有正确初始化连接工厂,因此没有获得正确的 DatabaseClient。

于 2020-01-05T04:39:40.770 回答