H2内存数据库。找不到表格


182

我有一个带有URL的H2数据库"jdbc:h2:test"。我使用创建表格CREATE TABLE PERSON (ID INT PRIMARY KEY, FIRSTNAME VARCHAR(64), LASTNAME VARCHAR(64));。然后,我使用来从此(空)表中选择所有内容SELECT * FROM PERSON。到目前为止,一切都很好。

但是,如果将URL更改为"jdbc:h2:mem:test",唯一的区别是数据库现在仅在内存中,这给了我一个提示org.h2.jdbc.JdbcSQLException: Table "PERSON" not found; SQL statement: SELECT * FROM PERSON [42102-154]。我可能在这里缺少一些简单的东西,但是任何帮助将不胜感激。


2
切换到内存模式后,您必须Person再次创建表。H2对您之前在磁盘上创建的数据库一无所知。
本杰明·穆奇科

程序的其余部分没有更改-我确实再次创建了表。
乔恩

Answers:


330

DB_CLOSE_DELAY=-1

hbm2ddl在创建表后关闭连接,因此h2放弃该连接。

如果您的连接网址是这样配置的

jdbc:h2:mem:test

在最后一个连接关闭时,数据库的内容会丢失。

如果要保留内容,则必须像这样配置网址

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

如果这样做,h2将保留其内容,直到虚拟机存活

注意分号(;)而不是冒号(:)。

请参阅“ 功能”页面的“内存数据库”部分。报价:

默认情况下,关闭与数据库的最后一个连接会关闭数据库。对于内存数据库,这意味着内容丢失。要保持数据库打开状态,请添加;DB_CLOSE_DELAY=-1到数据库URL。要在虚拟机处于活动状态时保留内存数据库的内容,请使用jdbc:h2:mem:test;DB_CLOSE_DELAY=-1


同时,我自己发现了问题,但是,是的,这是完全正确的。谢谢!
乔恩

3
它必须是一个命名的内存数据库,即jdbc:h2:mem:;DB_CLOSE_DELAY=-1不起作用。
彼得·贝克尔

我们如何将数据存储在文件中而不是内存中?
苏莱曼汗

8
如果使用小写形式在代码中命名表,则应该知道H2默认情况下所有大写形式都使用DATABASE_TO_UPPER = false来避免,例如jdbc:h2:mem:test; DB_CLOSE_DELAY = -1; DATABASE_TO_UPPER = false;
Oleksandr Petrenko

@OleksandrPetrenko-尾随';' 似乎会引起问题。我认为您需要忽略这一点。
大众队'18

102

我知道这不是您的情况,但我遇到了同样的问题,因为即使我在所有脚本(包括创建脚本)中都使用小写字母,H2都使用大写名称创建表,然后区分大小写。

通过添加;DATABASE_TO_UPPER=false到连接URL来解决。


7
哇-我很高兴您分享了这个!从来没有想过。
ms-tg

1
不是所提出问题的解决方案,而是我在搜索相同问题时遇到的问题的解决方案!
Yaytay 2015年

是否可以DATABASE_TO_UPPER=false在初始化脚本中将此内容设置为SQL语句?(类似于类似的语句SET MODE PostgreSQL;)如果是,确切的语法是什么?
Jonik 2015年

3
我该如何多次投票?非常感谢!这应该是第一个答案的一部分。
Ribesg

11

很难说。我创建了一个程序对此进行测试:

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

测试运行完成,没有失败,也没有意外的输出。您正在运行哪个版本的h2?


谢谢,我明天再试试。H2版本是我今天离开网站的版本:1.3.154
Jorn

1
我想我找到了问题。当我关闭用于创建表的连接,然后打开一个新表时,数据库消失了。在关闭上一个连接之前打开一个新连接时,数据将保留。当我使用文件时,数据(显然)始终保留。
乔恩

7

H2内存数据库将数据存储在JVM内部的内存中。JVM退出时,此数据将丢失。

我怀疑您在做什么与下面的两个Java类相似。这些类之一创建一个表,另一个尝试插入该表:

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

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

当我一个接一个地运行这些类时,得到以下输出:

C:\ Users \ Luke \ stuff> java CreateTable

C:\ Users \ Luke \ stuff> java InsertIntoTable
线程“主” org.h2.jdbc.JdbcSQLException中的异常:未找到表“ PERSON”;SQL语句:
插入人(ID,名字,姓氏)值(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)
        ...

第一个java进程退出后,由创建的表将CreateTable不再存在。因此,当InsertIntoTable类出现时,没有可插入的表。

当我将连接字符串更改为时jdbc:h2:test,我发现没有此类错误。我还发现文件test.h2.db已经出现。这是H2放置表的地方,并且由于它已存储在磁盘上,因此该表仍在此处以供InsertIntoTable类查找。


1
请注意,该registerDriver()调用是不必要的:首先:一个简单的Class.forName()对于大多数JDBC驱动程序都执行相同的操作(更重要的是)对于Java 6 und up来说完全没有必要,因为Java 6 und up在Java 6上自动检测(兼容)JDBC驱动程序。类路径。
Joachim Sauer

只要拥有内存的程序正在运行,内存中的数据库就存在吗?哇,我不知道> _ <但实际上,我知道我要做什么。阅读您的答案,我不确定您是否知道。
乔恩

2
@乔恩:我可能不知道您要做什么,我是根据您提供的信息猜测的。提供一个SSCCE(sscce.org)来演示您的问题可能会更有帮助-在这方面,我不会将您的问题称为“完整的”。我之所以提供上述答案,是因为SO上的人们(主要是程序设计的新手)可能会认为“内存中”数据库将数据存储在计算机内存中的某个位置,在程序调用之间可以生存。您的问题还不够完整,无法说服我您不是这些人之一。
卢克·伍德沃德

5

我试图添加

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

但是,这没有帮助。在H2网站上,我发现了以下内容,在某些情况下确实可以提供帮助。

默认情况下,关闭与数据库的最后一个连接会关闭数据库。对于内存数据库,这意味着内容丢失。若要保持数据库打开状态,请在数据库URL中添加; DB_CLOSE_DELAY = -1。要在虚拟机处于活动状态时保留内存数据库的内容,请使用jdbc:h2:mem:test; DB_CLOSE_DELAY = -1。

但是,我的问题是,该模式应该与默认模式不同。如此使用

JDBC URL: jdbc:h2:mem:test

我不得不使用:

JDBC URL: jdbc:h2:mem:testdb

然后表格可见


谢谢,“ testdb”对我也是一个修复(除了“ DB_CLOSE_DELAY = -1”)!
boly38 '17

4

我遇到了同样的问题,并将application-test.properties中的配置更改为此:

#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

而我的依赖:

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

以及在测试类上使用的注释:

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

3

我试图获取表元数据,但出现以下错误:

使用:

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

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

返回一个空的ResultSet。

但是使用以下URL可以正常工作:

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

需要指定:DATABASE_TO_UPPER = false


这不会添加答案未涵盖的任何内容。从评论
惠河利

3

打开h2-console时,JDBC URL必须与属性中指定的URL匹配:

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

在此处输入图片说明

这似乎很明显,但是我花了数小时才弄清楚这一点。


2

通过创建新的src / test / resources文件夹+插入application.properties文件(明确指定创建测试dbase)来解决:

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

1

我来这篇文章是因为我有同样的错误。

在我的情况下,数据库演化没有执行,因此表根本不存在。

我的问题是演进脚本的文件夹结构不正确。

来自:https : //www.playframework.com/documentation/2.0/Evolutions

Play使用多个Evolution脚本跟踪您的数据库演化。这些脚本使用普通的旧SQL编写,应位于应用程序的conf / evolutions / {数据库名称}目录中。如果演进适用于您的默认数据库,则此路径为conf / evolutions / default。

我有一个由Eclipse创建的名为conf / evolutions.default的文件夹。将文件夹结构更正为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

遇到了完全相同的问题,请尝试以上所有方法,但均未成功。该错误的相当有趣的原因是,在创建DB表之前(使用src.main.resources中的data.sql文件),JVM启动速度太快。因此,在调用“ select * from person”之前,我放置了一个Thread.sleep(1000)计时器等待一秒钟。现在可以正常工作。

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

主类:

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

0

在Spring Boot版本2.2.6.RELEASE中添加了Spring Data JPA的依赖关系后,我发现它可以正常工作-

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

在application.yml中添加H2 DB配置-

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

我通过添加以下配置找到了解决方案:

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

完整配置(使用简单的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

我正在使用H2版本1.4.200Spring-Boot 2.2.6.RELEASE


-2

Time.sleep(1000);

这对我来说是工作。只是尝试一下,如果您的PC速度很慢,则可以增加线程的持续时间,以使DB正常工作,并且JVM可以获取我们的表。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.