Baza danych H2 — prototypowanie

Autor
Damian
Terlecki
6 minut
Bazy danych

Warstwa danych jest zazwyczaj podstawowym elementem architektury aplikacji. Nie tylko podczas tworzenia samej aplikacji, ale również w razie przygotowywania małych programów poglądowych, jak i przypadków testowych, zachodzi potrzeba wykorzystania czegoś lekkiego w tym zakresie. W niektórych przypadkach jedynym wymogiem dla bazy danych jest możliwość jej szybkiego skonfigurowania i uruchomienia bez nadmiernej pracy i zastanowienia. Często nie jest również konieczna żaden interfejs użytkownika. Zwykle tak jest w przypadku wykonywania testów automatycznych/integracyjnych. Jednakże są sytuacje, gdy podczas prototypowania bądź tworzenia testów w oparciu o bazę danych, zajdzie potrzeba dostępu i sprawdzenia struktury bądź danych na bazie.

Początkowo sam korzystałem ze standardowych autonomicznych baz danych takich jak MySQL lub Oracle DB. Do przyjemności nie zaliczyłbym konieczność ich ustawiania dla każdego nowego środowiska ani proszenia o to kogoś, kto chciałby rzucić okiem na aplikację u siebie. Dopiero gdy usłyszałem o Dockerze, stało się to o wiele lżejsze. Z czasem jednak zdałem sobie sprawę z tego, że nawet taki nakład pracy nie jest współmierny w przypadku małych projektów i próbek demo. Od tego czasu zacząłem używać osadzalnych baz danych typu HSQLDB, Apache Derby i SQLite z możliwością ich uruchamiania w pamięci bądź z pliku.

Dopiero niedawno odkryłem, że jedna z baz - H2, z której miałem okazję skorzystać, ma tak naprawdę wszystko, czego potrzebuję do prototypowania i testów. Mogę ją podpiąć niemal natychmiastowo wykorzystując przy okazji Spring Boota, dodając zależność do pliku pom:

<dependency><br />
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.4.194</version>
</dependency>

Zakres (<scope></scope>) wybieram w zależności od tego, czego potrzebuję. Compile — jeśli chcę użyć dodatkowych narzędzi z biblioteki. Runtime w przypadku standardowego korzystania z aplikacji (prototypowanie). Ostatecznie, test — do uruchamiania testów integracyjnych. Daje mi to poczucie swobody i prostoty, bo taka konfiguracja jest już wystarczająca do podpięcia bazy danych. Nie muszę przejmować się ani instalacją, ani uruchamianiem i inicjalizacją bazy danych — tym drugim zajmuje się Spring Boot (starter JPA).

Konsola i tryb uruchomienia w pamięci

Konsola H2 — logowanie

Co więcej, paczka H2 wyposażona jest w narzędzia zapewniające dostęp do bazy poprzez konsolę. Używając Spring Boot (wraz z web starterem) można ją włączyć, dodając spring.h2.console.enabled=true do pliku application.properties. W ten sposób konsola internetowa będzie dostępna pod standardowym portem i ścieżką h2-console np. http://localhost:8080/h2-console. Zakładając, że przygotowaliśmy klasy encji, zostaną one automatycznie zainicjalizowane w bazie danych w pamięci dostępnej poprzez adres jdbc:h2:mem:testdb.

Konsola H2

Nie używasz Spring Boota? Bez obaw, uruchomienie serwerów jest równie proste, jak wywołanie org.h2.tools.Server.main(). W kontekście serwletów pomocna może być klasa org.h2.server.web.DbStarter. Bardziej szczegółowe wyjaśnienie znajduje się w dokumentacji. Znajdziesz tam również sekcję "zaawansowane", w której zawarte są informacje na temat użytkowania bazy w innych frameworkach/językach (np. .NET). Ręczne połączenie z bazą danych włącznie z uruchomieniem konsoli internetowej używając standardowej Javy, można zainicjować poniższym kodem:

Connection connection = DriverManager.getConnection("jdbc:h2:mem:testdb", "sa", "");
Statement statement = connection.createStatement();
if(statement.execute("Select * from dual")) {
    System.out.println("Successfully connected to the jdbc:h2:mem:testdb");
}
Server.startWebServer(connection);

Tym sposobem konsola powinna również otworzyć się w przeglądarce pod jednym z losowych portów.

Tryb serwerowy

Kolejny przydatny przypadek do rozważenia to połączenie z bazą danych spoza aplikacji, z innego procesu/hosta. Jest to zazwyczaj dosyć problematyczna sprawa z tego względu, że baza danych jest uruchomiona w pamięci. Standardowo można się z nią połączyć z tej samej wirtualnej maszyny i class loadera. Jednakże, istnieje również opcja umożliwienia dostępu do niej poprzez serwer TCP, dzięki narzędziom H2. Przykładowy bean w Springu, odpowiedzialny za stworzenie, wystartowanie i zamknięcie serwera można zdefiniować następująco:

@Bean(initMethod = "start", destroyMethod = "stop")
public Server inMemoryH2DatabaseServer() throws SQLException {
    return Server.createTcpServer("-tcp", "-tcpAllowOthers", "-tcpPort", "9090");
}

Baza będzie teraz dostępna na porcie 9090. Połączymy się z nią korzystając ze sterownika jdbc (org.h2.Driver) i ścieżki jdbc:h2:tcp://localhost:9090/mem:testdb. Jest to tak zwany tryb serwerowy. Dzięki niemu możliwa jest komunikacja z bazą z poziomu innej aplikacji np. JMetera — w celu weryfikacji danych bądź przeprowadzenia testów obciążeniowych. Aby użyć klasy org.h2.tools.Server, konieczne jest dodanie zależności H2 z zakresem compile.

Tryb osadzony

Możliwe jest również uruchomienie bazy danych w trybie osadzonym. W ten sposób nasze dane będą przechowywane i utrzymywane w pliku. Nie utracimy ich po zamknięciu i ponownym uruchomieniu aplikacji. Ustawienie tego trybu w Spring Boocie jest dosyć proste — wystarczy w pliku application.properties zdefiniować ścieżkę do stworzenia pliku bazy danych:

spring.datasource.url=jdbc:h2:./testdb
spring.jpa.hibernate.ddl-auto=update

Konieczne jest użycie spring.jpa.hibernate.ddl-auto=update w celu nadpisania standardowej wartości create-drop. Nie ma również żadnych problemów z łączeniem różnych trybów i osadzonego możemy użyć do standardowego działania aplikacji, a bazę uruchamiać w pamięci w celu uruchomienia testów. Wystarczy utworzyć oddzielny plik properties dla testów w katalogu test/resources.

Jakkolwiek dobrze brzmiące, każde podejście zawsze ma jakieś wady i zalety. Dlatego też to do nas należy wybór odpowiedniego narzędzia do rozwiązania napotkanego problemu.