Banco de dados H2 com Spring Boot

Por Gaspar Barancelli Junior em 22 de fevereiro de 2023

Pré-requisitos

Conhecimentos básicos em:

  • Maven/Gradle

  • JPA

  • Spring Boot

  • Spring Data

H2

H2 é um banco de dados relacional escrito em Java. Ele pode ser integrado em aplicativos Java ou executado no modo cliente-servidor.

Todos os modos contam com suporte para bancos de dados persistentes e na memória. Não há limite para o número de bancos de dados abertos simultaneamente ou para o número de conexões abertas.

Modo Integrado

No modo integrado, um aplicativo abre um banco de dados de dentro da mesma JVM usando JDBC. Este é o modo de conexão mais rápido e fácil. A desvantagem é que um banco de dados só pode ser aberto em uma máquina virtual.

Neste modo, as operações de I/O são realizadas por threads que executam um comando SQL. O aplicativo não pode interromper essas Threads, pois pode levar à corrupção do banco de dados.

Modo Servidor

Ao usar o modo de servidor, um aplicativo abre um banco de dados remotamente usando a API JDBC ou ODBC. Um servidor precisa ser iniciado na mesma ou em outra máquina virtual, ou em outro computador. Muitos aplicativos podem se conectar ao mesmo banco de dados ao mesmo tempo, conectando-se a este servidor. Internamente, o processo do servidor abre o(s) banco(s) de dados no modo integrado.

O modo de servidor é mais lento do que o modo integrado, porque todos os dados são transferidos por TCP/IP.

Modo misto

O modo misto é uma combinação do modo integrado e do modo de servidor. O primeiro aplicativo que se conecta a um banco de dados faz isso no modo integrado, mas também inicia um servidor para que outros aplicativos (em execução em diferentes processos ou máquinas virtuais) possam acessar simultaneamente os mesmos dados. As conexões locais são tão rápidas como se o banco de dados fosse usado apenas no modo integrado, enquanto as conexões remotas são um pouco mais lentas.

O servidor pode ser iniciado e parado de dentro do aplicativo (usando a API do servidor) ou automaticamente (modo misto automático). Ao usar o modo misto automático, todos os clientes que desejam se conectar ao banco de dados (não importa se é uma conexão local ou remota) podem fazer isso usando exatamente a mesmo URL do banco de dados

Visão geral da URL do banco de dados

Table 1. 1
Tema Formato de URL e exemplos

Conexão integrada (local)

jdbc:h2:[file:][<path>]<databaseName>
jdbc:h2:~/test
jdbc:h2:file:/data/sample
jdbc:h2:file:C:/data/sample (Windows only)

Na memória (particular)

jdbc:h2:mem:

Na memória (nomeado)

jdbc:h2:mem:<databaseName>
jdbc:h2:mem:test_mem

Modo de servidor (conexões remotas) usando TCP / IP

jdbc:h2:tcp://<server>[:<port>]/[<path>]<databaseName>
jdbc:h2:tcp://localhost/~/test
jdbc:h2:tcp://dbserv:8084/~/sample
jdbc:h2:tcp://localhost/mem:test

Modo de servidor (conexões remotas) usando TLS

jdbc:h2:ssl://<server>[:<port>]/[<path>]<databaseName>
jdbc:h2:ssl://localhost:8085/~/sample;

Usando arquivos criptografados

jdbc:h2:<url>;CIPHER=AES
jdbc:h2:ssl://localhost/~/test;CIPHER=AES
jdbc:h2:file:~/secure;CIPHER=AES

Métodos de bloqueio de arquivo

jdbc:h2:<url>;FILE_LOCK={FILE-SOCKET-NO}
jdbc:h2:file:~/private;CIPHER=AES;FILE_LOCK=SOCKET

Abra apenas se já existir

jdbc:h2:<url>;IFEXISTS=TRUE
jdbc:h2:file:~/sample;IFEXISTS=TRUE

Não feche o banco de dados quando a VM sair

jdbc:h2:<url>;DB_CLOSE_ON_EXIT=FALSE

Execute SQL na conexão

jdbc:h2:<url>;INIT=RUNSCRIPT FROM '~/create.sql'
jdbc:h2:file:~/sample;INIT=RUNSCRIPT FROM '~/create.sql'\;RUNSCRIPT FROM '~/populate.sql'

Nome de usuário e/ou senha

jdbc:h2:<url>[;USER=<username>][;PASSWORD=<value>]
jdbc:h2:file:~/sample;USER=sa;PASSWORD=123

Configurações de rastreamento de depuração

jdbc:h2:<url>;TRACE_LEVEL_FILE=<level 0..3>
jdbc:h2:file:~/sample;TRACE_LEVEL_FILE=3

Ignorar configurações desconhecidas

jdbc:h2:<url>;IGNORE_UNKNOWN_SETTINGS=TRUE

Modo de acesso a arquivo personalizado

jdbc:h2:<url>;ACCESS_MODE_DATA=rws

Banco de dados em um arquivo zip

jdbc:h2:zip:<zipFileName>!/<databaseName>
jdbc:h2:zip:~/db.zip!/test

Modo de compatibilidade

jdbc:h2:<url>;MODE=<databaseType>
jdbc:h2:~/test;MODE=MYSQL;DATABASE_TO_LOWER=TRUE

Reconexão automática

jdbc:h2:<url>;AUTO_RECONNECT=TRUE
jdbc:h2:tcp://localhost/~/test;AUTO_RECONNECT=TRUE

Modo misto automático

jdbc:h2:<url>;AUTO_SERVER=TRUE
jdbc:h2:~/test;AUTO_SERVER=TRUE

Tamanho da página

jdbc:h2:<url>;PAGE_SIZE=512

Alterar outras configurações

jdbc:h2:<url>;<setting>=<value>[;<setting>=<value>...]
jdbc:h2:file:~/sample;TRACE_LEVEL_SYSTEM_OUT=3

Configurando o H2 em projetos Spring Boot

Levando em consideração o seu conhecimento em Spring Boot. Nosso primeiro passo é criamos um novo projeto em Spring Boot e adicionar as seguintes dependências.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>
<!--Adicionado apenas para acessar o console do H2-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Após efetuar o download das dependências, vamos configurar o H2 adicionando as seguintes propriedades em nosso arquivo de configurações application.properties.

# DATASOURCE
spring.datasource.url=jdbc:h2:file:./data/exemplo
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password

# H2 Console
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console

# JPA
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update

Analisando as configurações do DataSource, observe o valor atribuído a propriedade URL, definimos que a conexão deve ser feita pelo modo integrado, e que o banco de dados deve ser armazenado no diretório /data/exemplo. Depois definimos o drive de conexão e as credenciais de acesso.

Na sequencia foi habilitado o console GUI (interface gráfica do utilizador) embutido para navegar pelo conteúdo do banco de dados e executar consultas SQL.

Por fim, configuramos o JPA, definindo o dialeto e também o ddl-auto como update, onde ao subir nossa aplicação o gerente de schemas do Hibernete detecta mudancas em nossas entidades e quando houver alterações as mesmas são aplicadas no banco de dados.

Mapeando uma entidade e testando nossa conexão com o banco de dados

Segue o código da entidade representando uma Pessoa e também de uma classe implementando JpaRepository que nos fornece inúmeros métodos para manipulação da entidade ao banco de dados.

import javax.persistence.*;

@Entity
@Table(name = "PERSON")
public class Person {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID", nullable = false)
    private Long id;

    @Column(name = "NAME", length = 50, nullable = false)
    private String name;

    @Deprecated
    public Person() {
    }

    public Person(String name) {
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}
import org.springframework.data.jpa.repository.JpaRepository;

public interface PersonRepository extends JpaRepository<Person, Long> {
}

Por fim segue um serviço que manipula objetos do tipo Pessoa em nosso banco de dados.

import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Service;

import java.util.logging.Level;
import java.util.logging.Logger;

@Service
public class PersonService implements ApplicationRunner {

    private static final Logger LOGGER = Logger.getLogger(PersonService.class.getName());

    private final PersonRepository repository;

    public PersonService(PersonRepository repository) {
        this.repository = repository;
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        var person = new Person("Gaspar Barancelli Junior");

        LOGGER.log(Level.INFO, "Persist");
        repository.save(person);
        LOGGER.log(Level.INFO, person.toString());

        LOGGER.log(Level.INFO, "Find");
        repository.findById(person.getId()).ifPresent(it -> {
            LOGGER.log(Level.INFO, person.toString());
        });

        var person2 = new Person("Rodrigo Barancelli");

        LOGGER.log(Level.INFO, "Persist");
        repository.save(person2);
        LOGGER.log(Level.INFO, person2.toString());

        person2.setName("Rodrigo Dalla Valle Barancelli");
        LOGGER.log(Level.INFO, "Update");
        repository.save(person2);
        LOGGER.log(Level.INFO, person2.toString());

        LOGGER.log(Level.INFO, "FindAll");
        repository.findAll().forEach(it -> LOGGER.log(Level.INFO, it.toString()));

        LOGGER.log(Level.INFO, "Delete");
        repository.delete(person2);
        LOGGER.log(Level.INFO, person2.toString());
    }
}

Ao executarmos nossa aplicação, obtemos a seguinte saida.

Persist
Person{id=1, name='Gaspar Barancelli Junior'}
Find
Person{id=1, name='Gaspar Barancelli Junior'}
Persist
Person{id=2, name='Rodrigo Barancelli'}
Update
Person{id=2, name='Rodrigo Dalla Valle Barancelli'}
FindAll
Person{id=1, name='Gaspar Barancelli Junior'}
Person{id=2, name='Rodrigo Dalla Valle Barancelli'}
Delete
Person{id=2, name='Rodrigo Dalla Valle Barancelli'}

Acessando o console do H2

Nas configurações realizadas anteriormente definimos que o path da nossa aplicação para acessar o console do banco de dados seria h2-console, portando abra o seguinte endereço em seu navegador http://localhost:8080/h2-console e copie os mesmos valores definimos no arquivo de configuração para os inputs de JDBC URL, User Name e Password.

screen h2 console login
Figure 1. Tela de login do query console do banco de dados h2

Após a conexão ser realizada seremos direcionados para uma nova página, onde podemos manipular nosso banco de dados. Nessa nova página vamos executar o seguinte comando SELECT * FROM PERSON, e verificar que os dados inseridos anteriormente estão sendo listados no console.

screen h2 query console
Figure 2. Query console do h2 executando um select

O código fonte dessa aplicação esta no repositório hospedado no GitHub.

// Livros recomendados relacionados ao assunto do post

// Compartilhe esse Post