【问题标题】:H2 in-memory database. Table not foundH2 内存数据库。找不到表
【发布时间】:2011-08-11 10:45:18
【问题描述】:

我有一个带有 URL "jdbc:h2:test" 的 H2 数据库。我使用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]。我可能在这里遗漏了一些简单的东西,但我们将不胜感激。

【问题讨论】:

  • 切换到内存模式后,您必须再次创建表Person。 H2 对您之前在磁盘上创建的数据库一无所知。
  • 程序的其余部分没有改变 - 我确实再次创建了表。

标签: java database h2


【解决方案1】:

很难说。我创建了一个程序来测试这个:

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
  • 我想我找到了问题所在。当我关闭创建表的连接,然后打开一个新的连接时,数据库就消失了。当我在关闭前一个连接之前打开一个新连接时,数据仍然存在。当我使用文件时,数据(显然)始终保留。
【解决方案2】:

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 类可以找到该表格。

【讨论】:

  • 请注意 registerDriver() 调用是不必要的:首先:一个简单的 Class.forName() 对大多数 JDBC 驱动程序执行相同的操作并且(更重要的是)它完全Java 6 und up 不需要,它会在类路径上自动检测(兼容)JDBC 驱动程序。
  • 只有拥有内存的程序正在运行,内存中的数据库才存在?哇,我不知道>_
  • @Jorn:我可能不知道您要做什么,我根据您提供的信息猜测。提供一个 SSCCE (sscce.org) 来展示您的问题可能会更有帮助 - 在这方面我不会称您的问题“完整”。我提供上述答案是因为 SO(主要是编程新手)上的某些人可能认为“内存中”数据库将数据存储在计算机内存中的某个位置,它可以在程序调用之间生存。你的问题不够完整,无法让我相信你不是这些人中的一员。
【解决方案3】:

DB_CLOSE_DELAY=-1

hbm2ddl在建表后关闭连接,所以h2将其丢弃。

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

jdbc:h2:mem:test

数据库的内容在最后一个连接关闭的那一刻丢失。

如果你想保留你的内容,你必须像这样配置 url

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

如果这样做,只要 vm 存在,h2 就会保留其内容。

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

查看功能页面的In-Memory Databases部分。引用:

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

【讨论】:

  • 我自己也发现了这个问题,但是是的,这是完全正确的。谢谢!
  • 并且它必须是一个命名的内存数据库,即jdbc:h2:mem:;DB_CLOSE_DELAY=-1不起作用。
  • 我们如何将数据存储在文件中而不是内存中?
  • 如果你在代码中使用小写来命名你的表,你应该知道 H2 大写的一切默认使用 DATABASE_TO_UPPER=false 来避免它,例如jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false;
  • @OleksandrPetrenko - 尾随 ';'似乎会引起问题。我认为你需要把它排除在外。
【解决方案4】:

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

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

【讨论】:

  • 哇 - 我很高兴你分享了这个!永远不会想到这一点。
  • 不是所提问题的解决方案,而是我在搜索相同问题时遇到的问题的解决方案!
  • 是否可以在初始化脚本中将这个DATABASE_TO_UPPER=false 设置为 SQL 语句? (类似于SET MODE PostgreSQL; 之类的语句)如果是这样,确切的语法是什么?
  • 我怎样才能多次投票?非常感谢!这应该是第一个答案的一部分。
【解决方案5】:

我来这个帖子是因为我有同样的错误。

在我的例子中,数据库演化没有被执行,所以表根本不存在。

我的问题是进化脚本的文件夹结构错误。

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

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

我有一个名为 conf/evolutions.default 的文件夹,由 eclipse 创建。在我将文件夹结构更正为 conf/evolutions/default

后问题消失了

【讨论】:

    【解决方案6】:

    我已经尝试添加

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

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

    默认情况下,关闭与数据库的最后一个连接会关闭数据库。对于内存数据库,这意味着内容丢失。要保持数据库打开,请将 ;DB_CLOSE_DELAY=-1 添加到数据库 URL。要在虚拟机处于活动状态时保留内存数据库的内容,请使用 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”)!
    【解决方案7】:
    <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>
    

    【讨论】:

      【解决方案8】:

      通过新建 src/test/resources 文件夹 + 插入 application.properties 文件解决,明确指定创建测试数据库:

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

      【讨论】:

        【解决方案9】:

        我遇到了同样的问题,并将 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 {
        ...
        }
        

        【讨论】:

          【解决方案10】:

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

          使用:

          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

          【讨论】:

          【解决方案11】:

          打开 h2-console 时,JDBC 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
          

          这似乎很明显,但我花了好几个小时才弄清楚这一点..

          【讨论】:

          • 感谢在接下来的几个小时内完成的面试任务,在我看到这条评论之前浪费了几个宝贵的时间,这就是问题所在!
          【解决方案12】:

          遇到完全相同的问题,尝试了以上所有方法,但均未成功。 该错误的一个相当有趣的原因是 JVM 在创建 DB 表之前启动得太快(使用 src.main.resources 中的 data.sql 文件)。所以我在调用“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());
          

          【讨论】:

            【解决方案13】:

            我发现它在添加 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
            

            【讨论】:

            • 问题跟spring没有关系,简单的问题扔到框架里解决很粗糙
            【解决方案14】:

            我已经尝试了上述解决方案,但在我的情况下,按照控制台中的建议添加了属性 DB_CLOSE_ON_EXIT=FALSE,它解决了问题。

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

            【讨论】:

              【解决方案15】:

              我尝试添加;DATABASE_TO_UPPER=false 参数,它在单个测试中确实有效,但对我有用的是;CASE_INSENSITIVE_IDENTIFIERS=TRUE

              最后我得到了:jdbc:h2:mem:testdb;CASE_INSENSITIVE_IDENTIFIERS=TRUE

              此外,我升级到Spring Boot 2.4.1时遇到的问题。

              【讨论】:

              • 我将 Spring Boot 从 1.3.3.RELEASE 升级到 2.4.4 并添加 ;CASE_INSENSITIVE_IDENTIFIERS=TRUE 也对我有用。
              【解决方案16】:

              在我的情况下,在 jpa 测试期间发生了缺少表错误,表是由 schem.sql 文件创建的,在将 @org.springframework.transaction.annotation.Transactional 放在测试后问题得到修复

              【讨论】:

                【解决方案17】:

                对于 Spring Boot 2.4+ 采用 spring.jpa.defer-datasource-initialization=true 在 application.properties 中

                【讨论】:

                • 现货 :) 解决了我的问题。谢谢!
                • 非常感谢。
                • 这确实是一个漂亮的解决方案。你能解释一下这个语句是做什么的吗?
                • 谢谢!它就像一个魅力
                • 谢谢兄弟..
                【解决方案18】:

                我参加聚会可能有点晚了,但我遇到了完全相同的错误,我尝试了这里和其他网站上提到的几乎所有解决方案,例如 DATABASE_TO_UPPER=false;DB_CLOSE_DELAY=-1; DB_CLOSE_ON_EXIT=假;忽略=真

                但对我没有任何帮助。 对我有用的是将 data.sql 重命名为 import.sql

                我在这里找到了 - https://stackoverflow.com/a/53179547/8219358

                或者

                对于 Spring Boot 2.4+,在 application.properties 中使用 spring.jpa.defer-datasource-initialization=true(此处提到 - https://stackoverflow.com/a/68086707/8219358

                我意识到其他解决方案更合乎逻辑,但没有一个对我有用,而且确实如此。

                【讨论】:

                  【解决方案19】:

                  在我的例子中,我使用了特殊关键字作为表中的列名,H2 数据库。如果您使用不同的数据库,请避免跨不同数据库使用这些特殊关键字。 Spring & Hibernate 不够聪明,无法准确告诉您哪些列名被禁止或确切的错误在表创建中的位置。 关键字如;

                  描述、区间、度量

                  为了解决我遇到的问题,我将这些字段重命名为:

                  descr、time_interval、time_metric

                  http://www.h2database.com/html/advanced.html

                  【讨论】:

                    【解决方案20】:
                       Use the same in applications.properties file
                       
                       spring.jpa.show-sql=true
                       spring.datasource.url=jdbc:h2:mem:testdb;DATABASE_TO_UPPER=false
                       DB_CLOSE_ON_EXIT=FALSE
                       spring.data.jpa.repositories.bootstrap-mode=default
                       spring.h2.console.enabled=true spring.jpa.generate-ddl=true
                       spring.jpa.hibernate.ddl-auto=create
                       spring.datasource.driverClassName=org.h2.Driver
                       spring.jpa.defer-datasource-initialization=true
                    

                    【讨论】:

                      猜你喜欢
                      • 2016-06-08
                      • 2016-08-07
                      • 2015-03-27
                      • 2022-11-21
                      • 2017-06-16
                      • 2022-11-18
                      • 2021-07-22
                      • 1970-01-01
                      • 2021-04-23
                      相关资源
                      最近更新 更多