Baza danych H2 w pamięci. Nie znaleziono tabeli


183

Mam bazę danych H2 z adresem URL "jdbc:h2:test". Tworzę tabelę za pomocą CREATE TABLE PERSON (ID INT PRIMARY KEY, FIRSTNAME VARCHAR(64), LASTNAME VARCHAR(64));. Następnie wybieram wszystko z tej (pustej) tabeli za pomocą SELECT * FROM PERSON. Na razie w porządku.

Jeśli jednak zmienię adres URL "jdbc:h2:mem:test", jedyną różnicą jest to, że baza danych jest teraz tylko w pamięci, to daje mi org.h2.jdbc.JdbcSQLException: Table "PERSON" not found; SQL statement: SELECT * FROM PERSON [42102-154]. Prawdopodobnie brakuje mi tutaj czegoś prostego, ale każda pomoc byłaby mile widziana.


2
Po przejściu do trybu w pamięci musisz Personponownie utworzyć tabelę . H2 nie wie nic wcześniej o bazie danych utworzonej na dysku.
Benjamin Muschko

Reszta programu nie uległa zmianie - ponownie utworzyłem tabelę.
Jorn

Odpowiedzi:


331

DB_CLOSE_DELAY=-1

hbm2ddl zamyka połączenie po utworzeniu tabeli, więc h2 je odrzuca.

Jeśli masz skonfigurowany adres URL połączenia w ten sposób

jdbc:h2:mem:test

zawartość bazy danych jest tracona w momencie zamknięcia ostatniego połączenia.

Jeśli chcesz zachować zawartość, musisz skonfigurować adres URL w ten sposób

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

Jeśli to zrobisz, h2 zachowa swoją zawartość tak długo, jak żyje VM .

Zwróć uwagę na średnik ( ;) zamiast dwukropka ( :).

Zobacz sekcję Bazy danych w pamięci na stronie Funkcje . Cytować:

Domyślnie zamknięcie ostatniego połączenia z bazą danych zamyka bazę danych. W przypadku bazy danych w pamięci oznacza to utratę zawartości. Aby utrzymać bazę danych otwartą, dodaj ;DB_CLOSE_DELAY=-1do adresu URL bazy danych. Aby zachować zawartość bazy danych w pamięci tak długo, jak maszyna wirtualna żyje, użyj jdbc:h2:mem:test;DB_CLOSE_DELAY=-1.


W międzyczasie sam znalazłem problem, ale tak, to jest całkowicie poprawne. Dzięki!
Jorn

3
Musi to być nazwana baza danych w pamięci, tzn. jdbc:h2:mem:;DB_CLOSE_DELAY=-1Nie działa.
Peter Becker

jak możemy przechowywać dane w pliku zamiast w pamięci?
Suleman khan

9
jeśli używasz małych liter do nazywania swoich tabel w kodzie, powinieneś wiedzieć, że H2 wielkie litery domyślnie używają DATABASE_TO_UPPER = false, aby tego uniknąć np. jdbc: h2: mem: test; DB_CLOSE_DELAY = -1; DATABASE_TO_UPPER = false;
Oleksandr Petrenko

@OleksandrPetrenko - that trailing ';' wydaje się powodować problemy. Myślę, że musisz to pominąć.
Volksman

103

Wiem, że to nie był twój przypadek, ale miałem ten sam problem, ponieważ H2 tworzył tabele z nazwami WIELKIE LITERY, a następnie zachowywał rozróżnianie wielkości liter, mimo że we wszystkich skryptach (także tych tworzących) użyłem małych liter.

Rozwiązany przez dodanie ;DATABASE_TO_UPPER=falsedo adresu URL połączenia.


7
Wow - bardzo się cieszę, że to udostępniłeś! nigdy bym o tym nie pomyślał.
ms-tg

1
Nie rozwiązanie zadanego pytania, ale rozwiązanie problemu, który miałem podczas wyszukiwania tego samego pytania!
Yaytay

Czy można ustawić tę DATABASE_TO_UPPER=falsefunkcję jako instrukcję SQL w skrypcie inicjującym? (Podobnie jak w przypadku instrukcji SET MODE PostgreSQL;). Jeśli tak, jaka jest dokładna składnia?
Jonik

3
Jak mogę to wielokrotnie głosować? Wielkie dzięki! To powinna być część pierwszej odpowiedzi.
Ribesg,

11

Ciężko powiedzieć. Stworzyłem program do testowania tego:

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

Test został zakończony bez żadnych awarii i nieoczekiwanych wyników. Którą wersję h2 używasz?


Spróbuję tego jutro, dzięki. Wersja H2 to ta, którą dzisiaj opuściłem: 1.3.154
Jorn

1
Myślę, że znalazłem problem. Kiedy zamykam połączenie, przy pomocy którego została utworzona tabela, a następnie otwieram nowe, db nie ma. Kiedy otwieram nowe połączenie przed zamknięciem poprzedniego, dane pozostają. Kiedy korzystam z pliku, dane (oczywiście) zawsze pozostają.
Jorn

7

Baza danych H2 w pamięci przechowuje dane w pamięci w JVM. Gdy JVM kończy pracę, dane te są tracone.

Podejrzewam, że to, co robisz, jest podobne do dwóch poniższych klas Java. Jedna z tych klas tworzy tabelę, a druga próbuje wstawić do niej:

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

i

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

Kiedy prowadziłem te klasy jedna po drugiej, otrzymałem następujące dane wyjściowe:

C: \ Users \ Luke \ stuff> java CreateTable

C: \ Users \ Luke \ stuff> java InsertIntoTable
Wyjątek w wątku „main” org.h2.jdbc.JdbcSQLException: Nie znaleziono tabeli „PERSON”; Instrukcja SQL:
WSTAW DO OSOBY (ID, NAZWISKO, LASTNAME) WARTOŚCI (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)
        ...

Gdy tylko pierwszy javaproces zakończy się, utworzona przez CreateTablenie tabela już nie istnieje. Kiedy pojawia się klasa InsertIntoTable, nie ma w niej tabeli do wstawienia.

Kiedy zmieniłem parametry połączenia na jdbc:h2:test, okazało się, że nie było takiego błędu. Odkryłem również, że test.h2.dbpojawił się plik . W tym miejscu H2 umieścił tabelę, a ponieważ przechowywano ją na dysku, tabela wciąż tam była do znalezienia dla klasy InsertIntoTable.


1
Należy pamiętać, że registerDriver()wywołanie nie jest konieczne: po pierwsze: prosty Class.forName () robi to samo dla większości sterowników JDBC i (co ważniejsze) jest zupełnie niepotrzebny dla Java 6 und up, który automatycznie wykrywa (kompatybilny) sterowniki JDBC na ścieżka klasy.
Joachim Sauer

Db w pamięci istnieje tylko tak długo, jak długo działa program będący właścicielem pamięci? Wow, nie miałem pojęcia> _ <Ale naprawdę wiem, co próbuję zrobić. Czytając twoją odpowiedź, nie jestem pewien.
Jorn

2
@Jorn: Mogę nie wiedzieć, co próbujesz zrobić, zgaduję na podstawie dostarczonych informacji. Bardziej pomocne może być dostarczenie SSCCE ( sscce.org ) demonstrującego twój problem - nie nazwałbym twojego pytania „kompletnym” pod tym względem. Podałem powyższą odpowiedź, ponieważ na SO są ludzie (głównie nowicjusze programowania), którzy mogliby pomyśleć, że baza danych „in-memory” przechowuje dane w pamięci komputera gdzieś, gdzie mogłaby przetrwać między wywołaniami programu. Twoje pytanie nie było wystarczająco kompletne, aby przekonać mnie, że nie jesteś jedną z tych osób.
Luke Woodward

5

Próbowałem dodać

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

To jednak nie pomogło. Na stronie H2 znalazłem następujące, które rzeczywiście mogą pomóc w niektórych przypadkach.

Domyślnie zamknięcie ostatniego połączenia z bazą danych zamyka bazę danych. W przypadku bazy danych w pamięci oznacza to utratę zawartości. Aby utrzymać bazę danych otwartą, dodaj; DB_CLOSE_DELAY = -1 do adresu URL bazy danych. Aby zachować zawartość bazy danych w pamięci tak długo, jak maszyna wirtualna żyje, użyj jdbc: h2: mem: test; DB_CLOSE_DELAY = -1.

Jednak moim problemem było to, że tylko schemat powinien być inny niż domyślny. Tak instynkt użycia

JDBC URL: jdbc:h2:mem:test

Musiałem użyć:

JDBC URL: jdbc:h2:mem:testdb

Wtedy stoły były widoczne


dzięki „testdb” też było dla mnie poprawką (oprócz „DB_CLOSE_DELAY = -1”)!
boly38

4

Miałem ten sam problem i zmieniłem konfigurację w application-test.properties na to:

#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

I moje zależności:

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

A adnotacje użyte w klasie testowej:

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

3

Próbowałem pobrać metadane tabeli, ale wystąpił następujący błąd:

Za pomocą:

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

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

zwrócił pusty zestaw wyników.

Ale użycie następującego adresu URL działało poprawnie:

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

Konieczne było określenie: DATABASE_TO_UPPER = false


To nie dodaje niczego, czego nie obejmuje ta odpowiedź. Z recenzji .
Wai Ha Lee

3

Podczas otwierania konsoli h2 adres URL JDBC musi być zgodny z adresem określonym we właściwościach:

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

wprowadź opis zdjęcia tutaj

Co wydaje się oczywiste, ale spędziłem godziny, zastanawiając się nad tym.


2

Rozwiązany przez utworzenie nowego folderu src / test / resources + wstawienie pliku application.properties, wyraźnie określając, aby utworzyć testową bazę danych:

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

1

Przyszedłem do tego postu, ponieważ miałem ten sam błąd.

W moim przypadku ewolucje bazy danych nie zostały wykonane, więc tabeli w ogóle nie było.

Mój problem polegał na tym, że struktura folderów dla skryptów ewolucji była nieprawidłowa.

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

Play śledzi ewolucje bazy danych za pomocą kilku skryptów ewolucji. Te skrypty są napisane zwykłym starym SQL-em i powinny znajdować się w katalogu conf / evolutions / {nazwa bazy danych} Twojej aplikacji. Jeśli zmiany dotyczą domyślnej bazy danych, ta ścieżka to conf / evolutions / default.

Miałem folder o nazwie conf / evolutions.default utworzony przez eclipse. Problem zniknął po poprawieniu struktury folderów na 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

Miałem dokładnie ten sam problem, wypróbowałem wszystkie powyższe, ale bez powodzenia. Dość zabawną przyczyną błędu było to, że JVM uruchomił się zbyt szybko, zanim utworzono tabelę DB (przy użyciu pliku data.sql w pliku src.main.resources). Dlatego ustawiłem licznik Thread.sleep (1000), aby poczekał tylko sekundę przed wywołaniem „select * from person”. Teraz działa bez zarzutu.

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

główna klasa:

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

0

Odkryłem, że działa po dodaniu zależności Spring Data JPA od wersji Spring boot 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>

Dodaj konfigurację H2 DB w 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

Znalazłem rozwiązanie, dodając tę ​​konfigurację:

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

Pełna konfiguracja (z prostym 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

Pracuję z wersją h2 1.4.200 i Spring-Boot 2.2.6


-2

Time.sleep (1000);

To dla mnie praca. Tylko na próbę, jeśli twój komputer jest wolny, możesz wydłużyć czas trwania wątku, aby DB działał dobrze, a JVM mógł uzyskać naszą tabelę.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.