Base de données en mémoire H2. Table introuvable


187

J'ai une base de données H2 avec une URL "jdbc:h2:test". Je crée une table en utilisant CREATE TABLE PERSON (ID INT PRIMARY KEY, FIRSTNAME VARCHAR(64), LASTNAME VARCHAR(64));. Je sélectionne ensuite tout dans cette table (vide) en utilisant SELECT * FROM PERSON. Jusqu'ici tout va bien.

Cependant, si je change l'URL en "jdbc:h2:mem:test", la seule différence étant que la base de données est maintenant uniquement en mémoire, cela me donne un org.h2.jdbc.JdbcSQLException: Table "PERSON" not found; SQL statement: SELECT * FROM PERSON [42102-154]. Il me manque probablement quelque chose de simple ici, mais toute aide serait appréciée.


2
Après être passé en mode en mémoire, vous devez à nouveau créer la table Person. H2 ne sait rien de la base de données que vous avez créée sur le disque auparavant.
Benjamin Muschko

Le reste du programme n'a pas changé - j'ai de nouveau créé la table.
Jorn

Réponses:


340

DB_CLOSE_DELAY=-1

hbm2ddl ferme la connexion après avoir créé la table, donc h2 la rejette.

Si votre URL de connexion est configurée comme ceci

jdbc:h2:mem:test

le contenu de la base de données est perdu au moment où la dernière connexion est fermée.

Si vous souhaitez conserver votre contenu, vous devez configurer l'URL comme ceci

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

Si tel est le cas, h2 conservera son contenu aussi longtemps que la VM vivra.

Notez le point-virgule ( ;) plutôt que deux-points ( :).

Consultez la section Bases de données en mémoire de la page Fonctionnalités . Citer:

Par défaut, la fermeture de la dernière connexion à une base de données ferme la base de données. Pour une base de données en mémoire, cela signifie que le contenu est perdu. Pour garder la base de données ouverte, ajoutez ;DB_CLOSE_DELAY=-1à l'URL de la base de données. Pour conserver le contenu d'une base de données en mémoire tant que la machine virtuelle est active, utilisez jdbc:h2:mem:test;DB_CLOSE_DELAY=-1.


J'ai trouvé le problème moi-même entre-temps, mais oui, c'est tout à fait correct. Merci!
Jorn

3
Et il doit s'agir d'une base de données nommée en mémoire, c'est-à-dire jdbc:h2:mem:;DB_CLOSE_DELAY=-1ne fonctionne pas.
Peter Becker

comment pouvons-nous stocker des données dans le fichier au lieu de la mémoire?
Suleman khan

9
si vous utilisez des minuscules pour nommer vos tables dans un code, sachez que H2 tout en majuscule par défaut utilise DATABASE_TO_UPPER = false pour l'éviter, par exemple jdbc: h2: mem: test; DB_CLOSE_DELAY = -1; DATABASE_TO_UPPER = false;
Oleksandr Petrenko

@OleksandrPetrenko - qui traîne ';' semble causer des problèmes. Je pense que vous devez laisser cela de côté.
Volksman

110

Je sais que ce n'était pas votre cas, mais j'ai eu le même problème car H2 créait les tables avec des noms MAJUSCULES puis se comportait en tenant compte de la casse, même si dans tous les scripts (y compris ceux de création), j'utilisais des minuscules.

Résolu en ajoutant ;DATABASE_TO_UPPER=falseà l'URL de connexion.


7
Wow - Je suis très content que vous ayez partagé celui-ci! Je n'aurais jamais pensé à ça.
ms-tg

1
Pas la solution à la question posée, mais la solution au problème que j'avais en cherchant avec la même question!
Yaytay

Est-il possible de définir cette DATABASE_TO_UPPER=falsechose comme une instruction SQL dans un script d'initialisation? (De même comme une déclaration comme SET MODE PostgreSQL;) Si oui, quelle est la syntaxe exacte?
Jonik

3
Comment puis-je voter plusieurs fois? Merci beaucoup! Cela devrait faire partie de la première réponse.
Ribesg

11

Dur à dire. J'ai créé un programme pour tester ceci:

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

Le test s'est terminé, sans échec ni sortie inattendue. Quelle version de h2 utilisez-vous?


Je vais essayer ça demain, merci. La version H2 est celle que j'ai sortie du site aujourd'hui: 1.3.154
Jorn

1
Je pense avoir trouvé le problème. Lorsque je ferme la connexion avec laquelle la table a été créée, puis j'en ouvre une nouvelle, la base de données a disparu. Lorsque j'ouvre une nouvelle connexion avant de fermer la précédente, les données restent. Lorsque j'utilise un fichier, les données restent (évidemment) toujours.
Jorn

7

La base de données en mémoire H2 stocke les données en mémoire dans la JVM. Lorsque la JVM se ferme, ces données sont perdues.

Je soupçonne que ce que vous faites est similaire aux deux classes Java ci-dessous. L'une de ces classes crée une table et l'autre essaie de s'y insérer:

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

et

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

Lorsque j'ai exécuté ces classes les unes après les autres, j'ai obtenu le résultat suivant:

C: \ Users \ Luke \ stuff> java CreateTable

C: \ Users \ Luke \ stuff> java InsertIntoTable
Exception dans le thread "main" org.h2.jdbc.JdbcSQLException: Table "PERSON" introuvable; Instruction SQL:
INSÉRER DANS LES VALEURS PERSONNE (ID, PRÉNOM, LASTNAME) (1, 'John', 'Doe') [42102-154]
        à org.h2.message.DbException.getJdbcSQLException (DbException.java:327)
        à org.h2.message.DbException.get (DbException.java:167)
        à org.h2.message.DbException.get (DbException.java:144)
        ...

Dès que le premier javaprocessus se termine, la table créée par CreateTablen'existe plus. Ainsi, lorsque la classe InsertIntoTable arrive, il n'y a pas de table dans laquelle l'insérer.

Lorsque j'ai changé les chaînes de connexion en jdbc:h2:test, j'ai constaté qu'il n'y avait pas une telle erreur. J'ai également constaté qu'un fichier test.h2.dbétait apparu. C'était là que H2 avait mis la table, et comme elle avait été stockée sur le disque, la table était toujours là pour que la classe InsertIntoTable la trouve.


1
Veuillez noter que l' registerDriver()appel n'est pas nécessaire: Tout d'abord: un simple Class.forName () fait la même chose pour la plupart des pilotes JDBC et (plus important encore) il est complètement inutile pour Java 6 et up, qui détecte automatiquement les pilotes JDBC (compatibles) sur le classpath.
Joachim Sauer

Une base de données en mémoire existe uniquement tant que le programme propriétaire de la mémoire est en cours d'exécution? Wow, je n'avais aucune idée> _ <Mais vraiment, je sais ce que j'essaye de faire. En lisant votre réponse, je ne suis pas sûr que vous l'ayez compris
Jorn

2
@Jorn: Je ne sais peut-être pas ce que vous essayez de faire, je suppose en fonction des informations que vous avez fournies. Il aurait peut-être été plus utile de fournir un SSCCE ( sscce.org ) démontrant votre problème - je n'appellerais pas votre question «complète» à cet égard. J'ai fourni la réponse ci-dessus parce qu'il y a des gens sur SO (novices en programmation, principalement) qui pourraient penser qu'une base de données `` en mémoire '' stocke les données dans la mémoire de l'ordinateur quelque part où elles pourraient survivre entre les appels de programme. Votre question n'était pas assez complète pour me convaincre que vous n'étiez pas l'une de ces personnes.
Luke Woodward

5

J'ai essayé d'ajouter

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

Cependant, cela n'a pas aidé. Sur le site H2 , j'ai trouvé des suivis, qui pourraient en effet aider dans certains cas.

Par défaut, la fermeture de la dernière connexion à une base de données ferme la base de données. Pour une base de données en mémoire, cela signifie que le contenu est perdu. Pour garder la base de données ouverte, ajoutez; DB_CLOSE_DELAY = -1 à l'URL de la base de données. Pour conserver le contenu d'une base de données en mémoire tant que la machine virtuelle est active, utilisez jdbc: h2: mem: test; DB_CLOSE_DELAY = -1.

Cependant , mon problème était que seul le schéma était censé être différent de celui par défaut. Tellement instable d'utiliser

JDBC URL: jdbc:h2:mem:test

J'ai dû utiliser:

JDBC URL: jdbc:h2:mem:testdb

Puis les tables étaient visibles


merci, "testdb" était un correctif pour moi aussi (en plus de "DB_CLOSE_DELAY = -1")!
boly38

4

J'ai eu le même problème et j'ai changé ma configuration dans application-test.properties en ceci:

#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

Et mes dépendances:

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

Et les annotations utilisées sur la classe de test:

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

3

J'essayais de récupérer les métadonnées de la table, mais j'ai eu l'erreur suivante:

En utilisant:

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

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

a renvoyé un ResultSet vide.

Mais en utilisant l'URL suivante à la place, cela a fonctionné correctement:

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

Il était nécessaire de spécifier: DATABASE_TO_UPPER = false


1
Cela n'ajoute rien qui n'est pas couvert par cette réponse. De l'avis .
Wai Ha Lee

3

Lors de l'ouverture de la console h2, l'URL JDBC doit correspondre à celle spécifiée dans les propriétés:

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

entrez la description de l'image ici

Ce qui semble évident, mais j'ai passé des heures à comprendre cela.


2

Résolu en créant un nouveau dossier src / test / resources + insérez le fichier application.properties, en spécifiant explicitement de créer une base de données de test:

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

1

Je suis venu à ce poste parce que j'ai eu la même erreur.

Dans mon cas, les évolutions de la base de données n'ont pas été exécutées, donc la table n'y était pas du tout.

Mon problème était que la structure des dossiers pour les scripts d'évolution était incorrecte.

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

Play suit les évolutions de votre base de données à l'aide de plusieurs scripts d'évolution. Ces scripts sont écrits en SQL ancien et doivent se trouver dans le répertoire conf / evolutions / {nom de la base de données} de votre application. Si les évolutions s'appliquent à votre base de données par défaut, ce chemin est conf / evolutions / default.

J'avais un dossier appelé conf / evolutions.default créé par eclipse. Le problème a disparu après avoir corrigé la structure des dossiers en conf / evolutions / default


1

Eu exactement le même problème, essayé tout ce qui précède, mais sans succès. La cause plutôt amusante de l'erreur était que la machine virtuelle Java avait démarré trop rapidement, avant la création de la table DB (en utilisant un fichier data.sql dans src.main.resources). J'ai donc mis une minuterie Thread.sleep (1000) pour attendre juste une seconde avant d'appeler "select * from person". Fonctionne parfaitement maintenant.

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

1

Je l'ai trouvé fonctionnant après avoir ajouté la dépendance de Spring Data JPA sur la version 2.2.6 de Spring Boot.

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

Ajouter la configuration H2 DB dans 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
<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

J'ai trouvé la solution en ajoutant cette configuration:

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

La configuration complète (avec 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

Je travaille avec h2 version 1.4.200 et Spring-Boot 2.2.6.RELEASE


0

J'ai essayé la solution ci-dessus, mais dans mon cas, comme suggéré dans la console, j'ai ajouté la propriété DB_CLOSE_ON_EXIT = FALSE , cela a résolu le problème.

 spring.datasource.url=jdbc:h2:mem:testdb;DATABASE_TO_UPPER=false;DB_CLOSE_ON_EXIT=FALSE

-2

Time.sleep (1000);

C'est du travail pour moi. Juste pour essayer, si votre PC est lent, vous pouvez augmenter la durée du thread pour que DB fonctionne correctement et que JVM puisse obtenir notre table.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.