Base de datos H2 en memoria. Tabla no encontrada

183

Tengo una base de datos H2 con URL "jdbc:h2:test". Creo una tabla usando CREATE TABLE PERSON (ID INT PRIMARY KEY, FIRSTNAME VARCHAR(64), LASTNAME VARCHAR(64));. Luego selecciono todo de esta tabla (vacía) usando SELECT * FROM PERSON. Hasta aquí todo bien.

Sin embargo, si cambio la URL a "jdbc:h2:mem:test", la única diferencia es que la base de datos ahora está solo en la memoria, esto me da un org.h2.jdbc.JdbcSQLException: Table "PERSON" not found; SQL statement: SELECT * FROM PERSON [42102-154]. Probablemente me estoy perdiendo algo simple aquí, pero cualquier ayuda sería apreciada.

Jorn
fuente
2
Después de cambiar al modo en memoria, debe Personvolver a crear la tabla . H2 no sabe nada sobre la base de datos que creó en el disco antes.
Benjamin Muschko
El resto del programa no cambió, volví a crear la tabla.
Jorn

Respuestas:

331

DB_CLOSE_DELAY=-1

hbm2ddl cierra la conexión después de crear la tabla, por lo que h2 la descarta.

Si tienes tu URL de conexión configurada de esta manera

jdbc:h2:mem:test

el contenido de la base de datos se pierde en el momento en que se cierra la última conexión.

Si desea mantener su contenido, debe configurar la URL de esta manera

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

Si lo hace, h2 mantendrá su contenido mientras dure la vm .

Observe el punto y coma ( ;) en lugar de dos puntos ( :).

Consulte la sección Bases de datos en memoria de la página Características . Citar:

Por defecto, al cerrar la última conexión a una base de datos se cierra la base de datos. Para una base de datos en memoria, esto significa que se pierde el contenido. Para mantener abierta la base de datos, agregue ;DB_CLOSE_DELAY=-1a la URL de la base de datos. Para mantener el contenido de una base de datos en memoria mientras la máquina virtual esté viva, use jdbc:h2:mem:test;DB_CLOSE_DELAY=-1.

reini2901
fuente
Encontré el problema mientras tanto, pero sí, esto es completamente correcto. ¡Gracias!
Jorn
3
Y tiene que ser una base de datos nombrada en memoria, es decir jdbc:h2:mem:;DB_CLOSE_DELAY=-1, no funciona.
Peter Becker
¿Cómo podemos almacenar datos en el archivo en lugar de la memoria?
Suleman khan el
9
si usa minúsculas para nombrar sus tablas en un código, debe saber que todo en mayúsculas H2 usa DATABASE_TO_UPPER = false para evitarlo, por ejemplo, jdbc: h2: mem: test; DB_CLOSE_DELAY = -1; DATABASE_TO_UPPER = false;
Oleksandr Petrenko
@OleksandrPetrenko - ese final ';' Parece causar problemas. Creo que debes dejar eso fuera.
Volksman el
104

Sé que este no fue su caso, pero tuve el mismo problema porque H2 estaba creando las tablas con MAYÚSCULAS y luego se comportaba con mayúsculas y minúsculas, aunque en todas las secuencias de comandos (incluidas las de creación) utilicé minúsculas.

Resuelto agregando ;DATABASE_TO_UPPER=falsea la URL de conexión.

Cristian Vrabie
fuente
77
Wow - ¡Estoy muy contento de que hayas compartido este! Nunca habría pensado en eso.
ms-tg
1
¡No es la solución a la pregunta que se hizo, sino la solución al problema que estaba teniendo al buscar con la misma pregunta!
Yaytay
¿Es posible configurar esto DATABASE_TO_UPPER=falsecomo una declaración SQL en un script de inicio? (De manera similar como una declaración como SET MODE PostgreSQL;) Si es así, ¿cuál es la sintaxis exacta?
Jonik
3
¿Cómo puedo votar esto varias veces? ¡Muchas gracias! Esto debería ser parte de la primera respuesta.
Ribesg
11

Difícil de decir. Creé un programa para probar esto:

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

La prueba se ejecutó hasta su finalización, sin fallas y sin resultados inesperados. ¿Qué versión de h2 estás ejecutando?

Joseph Ottinger
fuente
Intentaré esto mañana, gracias. La versión H2 es la que me dieron fuera de la página actual: 1.3.154
Jorn
1
Creo que encontré el problema. Cuando cierro la conexión con la que se creó la tabla y luego abro una nueva, la base de datos desaparece. Cuando abro una nueva conexión antes de cerrar la anterior, los datos permanecen. Cuando uso un archivo, los datos (obviamente) siempre permanecen.
Jorn
7

La base de datos H2 en memoria almacena datos en la memoria dentro de la JVM. Cuando la JVM sale, estos datos se pierden.

Sospecho que lo que está haciendo es similar a las dos clases de Java a continuación. Una de estas clases crea una tabla y la otra intenta insertarla:

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

y

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

Cuando ejecuté estas clases una tras otra, obtuve el siguiente resultado:

C: \ Users \ Luke \ stuff> java CreateTable

C: \ Users \ Luke \ stuff> java InsertIntoTable
Excepción en el hilo "main" org.h2.jdbc.JdbcSQLException: Tabla "PERSON" no encontrada; Instrucción SQL:
INSERTAR EN LA PERSONA (ID, NOMBRE, APELLIDOS) VALORES (1, 'John', 'Doe') [42102-154]
        en org.h2.message.DbException.getJdbcSQLException (DbException.java:327)
        en org.h2.message.DbException.get (DbException.java:167)
        en org.h2.message.DbException.get (DbException.java:144)
        ...

Tan pronto como javase cierra el primer proceso, la tabla creada por CreateTableya no existe. Entonces, cuando aparece la clase InsertIntoTable, no hay una tabla para insertarla.

Cuando cambié las cadenas de conexión a jdbc:h2:test, encontré que no había tal error. También encontré que test.h2.dbhabía aparecido un archivo . Aquí era donde H2 había colocado la tabla, y como se había almacenado en el disco, la tabla todavía estaba allí para que la clase InsertIntoTable la encontrara.

Luke Woodward
fuente
1
Tenga en cuenta que la registerDriver()llamada es innecesaria: Primero: un Class.forName () hace lo mismo para la mayoría de los controladores JDBC y (lo que es más importante) es completamente innecesario para Java 6 y superiores, que detecta automáticamente los controladores JDBC (compatibles) en el classpath
Joachim Sauer
¿Existe una base de datos en memoria solo mientras el programa que posee la memoria se esté ejecutando? Wow, no tenía idea> _ <Pero realmente, sé lo que estoy tratando de hacer. Al leer tu respuesta, no estoy seguro de que lo hagas.
Jorn
2
@Jorn: Puede que no sepa qué estás tratando de hacer, supongo que en función de la información que proporcionaste. Puede haber sido más útil proporcionar un SSCCE ( sscce.org ) que demuestre su problema; no diría que su pregunta es 'completa' a ese respecto. Proporcioné la respuesta anterior porque hay personas en SO (recién llegados a la programación, principalmente) que podrían pensar que una base de datos 'en memoria' almacena los datos en la memoria de la computadora en algún lugar donde podría sobrevivir entre las invocaciones del programa. Tu pregunta no fue lo suficientemente completa como para convencerme de que no eras una de estas personas.
Luke Woodward
5

He intentado agregar

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

Sin embargo, eso no ayudó. En el sitio H2 , encontré lo siguiente, que de hecho podría ayudar en algunos casos.

Por defecto, al cerrar la última conexión a una base de datos se cierra la base de datos. Para una base de datos en memoria, esto significa que se pierde el contenido. Para mantener abierta la base de datos, agregue; DB_CLOSE_DELAY = -1 a la URL de la base de datos. Para mantener el contenido de una base de datos en memoria mientras la máquina virtual esté viva, use jdbc: h2: mem: test; DB_CLOSE_DELAY = -1.

Sin embargo , mi problema era que solo el esquema se suponía que era diferente al predeterminado. Tan insted de usar

JDBC URL: jdbc:h2:mem:test

Tuve que usar:

JDBC URL: jdbc:h2:mem:testdb

Entonces las mesas fueron visibles

DevDio
fuente
¡gracias, "testdb" también fue una solución para mí (además de "DB_CLOSE_DELAY = -1")!
boly38
4

Tuve el mismo problema y cambié mi configuración en application-test.properties a esto:

#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

Y mis dependencias:

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

Y las anotaciones utilizadas en la clase de prueba:

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

Estaba tratando de recuperar los metadatos de la tabla, pero tuve el siguiente error:

Utilizando:

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

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

devolvió un ResultSet vacío.

Pero usar la siguiente URL en su lugar funcionó correctamente:

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

Era necesario especificar: DATABASE_TO_UPPER = false

Miguel Angel Vega Pabon
fuente
Esto no agrega nada no cubierto por esta respuesta. De la crítica .
Wai Ha Lee
3

Al abrir la consola h2, la URL JDBC debe coincidir con la especificada en las propiedades:

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

ingrese la descripción de la imagen aquí

Lo cual parece obvio, pero pasé horas resolviendo esto ...

nagy.zsolt.hun
fuente
2

Se resolvió creando una nueva carpeta src / test / resources + inserte el archivo application.properties, especificando explícitamente la creación de una base de datos de prueba:

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

Vine a esta publicación porque tuve el mismo error.

En mi caso, las evoluciones de la base de datos no se ejecutaron, por lo que la tabla no estaba allí en absoluto.

Mi problema fue que la estructura de carpetas para los scripts de evolución era incorrecta.

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

Play realiza un seguimiento de las evoluciones de su base de datos utilizando varios scripts de evoluciones. Estas secuencias de comandos están escritas en SQL antiguo y deben ubicarse en el directorio conf / evolutions / {database name} de su aplicación. Si las evoluciones se aplican a su base de datos predeterminada, esta ruta es conf / evolutions / default.

Tenía una carpeta llamada conf / evolutions.default creada por eclipse. El problema desapareció después de corregir la estructura de la carpeta a conf / evolutions / default

Oscar Fraxedas
fuente
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
fuente
0

Tuve exactamente el mismo problema, probé todo lo anterior, pero sin éxito. La causa bastante divertida del error fue que la JVM comenzó demasiado rápido, antes de que se creara la tabla DB (usando un archivo data.sql en src.main.resources). Así que puse un temporizador Thread.sleep (1000) para esperar solo un segundo antes de llamar a "select * from person". Trabajando perfectamente ahora.

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

clase principal:

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

Lo encontré funcionando después de agregar la dependencia de Spring Data JPA en la versión de arranque 2.2.6 de 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>

Agregue la configuración H2 DB en 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
fuente
0

Encontré la solución agregando esta configuración:

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

La configuración completa (con spring.datasource.url simple):

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

Estoy trabajando con h2 versión 1.4.200 y Spring-Boot 2.2.6.

Halcón
fuente
-2

Time.sleep (1000);

Esto es trabajo para mi. Solo para probar, si su PC es lenta, puede aumentar la duración del subproceso para que DB funcione bien y JVM pueda obtener nuestra tabla.

Dhara N.
fuente