Database in memoria H2. Tabella non trovata


183

Ho un database H2 con URL "jdbc:h2:test". Creo una tabella usando CREATE TABLE PERSON (ID INT PRIMARY KEY, FIRSTNAME VARCHAR(64), LASTNAME VARCHAR(64));. Quindi seleziono tutto da questa tabella (vuota) usando SELECT * FROM PERSON. Fin qui tutto bene.

Tuttavia, se cambio l'URL in "jdbc:h2:mem:test", l'unica differenza è che il database è ora solo in memoria, questo mi dà un org.h2.jdbc.JdbcSQLException: Table "PERSON" not found; SQL statement: SELECT * FROM PERSON [42102-154]. Probabilmente mi manca qualcosa di semplice qui, ma qualsiasi aiuto sarebbe apprezzato.


2
Dopo essere passati alla modalità in memoria devi creare Personnuovamente la tabella . H2 non sa nulla del database creato in precedenza sul disco.
Benjamin Muschko,

Il resto del programma non è cambiato: ho creato di nuovo la tabella.
Jorn,

Risposte:


331

DB_CLOSE_DELAY=-1

hbm2ddl chiude la connessione dopo aver creato la tabella, quindi h2 la scarta.

Se l'URL di connessione è configurato in questo modo

jdbc:h2:mem:test

il contenuto del database viene perso al momento della chiusura dell'ultima connessione.

Se vuoi conservare i tuoi contenuti devi configurare l'URL in questo modo

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

In tal caso, h2 manterrà il suo contenuto per tutto il tempo in cui vm vive.

Notare il punto e virgola ( ;) anziché i due punti ( :).

Vedere la sezione Database in memoria della pagina Funzioni . Per citare:

Per impostazione predefinita, la chiusura dell'ultima connessione a un database chiude il database. Per un database in memoria, ciò significa che il contenuto è perso. Per mantenere aperto il database, aggiungere ;DB_CLOSE_DELAY=-1all'URL del database. Per mantenere il contenuto di un database in memoria finché la macchina virtuale è attiva, utilizzare jdbc:h2:mem:test;DB_CLOSE_DELAY=-1.


Ho trovato il problema da solo nel frattempo, ma sì, questo è completamente corretto. Grazie!
Jorn,

3
E deve essere un database in memoria denominato, ovvero jdbc:h2:mem:;DB_CLOSE_DELAY=-1non funziona.
Peter Becker,

come possiamo archiviare i dati nel file anziché nella memoria?
Suleman khan,

9
se usi le lettere minuscole per assegnare un nome alle tue tabelle in un codice, dovresti sapere che H2 in maiuscolo per impostazione predefinita usa DATABASE_TO_UPPER = false per evitarlo, ad esempio jdbc: h2: mem: test; DB_CLOSE_DELAY = -1; DATABASE_TO_UPPER = false;
Oleksandr Petrenko,

@OleksandrPetrenko - that trailing ';' sembra causare problemi. Penso che tu debba lasciarlo fuori.
Volksman,

104

So che questo non è stato il tuo caso, ma ho avuto lo stesso problema perché H2 stava creando le tabelle con i MAIUSCOLI quindi si comportava con distinzione tra maiuscole e minuscole, anche se in tutti gli script (compresi quelli di creazione) ho usato lettere minuscole.

Risolto aggiungendo ;DATABASE_TO_UPPER=falseall'URL della connessione.


7
Wow - Sono molto contento che tu l'abbia condiviso! non ci avrei mai pensato.
ms-tg,

1
Non la soluzione alla domanda che è stata posta, ma la soluzione al problema che ho avuto durante la ricerca con la stessa domanda!
Yaytay,

È possibile impostare questa DATABASE_TO_UPPER=falsecosa come un'istruzione SQL in uno script di init? (Analogamente come un'affermazione simile SET MODE PostgreSQL;) In tal caso, qual è la sintassi esatta?
Jonik,

3
Come posso votare più volte? Molte grazie! Questo dovrebbe essere parte della prima risposta.
Ribesg,

11

Difficile da dire. Ho creato un programma per testare questo:

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();
}
}

Il test ha funzionato fino al completamento, senza errori e senza output imprevisto. Quale versione di h2 stai utilizzando?


Ci proverò domani, grazie. La versione H2 è quella che ho rimosso oggi dal sito: 1.3.154
Jorn,

1
Penso di aver trovato il problema. Quando chiudo la connessione con cui è stata creata la tabella, quindi ne apro una nuova, il db scompare. Quando apro una nuova connessione prima di chiudere la precedente, i dati rimangono. Quando uso un file, i dati (ovviamente) rimangono sempre.
Jorn,

7

Il database in memoria H2 memorizza i dati in memoria all'interno della JVM. All'uscita della JVM, questi dati vengono persi.

Sospetto che ciò che stai facendo sia simile alle due classi Java seguenti. Una di queste classi crea una tabella e l'altra tenta di inserirvi:

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 ho eseguito queste classi una dopo l'altra, ho ottenuto il seguente output:

C: \ Users \ Luke \ stuff> java CreateTable

C: \ Users \ Luke \ stuff> java InsertIntoTable
Eccezione nel thread "main" org.h2.jdbc.JdbcSQLException: tabella "PERSON" non trovata; Istruzione SQL:
INSERIRE IN PERSONA (ID, FIRSTNAME, LASTNAME) VALORI (1, 'John', 'Doe') [42102-154]
        at org.h2.message.DbException.getJdbcSQLException (DbException.java:327)
        at org.h2.message.DbException.get (DbException.java:167)
        at org.h2.message.DbException.get (DbException.java:144)
        ...

Non appena javatermina il primo processo, la tabella creata da CreateTablenon esiste più. Quindi, quando arriva la classe InsertIntoTable, non c'è alcuna tabella in cui inserirla.

Quando ho cambiato le stringhe di connessione in jdbc:h2:test, ho scoperto che non si verificava un errore del genere. Ho anche scoperto che test.h2.dbera apparso un file . Questo era il punto in cui H2 aveva inserito la tabella e poiché era stata archiviata su disco, la tabella era ancora lì per essere trovata dalla classe InsertIntoTable.


1
Si noti che la registerDriver()chiamata non è necessaria: Primo: un semplice Class.forName () fa lo stesso per la maggior parte dei driver JDBC e (cosa ancora più importante) è completamente inutile per Java 6 und up, che rileva automaticamente i driver JDBC (compatibili) sul classpath.
Joachim Sauer,

Un db in memoria esiste solo finché il programma che possiede la memoria è in esecuzione? Wow, non avevo idea> _ <Ma davvero, so cosa sto cercando di fare. Leggendo la tua risposta, non sono sicuro che tu lo faccia.
Jorn,

2
@Jorn: potrei non sapere cosa stai cercando di fare, immagino in base alle informazioni che hai fornito. Potrebbe essere stato più utile fornire un SSCCE ( sscce.org ) che dimostri il tuo problema - non definirei la tua domanda "completa" al riguardo. Ho fornito la risposta di cui sopra perché ci sono persone su SO (i nuovi arrivati ​​alla programmazione, principalmente) che potrebbero pensare che un database "in-memory" memorizzi i dati nella memoria del computer da qualche parte dove potrebbe sopravvivere tra le invocazioni del programma. La tua domanda non era abbastanza completa per convincermi che non eri una di queste persone.
Luke Woodward,

5

Ho provato ad aggiungere

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

Tuttavia, ciò non ha aiutato. Sul sito H2 , ho trovato il seguente, che in alcuni casi potrebbe davvero aiutare.

Per impostazione predefinita, la chiusura dell'ultima connessione a un database chiude il database. Per un database in memoria, ciò significa che il contenuto è perso. Per mantenere aperto il database, aggiungere; DB_CLOSE_DELAY = -1 all'URL del database. Per mantenere il contenuto di un database in memoria finché la macchina virtuale è attiva, utilizzare jdbc: h2: mem: test; DB_CLOSE_DELAY = -1.

Tuttavia , il mio problema era che solo lo schema doveva essere diverso da quello predefinito. Così istinto di usare

JDBC URL: jdbc:h2:mem:test

Ho dovuto usare:

JDBC URL: jdbc:h2:mem:testdb

Quindi i tavoli erano visibili


grazie, "testdb" è stato una soluzione anche per me (oltre a "DB_CLOSE_DELAY = -1")!
boly38

4

Ho avuto lo stesso problema e ho cambiato la mia configurazione in application-test.properties a questo:

#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 le mie dipendenze:

    <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 le annotazioni utilizzate nella classe di test:

@RunWith(SpringRunner.class)
@DataJpaTest
@ActiveProfiles("test")
public class CommentServicesIntegrationTests {
...
}

3

Stavo cercando di recuperare i metadati della tabella, ma ho avuto il seguente errore:

usando:

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

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

ha restituito un ResultSet vuoto.

Ma usando il seguente URL invece ha funzionato correttamente:

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

Era necessario specificare: DATABASE_TO_UPPER = false


Questo non aggiunge nulla non coperto da questa risposta. Dalla recensione .
Wai Ha Lee,

3

All'apertura della console h2, l'URL JDBC deve corrispondere a quello specificato nelle proprietà:

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

inserisci qui la descrizione dell'immagine

Il che sembra ovvio, ma ho passato ore a capirlo ..


2

Risolto creando una nuova cartella src / test / resources + inserire il file application.properties, specificando esplicitamente la creazione di un database di test:

spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=create

1

Sono venuto a questo post perché ho avuto lo stesso errore.

Nel mio caso le evoluzioni del database non sono state eseguite, quindi la tabella non era presente.

Il mio problema era che la struttura delle cartelle per gli script di evoluzione era errata.

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

Riproduci le tracce delle evoluzioni del tuo database usando diversi script di evoluzione. Questi script sono scritti in un semplice vecchio SQL e dovrebbero trovarsi nella directory conf / evolutions / {nome database} dell'applicazione. Se le evoluzioni si applicano al database predefinito, questo percorso è conf / evolutions / default.

Avevo una cartella chiamata conf / evolutions.default creata da eclipse. Il problema è scomparso dopo aver corretto la struttura delle cartelle in conf / evolutions / default


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>

0

Aveva lo stesso identico problema, provato tutto quanto sopra, ma senza successo. La causa piuttosto divertente dell'errore è che la JVM è stata avviata troppo velocemente, prima che fosse creata la tabella DB (usando un file data.sql in src.main.resources). Quindi ho messo un timer Thread.sleep (1000) per attendere solo un secondo prima di chiamare "select * from person". Ora funziona perfettamente.

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

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

0

L'ho trovato funzionante dopo aver aggiunto la dipendenza di Spring Data JPA da Spring boot versione 2.2.6.

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

Aggiungi la configurazione H2 DB in 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

0

Ho trovato la soluzione aggiungendo questa configurazione:

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

La configurazione completa (con semplice 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

Sto lavorando con la versione 1.4.200 di h2 e Spring-Boot 2.2.6.RELEASE


-2

Time.sleep (1000);

Questo è lavoro per me. Solo per provare, se il tuo PC è lento puoi aumentare la durata del thread in modo che DB funzioni bene e JVM possa ottenere la nostra tabella.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.