Проблемы с R2dbc H2 при использовании базы данных inMemory

Я пытался попробовать 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") в определении ConnectionFactory bean-компонента, когда Spring ApplicationContext инициализирован, это не удалось, потому что не удалось найти таблицу POSTS при инициализации DataInitializer. Из журнала запуска ConnectionFactoryInitializer инициализируется успешно, и создается таблица POSTS, и данные вставляются должным образом путем выполнения schema.sql и data.sql.

Но переключившись на .file("./testdb"), все заработало.

Полный код находится здесь.


person Hantsy    schedule 05.01.2020    source источник


Ответы (2)


Я получил ответ от разработчика Spring Data R2dc, @ mp911de. см. # issue269.

Проблема связана с поведением H2 при закрытии базы данных при закрытии последнего соединения. Настройте параметр DB_CLOSE_DELAY = -1, чтобы H2 сохранил базу данных. В качестве альтернативы используйте фабричный метод H2ConnextionFactory.inMemory (...), чтобы создать фабрику закрываемых соединений, которая не зависит от количества используемых соединений.

Измените мои коды на следующие, это работает:


 public ConnectionFactory connectionFactory() {       
     return H2ConnectionFactory.inMemory("testdb");
 }
person Hantsy    schedule 05.01.2020

Из официальной документации

ConnectionFactory

@Configuration
public class ApplicationConfiguration extends AbstractR2dbcConfiguration {

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

Этот подход позволяет вам использовать стандартный экземпляр io.r2dbc.spi.ConnectionFactory с контейнером, использующим Spring AbstractR2dbcConfiguration. По сравнению с регистрацией экземпляра ConnectionFactory напрямую, поддержка конфигурации имеет дополнительное преимущество, так как также предоставляет контейнеру реализацию ExceptionTranslator, которая переводит исключения R2DBC в исключения в переносимой иерархии DataAccessException Spring для классов доступа к данным, аннотированных аннотацией @Repository.

(...)

AbstractR2dbcConfiguration также регистрирует DatabaseClient, который требуется для взаимодействия с базой данных и для реализации репозитория.

Я написал в блоге сообщение о том, как настроить начало работы с R2DBC, здесь . Вот пример использования базы данных H2.

Я предполагаю, что вы неправильно инициализируете фабрику соединений и, следовательно, не получаете правильный DatabaseClient.

person Toerktumlare    schedule 05.01.2020
comment
Я так не думаю. Из журнала запуска ConnectionFactory в порядке, и ConnectionFactoryInitializer инициализация успешно (создание и вставка данных), но при инициализации DataInitializer для вставки других данных произошел сбой из-за не найденных сообщений таблицы. Я уже упоминал об использовании .file("./testdb") вместо InMemory, это работает. - person Hantsy; 05.01.2020
comment
ну, по-видимому, это не так, иначе вы бы здесь не написали. - person Toerktumlare; 05.01.2020
comment
@ThomasAndolf, в своей статье вы упомянули application.properties с использованием postgresql, но: Как мы можем использовать application.properties с H2DB? - person PaulDev; 10.07.2020
comment
Если вы не укажете свойства, приложение по умолчанию будет использовать H2DB. - person Toerktumlare; 10.07.2020