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 нічого не знає про базу даних, яку ви створили на диску раніше.
Бенджамін Мушко

Решта програми не змінилася - я знову створив таблицю.
Джорн

Відповіді:


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.


Я сам виявив проблему тим часом, але так, це цілком правильно. Дякую!
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 - що слідує ';' схоже, викликає проблеми. Я думаю, що вам потрібно це залишити.
Фолксман

103

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

Вирішено шляхом додавання ;DATABASE_TO_UPPER=falseдо URL-адреси з'єднання.


7
Нічого собі - я дуже рада, що ви поділилися цим! ніколи б не подумав про це.
ms-tg

1
Не рішення запитання, яке було задано, а рішення проблеми, яка була у мене при пошуку з тим самим питанням!
Ятай

Чи можна встановити цю DATABASE_TO_UPPER=falseріч як оператор SQL у сценарії init? (Так само, як твердження, як SET MODE PostgreSQL;) Як це так, точний синтаксис?
Jonik

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
Я думаю, що я знайшов проблему. Коли я закриваю з'єднання, з якого створена таблиця, а потім відкриваю новий, db вже немає. Коли я відкриваю нове з'єднання перед тим, як закрити попереднє, дані залишаються. Коли я використовую файл, дані (очевидно) завжди залишаються.
Джорн

7

База даних пам'яті H2 зберігає дані в пам'яті всередині 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: \ Користувачі \ Luke \ речі> java CreateTable

C: \ Користувачі \ Luke \ речі> java InsertIntoTable
Виняток у потоці "main" org.h2.jdbc.JdbcSQLException: Таблиця "ОСОБА" не знайдена; Оператор SQL:
ВСТУПИТИ В ОСОБЛИВОСТІ (ІД, ПІДПРИЄМНЕ, ЛАСНІМЕ) ЦІННОСТІ (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 на класний шлях.
Йоахім Зауер

ДБ в пам'яті існує лише до тих пір, поки працює програма, яка володіє пам'яттю? Нічого собі, я поняття не мав> _ <Але насправді я знаю, що намагаюся зробити. Читаючи вашу відповідь, я не впевнений, що ви це зробите.
Джорн

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

Потім було видно таблиці


дякую, "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

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

Що здається очевидним, але я витратив години, розбираючись у цьому.


2

Вирішується, створивши нову папку src / test / ресурси + вставити файл application.properties, чітко вказавши для створення тестової бази даних:

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

1

Я прийшов на цю посаду, тому що у мене була така ж помилка.

У моєму випадку еволюції бази даних не виконувались, тому таблиці взагалі не було.

Моя проблема полягала в тому, що структура папок для сценаріїв еволюції була неправильною.

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

Відтворення відстежує еволюцію вашої бази даних, використовуючи кілька сценаріїв еволюції. Ці сценарії написані звичайним старим SQL і повинні розташовуватися в каталозі conf / evolutions / {name database} вашої програми. Якщо еволюція застосовується до бази даних за замовчуванням, цей шлях є 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 boot версії 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 у 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

Час.спля (1000);

Це робота для мене. Тільки для спробу, якщо ваш ПК повільний, ви можете збільшити тривалість потоку, щоб DB працював нормально, а JVM зможе отримати нашу таблицю.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.