База данных H2 в памяти. Таблица не найдена

183

У меня есть база данных H2 с URL "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 ничего не знает о базе данных, которую вы создали на диске раньше.
Бенджамин Мущко
Остальная часть программы не изменилась - я снова создал таблицу.
Jorn

Ответы:

331

DB_CLOSE_DELAY=-1

hbm2ddl закрывает соединение после создания таблицы, поэтому h2 отбрасывает его.

Если ваш URL-адрес подключения настроен следующим образом

jdbc:h2:mem:test

содержимое базы данных теряется в момент закрытия последнего соединения.

Если вы хотите сохранить свой контент, вы должны настроить URL следующим образом

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

В этом случае h2 сохранит свое содержимое до тех пор, пока vm живет.

Обратите внимание на точку с запятой ( ;), а не на двоеточие ( :).

См. Раздел « Базы данных в памяти » на странице « Функции» . Цитировать:

По умолчанию закрытие последнего соединения с базой данных закрывает базу данных. Для базы данных в памяти это означает, что содержимое потеряно. Чтобы сохранить базу данных открытой, добавьте ;DB_CLOSE_DELAY=-1в базу данных URL. Чтобы сохранить содержимое базы данных в памяти, пока виртуальная машина жива, используйте jdbc:h2:mem:test;DB_CLOSE_DELAY=-1.

reini2901
источник
Тем временем я сам нашел проблему, но да, это совершенно правильно. Спасибо!
Jorn
3
И это должна быть именованная база данных в памяти, т.е. jdbc:h2:mem:;DB_CLOSE_DELAY=-1не работает.
Питер Беккер
как мы можем хранить данные в файле вместо памяти?
Сулеман хан
9
если вы используете нижний регистр для именования таблиц в коде, вы должны знать, что по умолчанию все заглавные буквы H2 по умолчанию используют DATABASE_TO_UPPER = false, чтобы избежать этого, например, jdbc: h2: mem: test; DB_CLOSE_DELAY = -1; DATABASE_TO_UPPER = false;
Александр Петренко
@OleksandrPetrenko - этот трейлинг ';' кажется, вызывает проблемы. Я думаю, тебе нужно это оставить.
Volksman
104

Я знаю, что это был не ваш случай, но у меня была та же проблема, потому что H2 создавал таблицы с именами UPPERCASE, а затем вел себя с учетом регистра, хотя во всех сценариях (в том числе в сценариях создания) я использовал строчные буквы.

Решается добавлением ;DATABASE_TO_UPPER=falseк URL-адресу подключения.

Кристиан Враби
источник
7
Вау, я очень рада, что вы поделились этим! никогда бы не подумал об этом.
MS-TG
1
Не решение вопроса, который был задан, а решение проблемы, с которой я столкнулся при поиске по тому же вопросу!
Yaytay
Можно ли установить эту DATABASE_TO_UPPER=falseвещь как оператор SQL в сценарии инициализации? (Точно так же, как утверждение типа SET MODE PostgreSQL;) Если так, какой точный синтаксис?
Джоник
3
Как я могу объявить это несколько раз? Большое спасибо! Это должно быть частью первого ответа.
Рибесг
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
Я думаю, что нашел проблему. Когда я закрываю соединение, с которым была создана таблица, затем открываю новое, БД исчезла. Когда я открываю новое соединение перед тем, как закрыть предыдущее, данные остаются. Когда я использую файл, данные (очевидно) всегда остаются.
Jorn
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
Исключение в потоке "main" org.h2.jdbc.JdbcSQLException: таблица "PERSON" не найдена; Оператор SQL:
ВСТАВИТЬ В ЧЕЛОВЕКА (ID, ИМЯ, ФАМИЛИЯ) ЦЕННОСТИ (1, «Джон», «Доу») [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 и выше, который автоматически определяет (совместимые) драйверы JDBC на CLASSPATH.
Йоахим Зауэр
БД в памяти существует только до тех пор, пока работает программа, которой принадлежит память? Ух ты, я понятия не имел> _ <Но на самом деле я знаю, что я пытаюсь сделать. Читая ваш ответ, я не уверен, что вы делаете.
Jorn
2
@Jorn: Я не знаю, что вы пытаетесь сделать, я думаю, основываясь на том, какую информацию вы предоставили. Возможно, было бы более полезно предоставить SSCCE ( sscce.org ), демонстрирующий вашу проблему - я бы не назвал ваш вопрос «завершенным» в этом отношении. Я дал приведенный выше ответ, потому что в SO есть люди (в основном новички в программировании), которые могут подумать, что база данных «в памяти» хранит данные в памяти компьютера где-то там, где они могут выжить между вызовами программы. Ваш вопрос не был достаточно полным, чтобы убедить меня, что вы не один из этих людей.
Люк Вудворд
5

Я пытался добавить

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

Однако это не помогло. На сайте H2 я нашел следующее, что действительно может помочь в некоторых случаях.

По умолчанию закрытие последнего соединения с базой данных закрывает базу данных. Для базы данных в памяти это означает, что содержимое потеряно. Чтобы оставить базу данных открытой, добавьте; 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

Тогда таблицы были видны

DevDio
источник
спасибо, "testdb" был исправлен и для меня (в дополнение к "DB_CLOSE_DELAY = -1")!
boly38
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-консоли URL JDBC должен соответствовать указанному в свойствах:

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

введите описание изображения здесь

Что кажется очевидным, но я потратил часы, чтобы понять это ..

nagy.zsolt.hun
источник
2

Решается путем создания новой папки src / test / resources + insert application.properties, в которой явно указывается создание тестовой базы данных:

spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=create
N.MATHIEU
источник
1

Я пришел на этот пост, потому что у меня была такая же ошибка.

В моем случае эволюция базы данных не была выполнена, поэтому таблицы там вообще не было.

Моя проблема заключалась в том, что структура папок для сценариев эволюции была неправильной.

от: https://www.playframework.com/documentation/2.0/Evolutions

Воспроизведение отслеживает эволюции вашей базы данных, используя несколько сценариев эволюции Эти сценарии написаны на простом старом SQL и должны находиться в каталоге conf / evolutions / {database name} вашего приложения. Если эволюции применяются к вашей базе данных по умолчанию, этот путь будет conf / evolutions / default.

У меня была папка с именем conf / evolutions.default, созданная Eclipse. Проблема исчезла после того, как я исправил структуру папок в 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

Имел точно такую ​​же проблему, пробовал все вышеописанное, но безуспешно. Довольно забавной причиной ошибки было то, что JVM запускалась слишком быстро, до того, как была создана таблица БД (с использованием файла data.sql в src.main.resources). Поэтому я установил таймер Thread.sleep (1000), чтобы подождать всего секунду, прежде чем вызвать «select * from person». Работаем без нареканий сейчас.

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 Data JPA от загрузочной версии Spring 2.2.6.RELEASE -

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

Добавьте конфигурацию H2 DB в 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

Я нашел решение, добавив эту конфигурацию:

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.200 и Spring-Boot 2.2.6.RELEASE

сокол
источник
-2

Time.sleep (1000);

Это работа для меня. Просто попробуйте, если ваш компьютер работает медленно, вы можете увеличить продолжительность потока, чтобы БД работала нормально, а JVM могла получить нашу таблицу.

Дхара Н.
источник