Пакет org.springframework.jdbc.datasource.embedded обеспечивает поддержку встроенных подсистем хранения на Java. Поддержка HSQL, H2 и Derby обеспечивается нативно. Также можно использовать расширяемый API-интерфейс для подключения новых встроенных типов баз данных и реализаций DataSource.

Зачем использовать встроенную базу данных?

Встроенная база данных может быть полезна на этапе разработки проекта благодаря своей упрощенности. Среди преимуществ простота настройки, быстрое время запуска, тестируемость и возможность быстрой доработки SQL в процессе разработки.

Создание встроенной базы данных с помощью Spring XML

Если вам нужно представить экземпляр встроенной базы данных в качестве бина в ApplicationContext в Spring, можно использовать тег embedded-database в пространстве имен spring-jdbc:

<jdbc:embedded-database id="dataSource" generate-name="true">
    <jdbc:script location="classpath:schema.sql"/>
    <jdbc:script location="classpath:test-data.sql"/>
</jdbc:embedded-database>

Предыдущая конфигурация создает встроенную базу данных HSQL, которая заполняется SQL из ресурсов schema.sql и test-data.sql в корне classpath. Кроме того, в качестве оптимального метода работы встроенной базе данных присваивается уникальное имя. Встроенная база данных становится доступной для контейнера Spring как бин типа javax.sql.DataSource, который затем можно внедрять в объекты доступа к данным по мере необходимости.

Создание встроенной базы данных программным способом

Класс EmbeddedDatabaseBuilder предоставляет свободный API-интерфейс для создания встроенной базы данных программным путем. Это можно использовать, если нужно создать встроенную базу данных в автономном окружении или в автономном интеграционном тесте, как в следующем примере:

Java
EmbeddedDatabase db = new EmbeddedDatabaseBuilder()
        .generateUniqueName(true)
        .setType(H2)
        .setScriptEncoding("UTF-8")
        .ignoreFailedDrops(true)
        .addScript("schema.sql")
        .addScripts("user_data.sql", "country_data.sql")
        .build();
// выполняем действия с базой данных (EmbeddedDatabase расширяет javax.sql.DataSource)
db.shutdown()
Kotlin
val db = EmbeddedDatabaseBuilder()
        .generateUniqueName(true)
        .setType(H2)
        .setScriptEncoding("UTF-8")
        .ignoreFailedDrops(true)
        .addScript("schema.sql")
        .addScripts("user_data.sql", "country_data.sql")
        .build()
// выполняем действия с базой данных (EmbeddedDatabase расширяет javax.sql.DataSource)
db.shutdown()

См. javadoc по EmbeddedDatabaseBuilder для получения более подробной информации обо всех поддерживаемых опциях.

Также можно использовать EmbeddedDatabaseBuilder для создания встроенной базы данных с помощью конфигурации Java, как показано в следующем примере:

Java
@Configuration
public class DataSourceConfig {
    @Bean
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder()
                .generateUniqueName(true)
                .setType(H2)
                .setScriptEncoding("UTF-8")
                .ignoreFailedDrops(true)
                .addScript("schema.sql")
                .addScripts("user_data.sql", "country_data.sql")
                .build();
    }
}
Kotlin
@Configuration
class DataSourceConfig {
    @Bean
    fun dataSource(): DataSource {
        return EmbeddedDatabaseBuilder()
                .generateUniqueName(true)
                .setType(H2)
                .setScriptEncoding("UTF-8")
                .ignoreFailedDrops(true)
                .addScript("schema.sql")
                .addScripts("user_data.sql", "country_data.sql")
                .build()
    }
}

Выбор типа встроенной базы данных

В этом разделе рассказывается о том, как выбрать одну из трех встроенных баз данных, которые поддерживает Spring. Раздел включает в себя следующие темы:

  • Использование HSQL

  • Использование H2

  • Использование Derby

Использование HSQL

Spring поддерживает HSQL 1.8.0 и выше. HSQL является встроенной базой данных по умолчанию, если тип не задан явным образом. Чтобы явно задать HSQL, установите для атрибута type тега embedded-database значение HSQL. Если вы используете API-интерфейс сресдтва сборки, вызовите метод setType(EmbeddedDatabaseType) с параметром EmbeddedDatabaseType.HSQL.

Использование H2

Spring поддерживает базу данных H2. Чтобы активировать H2, установите для атрибута type тега embedded-database значение H2. Если вы используете API-интерфейс средства сборки, вызовите метод setType(EmbeddedDatabaseType) с параметром EmbeddedDatabaseType.H2.

Использование Derby

Spring поддерживает Apache Derby 10.5 и выше. Чтобы активировать Derby, установите для атрибута type тега embedded-database значение DERBY. Если вы используете API-интерфейс средства сборки, вызовите метод setType(EmbeddedDatabaseType) с параметром EmbeddedDatabaseType.DERBY.

Тестирование логики доступа к данным с использованием встроенной базы данных

Встроенные базы данных обеспечивают легкий способ тестирования кода для получения выборки данных. Следующий пример - шаблон интеграционного теста доступа к данным, в котором используется встроенная база данных. Использование такого шаблона может быть полезно для разовых случаев, если встроенную базу данных не нужно использовать повторно во всех тестовых классах. Однако если нужно создать встроенную базу данных, которая будет использоваться совместно в тестовом комплекте, используйте Spring TestContext Framework и сконфигурируйте встроенную базу данных как бин в ApplicationContext из Spring, как это описано выше. В следующем листинге показан тестовый шаблон:

Java
public class DataAccessIntegrationTestTemplate {
    private EmbeddedDatabase db;
    @BeforeEach
    public void setUp() {
        // создает резидентную (с хранением в оперативной памяти) базу данных HSQL, заполненную скриптами по умолчанию
        // classpath:schema.sql и classpath:data.sql
        db = new EmbeddedDatabaseBuilder()
                .generateUniqueName(true)
                .addDefaultScripts()
                .build();
    }
    @Test
    public void testDataAccess() {
        JdbcTemplate template = new JdbcTemplate(db);
        template.query( /* ... */ );
    }
    @AfterEach
    public void tearDown() {
        db.shutdown();
    }
}
Kotlin
class DataAccessIntegrationTestTemplate {
    private lateinit var db: EmbeddedDatabase
    @BeforeEach
    fun setUp() {
        // создает резидентную (с хранением в оперативной памяти) базу данных HSQL, заполненную скриптами по умолчанию
        // classpath:schema.sql и classpath:data.sql
        db = EmbeddedDatabaseBuilder()
                .generateUniqueName(true)
                .addDefaultScripts()
                .build()
    }
    @Test
    fun testDataAccess() {
        val template = JdbcTemplate(db)
        template.query( /* ... */)
    }
    @AfterEach
    fun tearDown() {
        db.shutdown()
    }
}

Создание уникальных имен для встроенных баз данных

Команды разработчиков часто сталкиваются с ошибками при работе со встроенными базами данных, если их тестовый комплект случайно пытается воссоздать дополнительные экземпляры одной и той же базы данных. Это довольно легко может произойти, если файл конфигурации XML или класс с аннотацией @Configuration отвечает за создание встроенной базы данных, а соответствующая конфигурация затем повторно используется в нескольких сценариях тестирования в рамках одного тестового комплекта (то есть в рамках одного процесса JVM) – например, интеграционные тесты в отношении встроенных баз данных, конфигурация ApplicationContext которых отличается только тем, какие профили определения бинов активны.

Первопричиной таких ошибок является тот факт, что фабрика EmbeddedDatabaseFactory из Spring (используемая внутренним способом как элементом пространства имен XML <jdbc:embedded-database>, так и конфигурацией EmbeddedDatabaseBuilder для Java) устанавливает имя встроенной базы данных на testdb, если не задано иное. В случае <jdbc:embedded-database>, встроенной базе данных обычно присваивается имя, равное id бина (зачастую наподобие dataSource). Таким образом, последующие попытки создать встроенную базу данных не приводят к созданию новой базы данных. Вместо этого повторно используется один и тот же URL-адрес подключения JDBC, а попытки создать новую встроенную базу данных фактически указывают на существующую встроенную базу данных, созданную на основе той же конфигурации.

Для решения этой распространенной проблемы в Spring Framework 4.2 предусмотрены средства поддержки генерации уникальных имен для встроенных баз данных. Чтобы активировать функцию использования сгенерированных имен, используйте один из следующих параметров.

  • EmbeddedDatabaseFactory.setGenerateUniqueDatabaseName()

  • EmbeddedDatabaseBuilder.generateUniqueName()

  • <jdbc:embedded-database generate-name="true" …​ >

Расширение поддержки встроенных баз данных

Можно расширить средства поддержки встроенных баз данных JDBC в Spring двумя способами:

  • Путем реализации EmbeddedDatabaseConfigurer для обеспечения поддержки нового типа встроенной базы данных.

  • Путем реализации DataSourceFactory для обеспечения поддержки новой реализации DataSource, например, пула соединений, чтобы управлять подключениями к встроенной базе данных.

Мы призываем вас вносить свои предложения касательно расширений в сообществе Spring на GitHub Issues.