H2 In-Memory-Datenbank. Tabelle nicht gefunden

181

Ich habe eine H2-Datenbank mit URL "jdbc:h2:test". Ich erstelle eine Tabelle mit CREATE TABLE PERSON (ID INT PRIMARY KEY, FIRSTNAME VARCHAR(64), LASTNAME VARCHAR(64));. Ich wähle dann alles aus dieser (leeren) Tabelle mit SELECT * FROM PERSON. So weit, ist es gut.

Wenn ich jedoch die URL in ändere "jdbc:h2:mem:test", besteht der einzige Unterschied darin, dass sich die Datenbank jetzt nur im Speicher befindet. Dies gibt mir eine org.h2.jdbc.JdbcSQLException: Table "PERSON" not found; SQL statement: SELECT * FROM PERSON [42102-154]. Ich vermisse hier wahrscheinlich etwas Einfaches, aber jede Hilfe wäre dankbar.

Jorn
quelle
2
Nach dem Umschalten in den In-Memory-Modus müssen Sie die Tabelle Personerneut erstellen . H2 weiß nichts über die Datenbank, die Sie zuvor auf der Festplatte erstellt haben.
Benjamin Muschko
Der Rest des Programms hat sich nicht geändert - ich habe die Tabelle erneut erstellt.
Jorn

Antworten:

329

DB_CLOSE_DELAY=-1

hbm2ddl schließt die Verbindung nach dem Erstellen der Tabelle, sodass h2 sie verwirft.

Wenn Sie Ihre Verbindungs-URL so konfiguriert haben

jdbc:h2:mem:test

Der Inhalt der Datenbank geht verloren, sobald die letzte Verbindung geschlossen wird.

Wenn Sie Ihren Inhalt behalten möchten, müssen Sie die URL wie folgt konfigurieren

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

In diesem Fall behält h2 seinen Inhalt so lange bei, wie der VM lebt.

Beachten Sie das Semikolon ( ;) anstelle von Doppelpunkt ( :).

Weitere Informationen finden Sie im Abschnitt In-Memory-Datenbanken auf der Seite Funktionen . Zitieren:

Standardmäßig wird durch Schließen der letzten Verbindung zu einer Datenbank die Datenbank geschlossen. Für eine In-Memory-Datenbank bedeutet dies, dass der Inhalt verloren geht. Fügen Sie ;DB_CLOSE_DELAY=-1der Datenbank-URL hinzu , um die Datenbank geöffnet zu halten . Verwenden Sie, um den Inhalt einer In-Memory-Datenbank so lange zu speichern, wie die virtuelle Maschine aktiv ist jdbc:h2:mem:test;DB_CLOSE_DELAY=-1.

reini2901
quelle
Ich habe das Problem in der Zwischenzeit selbst gefunden, aber ja, das ist völlig richtig. Vielen Dank!
Jorn
3
Und es muss eine benannte In-Memory-Datenbank sein, dh jdbc:h2:mem:;DB_CLOSE_DELAY=-1funktioniert nicht.
Peter Becker
Wie können wir Daten in der Datei anstelle des Speichers speichern?
Suleman Khan
8
Wenn Sie zum Benennen Ihrer Tabellen in einem Code Kleinbuchstaben verwenden, sollten Sie wissen, dass H2 standardmäßig alles in Großbuchstaben verwendet. Verwenden Sie DATABASE_TO_UPPER = false, um dies zu vermeiden, z. B. jdbc: h2: mem: test; DB_CLOSE_DELAY = -1; DATABASE_TO_UPPER = false;
Oleksandr Petrenko
@OleksandrPetrenko - das nachfolgende ';' scheint Probleme zu verursachen. Ich denke, Sie müssen das weglassen.
Volksman
102

Ich weiß, dass dies nicht Ihr Fall war, aber ich hatte das gleiche Problem, weil H2 die Tabellen mit UPPERCASE-Namen erstellte und sich dann zwischen Groß- und Kleinschreibung unterschied, obwohl ich in allen Skripten (einschließlich der Erstellungsskripte) Kleinbuchstaben verwendet habe.

Gelöst durch Hinzufügen ;DATABASE_TO_UPPER=falsezur Verbindungs-URL.

Cristian Vrabie
quelle
7
Wow - ich bin sehr froh, dass du diesen geteilt hast! Daran hätte ich nie gedacht.
ms-tg
1
Nicht die Lösung für die gestellte Frage, sondern die Lösung für das Problem, das ich bei der Suche mit derselben Frage hatte!
Yaytay
Ist es möglich, dieses DATABASE_TO_UPPER=falseDing als SQL-Anweisung in einem Init-Skript festzulegen? (Ähnlich wie bei einer Anweisung wie SET MODE PostgreSQL;) Wenn ja, wie lautet die genaue Syntax?
Jonik
3
Wie kann ich dies mehrmals verbessern? Vielen Dank! Dies sollte Teil der ersten Antwort sein.
Ribesg
11

Schwer zu erzählen. Ich habe ein Programm erstellt, um dies zu testen:

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

Der Test wurde ohne Fehler und ohne unerwartete Ausgabe abgeschlossen. Welche Version von h2 verwenden Sie?

Joseph Ottinger
quelle
Ich werde es morgen versuchen, danke. Die H2-Version ist die, die ich heute von der Seite bekommen habe: 1.3.154
Jorn
1
Ich glaube, ich habe das Problem gefunden. Wenn ich die Verbindung schließe, mit der die Tabelle erstellt wurde, und dann eine neue öffne, ist die Datenbank verschwunden. Wenn ich eine neue Verbindung öffne, bevor ich die vorherige schließe, bleiben die Daten erhalten. Wenn ich eine Datei verwende, bleiben die Daten (offensichtlich) immer erhalten.
Jorn
7

Die speicherinterne H2-Datenbank speichert Daten im Speicher innerhalb der JVM. Beim Beenden der JVM gehen diese Daten verloren.

Ich vermute, dass das, was Sie tun, den beiden folgenden Java-Klassen ähnlich ist. Eine dieser Klassen erstellt eine Tabelle und die andere versucht, sie einzufügen:

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

und

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

Wenn ich diese Klassen nacheinander durchführte, erhielt ich die folgende Ausgabe:

C: \ Benutzer \ Luke \ stuff> java CreateTable

C: \ Users \ Luke \ stuff> Java InsertIntoTable
Ausnahme im Thread "main" org.h2.jdbc.JdbcSQLException: Tabelle "PERSON" nicht gefunden; SQL-Anweisung:
INSERT IN PERSON (ID, FIRSTNAME, LASTNAME) -WERTE (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)
        ...

Sobald der erste javaProzess beendet wird, existiert die von erstellte Tabelle CreateTablenicht mehr. Wenn also die InsertIntoTable-Klasse hinzukommt, gibt es keine Tabelle, in die sie eingefügt werden kann.

Als ich die Verbindungszeichenfolgen in änderte, jdbc:h2:teststellte ich fest, dass es keinen solchen Fehler gab. Ich fand auch, dass eine Datei test.h2.dberschienen war. Hier hatte H2 die Tabelle abgelegt, und da sie auf der Festplatte gespeichert war, war die Tabelle immer noch für die InsertIntoTable-Klasse verfügbar.

Luke Woodward
quelle
1
Bitte beachten Sie, dass der registerDriver()Aufruf nicht erforderlich ist: Erstens: Eine einfache Class.forName () macht dasselbe für die meisten JDBC-Treiber und (was noch wichtiger ist) ist für Java 6 und höher völlig unnötig, wodurch (kompatible) JDBC-Treiber auf dem automatisch erkannt werden Klassenpfad.
Joachim Sauer
Eine In-Memory-Datenbank existiert nur, solange das Programm ausgeführt wird, dem der Speicher gehört? Wow, ich hatte keine Ahnung> _ <Aber ich weiß wirklich, was ich versuche zu tun. Wenn ich Ihre Antwort lese, bin ich mir nicht sicher, ob Sie das tun.
Jorn
2
@Jorn: Ich weiß möglicherweise nicht, was Sie versuchen, ich schätze, basierend auf den Informationen, die Sie bereitgestellt haben. Es wäre vielleicht hilfreicher gewesen, eine SSCCE ( sscce.org ) bereitzustellen , die Ihr Problem demonstriert. Ich würde Ihre Frage in dieser Hinsicht nicht als "vollständig" bezeichnen. Ich habe die obige Antwort gegeben, weil es Leute auf SO gibt (hauptsächlich Neulinge in der Programmierung), die denken könnten, dass eine 'In-Memory'-Datenbank die Daten im Speicher des Computers irgendwo speichert, wo sie zwischen Programmaufrufen überleben könnten. Ihre Frage war nicht vollständig genug, um mich davon zu überzeugen, dass Sie nicht zu diesen Personen gehören.
Luke Woodward
5

Ich habe versucht hinzuzufügen

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

Das hat jedoch nicht geholfen. Auf der H2-Site habe ich Folgendes gefunden, was in einigen Fällen tatsächlich helfen könnte.

Standardmäßig wird durch Schließen der letzten Verbindung zu einer Datenbank die Datenbank geschlossen. Für eine In-Memory-Datenbank bedeutet dies, dass der Inhalt verloren geht. Um die Datenbank geöffnet zu halten, fügen Sie der Datenbank-URL Folgendes hinzu: DB_CLOSE_DELAY = -1. Verwenden Sie jdbc: h2: mem: test; DB_CLOSE_DELAY = -1, um den Inhalt einer In-Memory-Datenbank so lange zu erhalten, wie die virtuelle Maschine aktiv ist.

Allerdings war mein Problem , dass nur das Schema als Standard ein , anders sein sollte. Also sofort benutzt

JDBC URL: jdbc:h2:mem:test

Ich musste verwenden:

JDBC URL: jdbc:h2:mem:testdb

Dann waren die Tabellen sichtbar

DevDio
quelle
danke, "testdb" war auch für mich ein Fix (zusätzlich zu "DB_CLOSE_DELAY = -1")!
Boly38
4

Ich hatte das gleiche Problem und änderte meine Konfiguration in application-test.properties wie folgt:

#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

Und meine Abhängigkeiten:

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

Und die Anmerkungen zur Testklasse:

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

Ich habe versucht, Tabellen-Metadaten abzurufen, hatte jedoch den folgenden Fehler:

Verwenden von:

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

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

hat ein leeres ResultSet zurückgegeben.

Aber stattdessen hat es mit der folgenden URL richtig funktioniert:

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

Es musste angegeben werden: DATABASE_TO_UPPER = false

Miguel Angel Vega Pabon
quelle
Dies fügt nichts hinzu, was von dieser Antwort nicht abgedeckt wird . Aus der Überprüfung .
Wai Ha Lee
3

Beim Öffnen der h2-Konsole muss die JDBC-URL mit der in den Eigenschaften angegebenen übereinstimmen:

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

Geben Sie hier die Bildbeschreibung ein

Was offensichtlich erscheint, aber ich habe Stunden damit verbracht, dies herauszufinden.

nagy.zsolt.hun
quelle
2

Gelöst durch Erstellen eines neuen Ordners src / test / resources + Einfügen der Datei application.properties, wobei explizit angegeben wird, dass eine Testdatenbank erstellt werden soll:

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

Ich bin zu diesem Beitrag gekommen, weil ich den gleichen Fehler hatte.

In meinem Fall wurden die Datenbankentwicklungen nicht ausgeführt, sodass die Tabelle überhaupt nicht vorhanden war.

Mein Problem war, dass die Ordnerstruktur für die Evolutionsskripte falsch war.

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

Play verfolgt Ihre Datenbankentwicklungen mithilfe mehrerer Evolutionsskripte. Diese Skripte sind in einfachem altem SQL geschrieben und sollten sich im Verzeichnis conf / evolutions / {Datenbankname} Ihrer Anwendung befinden. Wenn die Entwicklungen für Ihre Standarddatenbank gelten, lautet dieser Pfad conf / evolutions / default.

Ich hatte einen Ordner namens conf / evolutions.default von Eclipse erstellt. Das Problem verschwand, nachdem ich die Ordnerstruktur auf conf / evolutions / default korrigiert hatte

Oscar Fraxedas
quelle
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.
quelle
0

Hatte genau das gleiche Problem, versuchte alle oben genannten, aber ohne Erfolg. Die ziemlich lustige Ursache des Fehlers war, dass die JVM zu schnell gestartet wurde, bevor die DB-Tabelle erstellt wurde (unter Verwendung einer data.sql-Datei in src.main.resources). Also habe ich einen Thread.sleep (1000) -Timer eingestellt, der nur eine Sekunde wartet, bevor ich "select * from person" aufrufe. Jetzt einwandfrei arbeiten.

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

Hauptklasse:

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

Ich fand es funktionierend, nachdem ich die Abhängigkeit von Spring Data JPA von Spring Boot Version 2.2.6 hinzugefügt hatte. RELEASE -

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

Hinzufügen der H2 DB-Konfiguration 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
Bagesh Sharma
quelle
0

Ich fand die Lösung durch Hinzufügen dieser Konfiguration:

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

Die vollständige Konfiguration (mit einfacher 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

Ich arbeite mit h2 Version 1.4.200 und Spring-Boot 2.2.6.RELEASE

Falke
quelle
-2

Time.sleep (1000);

Das ist Arbeit für mich. Nur zum Ausprobieren: Wenn Ihr PC langsam ist, können Sie die Zeitdauer des Threads erhöhen, damit die Datenbank einwandfrei funktioniert und JVM unsere Tabelle abrufen kann.

Dhara N.
quelle