Basis data H2 dalam memori. Tabel tidak ditemukan

183

Saya punya database H2 dengan URL "jdbc:h2:test". Saya membuat tabel menggunakan CREATE TABLE PERSON (ID INT PRIMARY KEY, FIRSTNAME VARCHAR(64), LASTNAME VARCHAR(64));. Saya kemudian memilih semuanya dari tabel (kosong) ini menggunakan SELECT * FROM PERSON. Sejauh ini baik.

Namun, jika saya mengubah URL ke "jdbc:h2:mem:test", satu-satunya perbedaan adalah database sekarang hanya dalam memori, ini memberi saya org.h2.jdbc.JdbcSQLException: Table "PERSON" not found; SQL statement: SELECT * FROM PERSON [42102-154]. Saya mungkin melewatkan sesuatu yang sederhana di sini, tetapi bantuan apa pun akan dihargai.

Jorn
sumber
2
Setelah beralih ke mode dalam memori Anda harus membuat tabel Personlagi. H2 tidak tahu apa-apa tentang database yang Anda buat di disk sebelumnya.
Benjamin Muschko
Sisa program tidak berubah - saya membuat tabel lagi.
Jorn

Jawaban:

331

DB_CLOSE_DELAY=-1

hbm2ddl menutup koneksi setelah membuat tabel, jadi h2 membuangnya.

Jika Anda memiliki koneksi-url Anda dikonfigurasi seperti ini

jdbc:h2:mem:test

konten basis data terputus pada saat koneksi terakhir ditutup.

Jika Anda ingin menyimpan konten Anda, Anda harus mengkonfigurasi url seperti ini

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

Jika demikian, h2 akan menyimpan kontennya selama vm hidup.

Perhatikan titik koma ( ;) daripada titik dua ( :).

Lihat bagian In-Memory Databases di halaman Features . Kutipan:

Secara default, menutup koneksi terakhir ke database menutup database. Untuk basis data dalam memori, ini berarti kontennya hilang. Agar database tetap terbuka, tambahkan ;DB_CLOSE_DELAY=-1ke URL basis data. Untuk menjaga konten database di-memori selama mesin virtual masih hidup, gunakan jdbc:h2:mem:test;DB_CLOSE_DELAY=-1.

reini2901
sumber
Saya menemukan masalahnya sendiri dalam waktu yang bersamaan, tapi ya, ini sepenuhnya benar. Terima kasih!
Jorn
3
Dan itu harus menjadi basis data dalam memori, yaitu jdbc:h2:mem:;DB_CLOSE_DELAY=-1tidak berfungsi.
Peter Becker
bagaimana kita bisa menyimpan data dalam file alih-alih memori?
Suleman khan
9
jika Anda menggunakan huruf kecil untuk penamaan tabel Anda dalam kode Anda harus tahu bahwa H2 huruf besar semuanya secara default menggunakan DATABASE_TO_UPPER = false untuk menghindarinya misalnya jdbc: h2: mem: test; DB_CLOSE_DELAY = -1; DATABASE_TO_UPPER = false;
Oleksandr Petrenko
@OleksandrPetrenko - yang mengikuti ';' tampaknya menyebabkan masalah. Saya pikir Anda perlu meninggalkan itu.
Volksman
104

Saya tahu ini bukan kasus Anda, tetapi saya memiliki masalah yang sama karena H2 membuat tabel dengan nama UPPERCASE kemudian berperilaku case-sensitive, meskipun dalam semua skrip (termasuk dalam skrip penciptaan) saya menggunakan huruf kecil.

Dipecahkan dengan menambahkan ;DATABASE_TO_UPPER=falseke URL koneksi.

Cristian Vrabie
sumber
7
Wow - Saya sangat senang Anda membagikan yang ini! tidak akan pernah memikirkan itu.
ms-tg
1
Bukan solusi untuk pertanyaan yang diajukan, tetapi solusi untuk masalah yang saya alami ketika mencari dengan pertanyaan yang sama!
Yaytay
Apakah mungkin untuk mengatur DATABASE_TO_UPPER=falsehal ini sebagai pernyataan SQL dalam skrip init? (Sama seperti pernyataan seperti SET MODE PostgreSQL;) Jika demikian, apa sintaks yang tepat?
Jonik
3
Bagaimana saya bisa memperbaiki ini beberapa kali? Terima kasih banyak! Ini harus menjadi bagian dari jawaban pertama.
Ribesg
11

Sulit dikatakan. Saya membuat program untuk menguji ini:

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

Tes berjalan sampai selesai, tanpa kegagalan dan tanpa hasil yang tidak terduga. Versi h2 mana yang Anda jalankan?

Joseph Ottinger
sumber
Saya akan coba ini besok, terima kasih. Versi H2 adalah yang saya dapatkan dari situs hari ini: 1.3.154
Jorn
1
Saya pikir saya menemukan masalahnya. Ketika saya menutup koneksi, tabel itu dibuat, lalu buka yang baru, db hilang. Ketika saya membuka koneksi baru sebelum saya menutup yang sebelumnya, data tetap ada. Ketika saya menggunakan file, data (jelas) selalu tetap.
Jorn
7

Database in-memory H2 menyimpan data dalam memori di dalam JVM. Ketika JVM keluar, data ini hilang.

Saya menduga bahwa apa yang Anda lakukan mirip dengan dua kelas Java di bawah ini. Salah satu dari kelas-kelas ini membuat tabel dan yang lain mencoba untuk memasukkannya:

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

dan

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

Ketika saya menjalankan kelas-kelas ini satu demi satu, saya mendapat hasil sebagai berikut:

C: \ Users \ Luke \ stuff> java CreateTable

C: \ Users \ Luke \ stuff> java InsertIntoTable
Pengecualian di utas "utama" org.h2.jdbc.JdbcSQLException: Tabel "PERSON" tidak ditemukan; Pernyataan SQL:
MASUKKAN ORANG (ID, FIRSTNAME, LASTNAME) NILAI (1, 'John', 'Doe') [42102-154]
        di org.h2.message.DbException.getJdbcSQLException (DbException.java:327)
        di org.h2.message.DbException.get (DbException.java:167)
        di org.h2.message.DbException.get (DbException.java:144)
        ...

Segera setelah javaproses pertama keluar, tabel yang dibuat oleh CreateTabletidak ada lagi. Jadi, ketika kelas InsertIntoTable datang, tidak ada tabel untuk dimasukkan.

Ketika saya mengubah string koneksi ke jdbc:h2:test, saya menemukan bahwa tidak ada kesalahan seperti itu. Saya juga menemukan bahwa file test.h2.dbtelah muncul. Di sinilah H2 meletakkan tabel, dan karena itu telah disimpan di disk, tabel itu masih ada untuk ditemukan kelas InsertIntoTable.

Luke Woodward
sumber
1
Harap dicatat bahwa registerDriver()panggilan itu tidak perlu: Pertama: Class.forName () sederhana melakukan hal yang sama untuk sebagian besar driver JDBC dan (lebih penting) itu sama sekali tidak perlu untuk Java 6 und up, yang mendeteksi driver JDBC otomatis (kompatibel) pada jalan setapak kelas.
Joachim Sauer
Sebuah memori db hanya ada selama program yang memiliki memori berjalan? Wow, saya tidak tahu> _ <Tapi sungguh, saya tahu apa yang saya coba lakukan. Membaca jawaban Anda, saya tidak yakin Anda tahu.
Jorn
2
@Jorn: Saya mungkin tidak tahu apa yang Anda coba lakukan, saya menduga berdasarkan informasi apa yang Anda berikan. Mungkin lebih bermanfaat untuk menyediakan SSCCE ( sscce.org ) yang menunjukkan masalah Anda - saya tidak akan menyebut pertanyaan Anda 'lengkap' dalam hal itu. Saya memberikan jawaban di atas karena ada orang-orang di SO (pendatang baru untuk pemrograman, terutama) yang mungkin berpikir bahwa database 'in-memory' menyimpan data dalam memori komputer di suatu tempat di mana ia bisa bertahan di antara permintaan program. Pertanyaan Anda tidak cukup lengkap untuk meyakinkan saya bahwa Anda bukan salah satu dari orang-orang ini.
Luke Woodward
5

Saya sudah mencoba menambahkan

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

Namun, itu tidak membantu. Di situs H2 , saya telah menemukan yang berikut, yang memang bisa membantu dalam beberapa kasus.

Secara default, menutup koneksi terakhir ke database menutup database. Untuk basis data dalam memori, ini berarti kontennya hilang. Agar database tetap terbuka, tambahkan; DB_CLOSE_DELAY = -1 ke URL basis data. Untuk menjaga konten database di-memori selama mesin virtual masih hidup, gunakan jdbc: h2: mem: test; DB_CLOSE_DELAY = -1.

Namun , masalah saya adalah bahwa hanya skema yang seharusnya berbeda dari yang standar. Jadi insted menggunakan

JDBC URL: jdbc:h2:mem:test

Saya harus menggunakan:

JDBC URL: jdbc:h2:mem:testdb

Kemudian tabel terlihat

DevDio
sumber
terima kasih, "testdb" juga merupakan perbaikan untuk saya (selain "DB_CLOSE_DELAY = -1")!
boly38
4

Saya memiliki masalah yang sama dan mengubah konfigurasi saya di application-test.properties menjadi ini:

#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

Dan ketergantungan saya:

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

Dan penjelasan yang digunakan pada kelas tes:

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

Saya mencoba mengambil data meta tabel, tetapi mengalami kesalahan berikut:

Menggunakan:

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

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

mengembalikan ResultSet kosong.

Tetapi menggunakan URL berikut ini malah berfungsi dengan baik:

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

Ada kebutuhan untuk menentukan: DATABASE_TO_UPPER = false

Miguel Angel Vega Pabon
sumber
Ini tidak menambahkan apa pun yang tidak dicakup oleh jawaban ini . Dari Ulasan .
Wai Ha Lee
3

Saat membuka konsol h2, URL JDBC harus cocok dengan yang ditentukan di properti:

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

masukkan deskripsi gambar di sini

Yang tampaknya jelas, tapi saya menghabiskan berjam-jam mencari tahu ini ..

nagy.zsolt.hun
sumber
2

Dipecahkan dengan membuat folder src / test / resources baru + menyisipkan file application.properties, secara eksplisit menentukan untuk membuat test dbase:

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

Saya datang ke pos ini karena saya memiliki kesalahan yang sama.

Dalam kasus saya, evolusi basis data tidak dijalankan, jadi tabelnya tidak ada sama sekali.

Masalah saya adalah bahwa struktur folder untuk skrip evolusi salah.

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

Mainkan trek evolusi basis data Anda menggunakan beberapa skrip evolusi. Skrip-skrip ini ditulis dalam SQL lama dan harus diletakkan di direktori conf / evolutions / {database name} aplikasi Anda. Jika evolusi berlaku untuk database default Anda, jalur ini adalah conf / evolusi / default.

Saya memiliki folder bernama conf / evolutions.default yang dibuat oleh eclipse. Masalahnya hilang setelah saya memperbaiki struktur folder menjadi conf / evolutions / default

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

Punya masalah yang sama persis, mencoba semua di atas, tetapi tidak berhasil. Penyebab kesalahan yang agak lucu adalah bahwa JVM mulai terlalu cepat, sebelum tabel DB dibuat (menggunakan file data.sql di src.main.resources). Jadi saya telah meletakkan timer Thread.sleep (1000) untuk menunggu sebentar sebelum memanggil "select * from person". Bekerja dengan sempurna sekarang.

properti aplikasi:

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

kelas utama:

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

Saya menemukan itu berfungsi setelah menambahkan ketergantungan Spring Data JPA pada Spring boot versi 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>

Tambahkan konfigurasi H2 DB di 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
sumber
0

Saya menemukan solusinya dengan menambahkan konfigurasi ini:

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

Konfigurasi lengkap (dengan spring.datasource.url sederhana):

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

Saya bekerja dengan h2 versi 1.4.200 dan Spring-Boot 2.2.6.RELEASE

Elang
sumber
-2

Time.sleep (1000);

Ini bekerja untuk saya. Hanya untuk dicoba, jika PC Anda lambat, Anda dapat menambah durasi waktu utas sehingga DB berfungsi dengan baik dan JVM dapat memperoleh tabel kami.

Dhara N.
sumber