Banco de dados H2 na memória. Tabela não encontrada

183

Eu tenho um banco de dados H2 com URL "jdbc:h2:test". Eu crio uma tabela usando CREATE TABLE PERSON (ID INT PRIMARY KEY, FIRSTNAME VARCHAR(64), LASTNAME VARCHAR(64));. Em seguida, seleciono tudo dessa tabela (vazia) usando SELECT * FROM PERSON. Por enquanto, tudo bem.

No entanto, se eu alterar o URL para "jdbc:h2:mem:test", a única diferença é que o banco de dados agora está apenas na memória, isso me dá uma org.h2.jdbc.JdbcSQLException: Table "PERSON" not found; SQL statement: SELECT * FROM PERSON [42102-154]. Provavelmente estou perdendo algo simples aqui, mas qualquer ajuda seria apreciada.

Jorn
fonte
2
Depois de mudar para o modo de memória, você precisa criar a tabela Personnovamente. H2 não sabe nada sobre o banco de dados que você criou no disco antes.
Benjamin Muschko
O restante do programa não mudou - eu criei a tabela novamente.
Jorn

Respostas:

331

DB_CLOSE_DELAY=-1

O hbm2ddl fecha a conexão depois de criar a tabela; portanto, a h2 a descarta.

Se você tiver seu URL de conexão configurado assim

jdbc:h2:mem:test

o conteúdo do banco de dados é perdido no momento em que a última conexão é fechada.

Se você deseja manter seu conteúdo, precisa configurar o URL como este

jdbc:h2:mem:test;DB_CLOSE_DELAY=-1

Se fizer isso, a h2 manterá seu conteúdo enquanto a vm permanecer.

Observe o ponto-e-vírgula ( ;) em vez de dois-pontos ( :).

Consulte a seção Bancos de dados na memória da página Recursos . Citar:

Por padrão, fechar a última conexão com um banco de dados fecha o banco de dados. Para um banco de dados na memória, isso significa que o conteúdo é perdido. Para manter o banco de dados aberto, adicione ;DB_CLOSE_DELAY=-1ao URL do banco de dados. Para manter o conteúdo de um banco de dados na memória enquanto a máquina virtual estiver ativa, use jdbc:h2:mem:test;DB_CLOSE_DELAY=-1.

reini2901
fonte
Eu mesmo encontrei o problema nesse meio tempo, mas sim, isso está completamente correto. Obrigado!
Jorn
3
E deve ser um banco de dados nomeado na memória, ou seja jdbc:h2:mem:;DB_CLOSE_DELAY=-1, não funciona.
Peter Becker
como podemos armazenar dados no arquivo em vez de memória?
precisa saber é o seguinte
9
se você usa letras minúsculas para nomear suas tabelas em um código, deve saber que tudo em maiúsculas H2, por padrão, usa DATABASE_TO_UPPER = false para evitá-lo, por exemplo, jdbc: h2: mem: test; DB_CLOSE_DELAY = -1; DATABASE_TO_UPPER = false;
Oleksandr Petrenko
@OleksandrPetrenko - that trailing ';' parece causar problemas. Eu acho que você precisa deixar isso de fora.
Volksman
104

Eu sei que esse não foi o seu caso, mas tive o mesmo problema, porque o H2 estava criando as tabelas com nomes MAIÚSCULAS e, então, fazia distinção entre maiúsculas e minúsculas, mesmo que em todos os scripts (inclusive nos de criação) eu usasse letras minúsculas.

Resolvido adicionando ;DATABASE_TO_UPPER=falseao URL de conexão.

Cristian Vrabie
fonte
7
Uau - Estou muito feliz que você compartilhou este! Jamais teria pensado nisso.
ms-TG
1
Não é a solução para a pergunta que foi feita, mas a solução para o problema que eu estava tendo ao pesquisar com a mesma pergunta!
Yaytay
É possível definir isso DATABASE_TO_UPPER=falsecomo uma instrução SQL em um script init? (Da mesma forma que uma declaração como SET MODE PostgreSQL;) Em caso afirmativo, qual é a sintaxe exata?
Jonik
3
Como posso votar isso várias vezes? Muito obrigado! Isso deve fazer parte da primeira resposta.
precisa saber é o seguinte
11

Difícil de dizer. Eu criei um programa para testar isso:

package com.gigaspaces.compass;

import org.testng.annotations.Test;

import java.sql.*;

public class H2Test {
@Test
public void testDatabaseNoMem() throws SQLException {
    testDatabase("jdbc:h2:test");
}
@Test
public void testDatabaseMem() throws SQLException {
    testDatabase("jdbc:h2:mem:test");
}

private void testDatabase(String url) throws SQLException {
    Connection connection= DriverManager.getConnection(url);
    Statement s=connection.createStatement();
    try {
    s.execute("DROP TABLE PERSON");
    } catch(SQLException sqle) {
        System.out.println("Table not found, not dropping");
    }
    s.execute("CREATE TABLE PERSON (ID INT PRIMARY KEY, FIRSTNAME VARCHAR(64), LASTNAME VARCHAR(64))");
    PreparedStatement ps=connection.prepareStatement("select * from PERSON");
    ResultSet r=ps.executeQuery();
    if(r.next()) {
        System.out.println("data?");
    }
    r.close();
    ps.close();
    s.close();
    connection.close();
}
}

O teste foi concluído, sem falhas e sem saída inesperada. Qual versão do h2 você está executando?

Joseph Ottinger
fonte
Vou tentar isso amanhã, obrigado. A versão H2 é a que saí do site hoje: 1.3.154
Jorn
1
Eu acho que encontrei o problema. Quando fecho a conexão com a qual a tabela foi criada, abra uma nova e o banco de dados se foi. Quando abro uma nova conexão antes de fechar a anterior, os dados permanecem. Quando eu uso um arquivo, os dados (obviamente) sempre permanecem.
Jorn
7

O banco de dados na memória H2 armazena dados na memória dentro da JVM. Quando a JVM sai, esses dados são perdidos.

Eu suspeito que o que você está fazendo é semelhante às duas classes Java abaixo. Uma dessas classes cria uma tabela e a outra tenta inserir nela:

import java.sql.*;

public class CreateTable {
    public static void main(String[] args) throws Exception {
        DriverManager.registerDriver(new org.h2.Driver());
        Connection c = DriverManager.getConnection("jdbc:h2:mem:test");
        PreparedStatement stmt = c.prepareStatement("CREATE TABLE PERSON (ID INT PRIMARY KEY, FIRSTNAME VARCHAR(64), LASTNAME VARCHAR(64))");
        stmt.execute();
        stmt.close();
        c.close();
    }
}

e

import java.sql.*;

public class InsertIntoTable {
    public static void main(String[] args) throws Exception {
        DriverManager.registerDriver(new org.h2.Driver());
        Connection c = DriverManager.getConnection("jdbc:h2:mem:test");
        PreparedStatement stmt = c.prepareStatement("INSERT INTO PERSON (ID, FIRSTNAME, LASTNAME) VALUES (1, 'John', 'Doe')");
        stmt.execute();
        stmt.close();
        c.close();
    }
}

Quando executei essas classes uma após a outra, obtive a seguinte saída:

C: \ Usuários \ Lucas \ coisas> Java CreateTable

C: \ Users \ Luke \ stuff> java InsertIntoTable
Exceção no encadeamento "main" org.h2.jdbc.JdbcSQLException: Tabela "PERSON" não encontrada; Instrução SQL:
INSERIR NA PESSOA (ID, PRIMEIRO NOME, SOBRENOME) VALORES (1, 'João', 'Corça') [42102-154]
        em org.h2.message.DbException.getJdbcSQLException (DbException.java:327)
        em org.h2.message.DbException.get (DbException.java:167)
        em org.h2.message.DbException.get (DbException.java:144)
        ...

Assim que o primeiro javaprocesso termina, a tabela criada por CreateTablenão existe mais. Portanto, quando a classe InsertIntoTable aparecer, não haverá tabela para inserir.

Quando alterei as cadeias de conexão para jdbc:h2:test, descobri que não havia esse erro. Também descobri que um arquivo test.h2.dbhavia aparecido. Foi aqui que H2 colocou a tabela e, como ela foi armazenada em disco, a tabela ainda estava lá para a classe InsertIntoTable encontrar.

Luke Woodward
fonte
1
Observe que a registerDriver()chamada é desnecessária: Primeiro: um simples Class.forName () faz o mesmo para a maioria dos drivers JDBC e (mais importante) é completamente desnecessário para o Java 6 und up, que detecta automaticamente drivers JDBC (compatíveis) no caminho de classe.
Joachim Sauer
Um db na memória existe apenas enquanto o programa que possui a memória estiver em execução? Uau, eu não tinha ideia> _ <Mas, realmente, eu sei o que estou tentando fazer. Lendo sua resposta, não tenho certeza.
Jorn
2
@ Jorn: Talvez eu não saiba o que você está tentando fazer, acho que com base nas informações que você forneceu. Pode ter sido mais útil fornecer um SSCCE ( sscce.org ) demonstrando seu problema - eu não consideraria sua pergunta 'completa' a esse respeito. Forneci a resposta acima porque existem pessoas no SO (principalmente iniciantes na programação) que podem pensar que um banco de dados 'in-memory' armazena os dados na memória do computador em algum lugar onde ele possa sobreviver entre as invocações de programas. Sua pergunta não foi completa o suficiente para me convencer de que você não era uma dessas pessoas.
Luke Woodward
5

Eu tentei adicionar

jdbc:h2:mem:test;DB_CLOSE_DELAY=-1

No entanto, isso não ajudou. No site H2 , descobri o seguinte, o que de fato poderia ajudar em alguns casos.

Por padrão, fechar a última conexão com um banco de dados fecha o banco de dados. Para um banco de dados na memória, isso significa que o conteúdo é perdido. Para manter o banco de dados aberto, adicione; DB_CLOSE_DELAY = -1 ao URL do banco de dados. Para manter o conteúdo de um banco de dados na memória enquanto a máquina virtual estiver ativa, use jdbc: h2: mem: test; DB_CLOSE_DELAY = -1.

No entanto , meu problema era que apenas o esquema deveria ser diferente do padrão. Tão instado de usar

JDBC URL: jdbc:h2:mem:test

Eu tive que usar:

JDBC URL: jdbc:h2:mem:testdb

Então as mesas estavam visíveis

DevDio
fonte
obrigado, "testdb" foi uma correção para mim também (além de "DB_CLOSE_DELAY = -1")!
precisa
4

Eu tive o mesmo problema e alterei minha configuração em application-test.properties para isso:

#Test Properties
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=create-drop

E minhas dependências:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.h2database/h2 -->
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <version>1.4.198</version>
        <scope>test</scope>
    </dependency>

E as anotações usadas na classe de teste:

@RunWith(SpringRunner.class)
@DataJpaTest
@ActiveProfiles("test")
public class CommentServicesIntegrationTests {
...
}
Georgi Peev
fonte
3

Eu estava tentando buscar metadados da tabela, mas tive o seguinte erro:

Usando:

String JDBC_URL = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1";

DatabaseMetaData metaData = connection.getMetaData();
...
metaData.getColumns(...);

retornou um ResultSet vazio.

Mas, usando o seguinte URL, ele funcionou corretamente:

String JDBC_URL = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false";

Havia uma necessidade de especificar: DATABASE_TO_UPPER = false

Miguel Angel Vega Pabon
fonte
Isso não adiciona nada não coberto por esta resposta. Da revisão .
Wai Ha Lee
3

Ao abrir o console h2, a URL JDBC deve corresponder à especificada nas propriedades:

spring.datasource.driverClassName=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:testdb

spring.jpa.hibernate.ddl-auto=create
spring.jpa.show-sql=true

spring.h2.console.enabled=true

insira a descrição da imagem aqui

O que parece óbvio, mas passei horas descobrindo isso.

nagy.zsolt.hun
fonte
2

Resolvido criando uma nova pasta src / test / resources + insira o arquivo application.properties, especificando explicitamente para criar um dbase de teste:

spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=create
N.MATHIEU
fonte
1

Eu vim para este post porque tinha o mesmo erro.

No meu caso, as evoluções do banco de dados não foram executadas; portanto, a tabela não estava lá.

Meu problema era que a estrutura de pastas dos scripts de evolução estava errada.

from: https://www.playframework.com/documentation/2.0/Evolutions

O Play acompanha as evoluções do banco de dados usando vários scripts de evolução. Esses scripts são escritos em SQL antigo simples e devem estar localizados no diretório conf / evolutions / {database name} do seu aplicativo. Se as evoluções se aplicarem ao seu banco de dados padrão, esse caminho será conf / evolutions / default.

Eu tinha uma pasta chamada conf / evolutions.default criada pelo eclipse. O problema desapareceu depois que eu corrigi a estrutura da pasta para conf / evolutions / default

Oscar Fraxedas
fonte
0
<bean id="benchmarkDataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="org.h2.Driver" />
    <property name="url" value="jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1" />
    <property name="username" value="sa" />
    <property name="password" value="" />
</bean>
Alex R
fonte
0

Teve exatamente o mesmo problema, tentei todas as opções acima, mas sem sucesso. A causa mais engraçada do erro foi que a JVM foi iniciada muito rapidamente, antes da criação da tabela DB (usando um arquivo data.sql em src.main.resources). Então, eu coloquei um timer Thread.sleep (1000) para aguardar apenas um segundo antes de chamar "select * from person". Trabalhando perfeitamente agora.

application.properties:

spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

data.sql:

create table person
(
id integer not null,
name varchar(255) not null,
location varchar(255),
birth_date timestamp,
primary key(id)
);

insert into person values (
10001, 'Tofu', 'home', sysdate()
);

PersonJdbcDAO.java:

    public List<Person> findAllPersons(){
    return jdbcTemplate.query("select * from person", 
        new BeanPropertyRowMapper<Person>(Person.class));
}

classe principal:

Thread.sleep(1000);
logger.info("All users -> {}", dao.findAllPersons());
Tudor Gafiuc
fonte
0

Eu o encontrei funcionando depois de adicionar a dependência do Spring Data JPA na versão de inicialização 2.2.2 do Spring.

    <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>

Adicione a configuração do H2 DB no application.yml -

spring:
  datasource:
    driverClassName: org.h2.Driver
    initialization-mode: always
    username: sa
    password: ''
    url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
  h2:
    console:
      enabled: true
      path: /h2
  jpa:
    database-platform: org.hibernate.dialect.H2Dialect
    hibernate:
      ddl-auto: none
Bagesh Sharma
fonte
0

Encontrei a solução adicionando esta configuração:

spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

A configuração completa (com simple spring.datasource.url):

spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=sa
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=create-drop

Estou trabalhando com a versão h2 1.4.200 e o Spring-Boot 2.2.6.

Falcão
fonte
-2

Time.sleep (1000);

Isso é trabalho para mim. Apenas para tentar, se o seu PC estiver lento, você poderá aumentar o tempo de duração do encadeamento para que o DB funcione bem e a JVM possa obter nossa tabela.

Dhara N.
fonte