H2 bellek içi veritabanı. Tablo bulunamadı


184

URL içeren bir H2 veritabanım var "jdbc:h2:test". Kullanarak tablo oluşturuyorum CREATE TABLE PERSON (ID INT PRIMARY KEY, FIRSTNAME VARCHAR(64), LASTNAME VARCHAR(64));. Sonra kullanarak bu (boş) tablodan her şeyi seçin SELECT * FROM PERSON. Çok uzak çok iyi.

Ancak, URL olarak değiştirirseniz, "jdbc:h2:mem:test"veritabanı olan tek fark şimdi sadece bellekte, bu bana bir verir org.h2.jdbc.JdbcSQLException: Table "PERSON" not found; SQL statement: SELECT * FROM PERSON [42102-154]. Muhtemelen burada basit bir şey eksik, ama herhangi bir yardım mutluluk duyacağız.


2
Bellek içi moda geçtikten sonra tabloyu Personyeniden oluşturmanız gerekir . H2, daha önce diskte oluşturduğunuz veritabanı hakkında hiçbir şey bilmiyor.
Benjamin Muschko

Programın geri kalanı değişmedi - tabloyu tekrar oluşturdum.
Jorn

Yanıtlar:


332

DB_CLOSE_DELAY=-1

hbm2ddl, tablo oluşturulduktan sonra bağlantıyı kapatır, böylece h2 atar.

Bağlantı URL'niz bu şekilde yapılandırıldıysa

jdbc:h2:mem:test

son bağlantı kapatıldığı anda veritabanının içeriği kaybolur.

İçeriğinizi korumak istiyorsanız, URL'yi böyle yapılandırmanız gerekir

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

Bunu yaparsanız, h2 içeriği vm yaşadığı sürece devam edecektir .

İki ;nokta üst üste ( :) yerine noktalı virgül ( ) öğesine dikkat edin .

Bkz In-Memory Veritabanları bölümüne Özellikleri sayfasında. Alıntılamak:

Varsayılan olarak, bir veritabanına son bağlantının kapatılması veritabanını kapatır. Bellek içi veritabanı için bu, içeriğin kaybolduğu anlamına gelir. Veritabanını açık tutmak için ;DB_CLOSE_DELAY=-1veritabanı URL'sine ekleyin. Sanal makine canlı olduğu sürece bellek içi veritabanının içeriğini korumak için kullanın jdbc:h2:mem:test;DB_CLOSE_DELAY=-1.


Bu arada problemi kendim buldum, ama evet, bu tamamen doğru. Teşekkürler!
Jorn

3
Ve adlandırılmış bir bellek içi veritabanı olmalıdır, yani jdbc:h2:mem:;DB_CLOSE_DELAY=-1çalışmaz.
Peter Becker

verileri bellek yerine dosyada nasıl saklayabiliriz?
Suleman khan

9
tablolarınızı bir kodda adlandırmak için küçük harf kullanırsanız, H2'nin büyük harfle varsayılan olarak her şeyi önlemek için DATABASE_TO_UPPER = false kullandığını bilmelisiniz, örneğin jdbc: h2: mem: test; DB_CLOSE_DELAY = -1; DATABASE_TO_UPPER = false;
Oleksandr Petrenko

@OleksandrPetrenko - bu sondaki ';' sorunlara yol açıyor gibi görünüyor. Bence bunu dışarıda bırakmalısın.
Volksman

104

Bu sizin durumunuz olmadığını biliyorum ama aynı sorun vardı çünkü H2 UPPERCASE isimleri ile tablolar oluşturmak ve sonra tüm senaryolarda (oluşturma olanlar dahil) küçük harf kullandığım rağmen, büyük / küçük harf duyarlı davranıyor.

;DATABASE_TO_UPPER=falseBağlantı URL'sine eklenerek çözüldü .


7
Vay be - Bunu paylaştığın için çok mutluyum! bunu asla düşünemezdim.
ms-tg

1
Sorulan sorunun çözümü değil, aynı soruyla arama yaparken yaşadığım sorunun çözümü!
Yaytay

Bu DATABASE_TO_UPPER=falseşeyi bir init betiğinde SQL deyimi olarak ayarlamak mümkün müdür ? (Benzer bir ifadeye benzer şekilde SET MODE PostgreSQL;) Öyleyse, tam sözdizimi nedir?
Jonik

3
Bunu birden fazla kez nasıl oylayabilirim? Çok teşekkürler! Bu ilk cevabın bir parçası olmalıdır.
Ribesg

11

Söylemesi zor. Bunu test etmek için bir program oluşturdum:

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

Test, hiçbir arıza ve beklenmeyen çıktı olmadan tamamlandı. Hangi H2 sürümünü kullanıyorsunuz?


Bunu yarın deneyeceğim, teşekkürler. H2 versiyonu bugün siteden çıktım: 1.3.154
Jorn

1
Sanırım problemi buldum. Tablonun oluşturulduğu bağlantıyı kapattığımda, yeni bir tane açtığımda, db gitti. Bir öncekini kapatmadan önce yeni bir bağlantı açtığımda veriler kalır. Bir dosya kullandığımda, veriler (açıkçası) her zaman kalır.
Jorn

7

H2 bellek içi veritabanı, verileri JVM içindeki bellekte saklar. JVM çıktığında, bu veriler kaybolur.

Yaptığınız şeyin aşağıdaki iki Java sınıfına benzer olduğundan şüpheleniyorum. Bu sınıflardan biri bir tablo oluşturur ve diğeri tabloya eklemeye çalışır:

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

ve

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

Bu sınıfları peş peşe koştuğumda şu çıktıyı aldım:

C: \ Kullanıcılar \ Luke \ stuff> java CreateTable

C: \ Kullanıcılar \ Luke \ stuff> java InsertIntoTable
"Main" iş parçacığındaki kural dışı durum org.h2.jdbc.JdbcSQLException: Tablo "PERSON" bulunamadı; SQL deyimi:
KİŞİ İÇERİN (INS, FIRSTNAME, LASTNAME) DEĞERLERİ (1, 'John', 'Doe') [42102-154]
        at org.h2.message.DbException.getJdbcSQLException (DbException.java:327)
        org.h2.message.DbException.get adresinde (DbException.java:167)
        org.h2.message.DbException.get (DbException.java:144) adresinde
        ...

İlk javaişlem biter bitmez , oluşturulan tablo CreateTableartık mevcut değildir. Bu nedenle, InsertIntoTable sınıfı geldiğinde, içine eklenecek bir tablo yoktur.

Bağlantı dizelerini değiştirdiğimde jdbc:h2:testböyle bir hata olmadığını gördüm. Ayrıca bir dosyanın test.h2.dbortaya çıktığını da buldum . H2, tabloyu koyduğu yerdi ve diskte depolandığından beri, InsertIntoTable sınıfının bulması için tablo hala oradaydı.


1
registerDriver()Çağrının gereksiz olduğunu lütfen unutmayın : Birincisi: basit bir Class.forName () çoğu JDBC sürücüsü için aynı şeyi yapar ve (daha da önemlisi), Java 6 und up için tamamen gereksizdir. sınıf yolu.
Joachim Sauer

Bir bellekte db yalnızca belleğin sahibi olan program çalışıyor olduğu sürece var mı? Vay be, hiçbir fikrim yoktu> _ <Ama gerçekten, ne yapmaya çalıştığımı biliyorum. Cevabınızı okurken, yaptığınızdan emin değilim.
Jorn

2
@Jorn: Ne yapmaya çalıştığınızı bilmiyor olabilirim, verdiğiniz bilgilere dayanarak tahmin ediyorum. Sorununuzu gösteren bir SSCCE ( sscce.org ) sağlamak daha yararlı olabilirdi - bu konuda sorunuzu 'tam' olarak adlandırmam. Yukarıdaki cevabı sağladım, çünkü SO'da (çoğunlukla programlamaya yeni başlayanlar), bir 'bellek içi' veritabanının, verileri bilgisayar belleğinde program çağrıları arasında hayatta kalabileceği bir yerde sakladığını düşünebilecek insanlar var. Sorunuz beni bu insanlardan biri olmadığınıza ikna edecek kadar eksiksiz değildi.
Luke Woodward

5

Eklemeye çalıştım

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

Ancak, bu yardımcı olmadı. On H2 bölgesine , ben gerçekten de bazı durumlarda yardımcı olabilecek aşağıdaki, bulduk.

Varsayılan olarak, bir veritabanına son bağlantının kapatılması veritabanını kapatır. Bellek içi veritabanı için bu, içeriğin kaybolduğu anlamına gelir. Veritabanını açık tutmak için veritabanı URL'sine; DB_CLOSE_DELAY = -1 ekleyin. Sanal makine canlı olduğu sürece bellek içi veritabanının içeriğini korumak için jdbc: h2: mem: test; DB_CLOSE_DELAY = -1 kullanın.

Ancak , benim sorunum sadece şemanın varsayılandan farklı olması gerekiyordu. Bu yüzden kullanmak istedim

JDBC URL: jdbc:h2:mem:test

Kullanmak zorunda kaldım:

JDBC URL: jdbc:h2:mem:testdb

Sonra tablolar göründü


teşekkürler, "testdb" benim için de bir düzeltme oldu ("DB_CLOSE_DELAY = -1" ek olarak)!
boly38

4

Ben de aynı sorunu vardı ve application-test.properties bu benim yapılandırma değiştirdi:

#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

Ve bağımlılıklarım:

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

Ve test sınıfında kullanılan ek açıklamalar:

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

3

Tablo meta verilerini almaya çalışıyordum, ancak aşağıdaki hatayı aldım:

Kullanımı:

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

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

boş bir ResultSet döndürdü.

Ancak aşağıdaki URL'yi kullanmak düzgün bir şekilde çalıştı:

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

Belirtmek gerekiyordu: DATABASE_TO_UPPER = false


Bu, bu yanıtın kapsamadığı hiçbir şey eklemez . Yorumdan .
Wai Ha Lee

3

H2-konsolunu açarken, JDBC URL'si özelliklerde belirtilen URL ile eşleşmelidir:

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

resim açıklamasını buraya girin

Bu çok açık görünüyor, ama bunu anlamak için saatler geçirdim.


2

Yeni bir src / test / resources klasörü + insert application.properties dosyası oluşturarak, açıkça bir test dbase oluşturmayı belirterek çözüldü:

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

1

Aynı hatayla karşılaştığım için bu gönderiye geldim.

Benim durumumda veritabanı gelişmeleri yürütülmedi, bu yüzden tablo hiç yoktu.

Benim sorunum evrim komut dosyalarının klasör yapısının yanlış olmasıydı.

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

Play, çeşitli evrim komut dosyalarını kullanarak veritabanı evriminizi izler. Bu komut dosyaları eski SQL'de yazılmıştır ve uygulamanızın conf / evolutions / {database name} dizininde bulunmalıdır. Gelişmeler varsayılan veritabanınız için geçerliyse, bu yol conf / evolutions / default şeklindedir.

Eclipse tarafından oluşturulan conf / evolutions.default adlı bir klasörüm vardı . Klasör yapısını conf / evolutions / default olarak düzelttikten sonra sorun kayboldu


1

Aynı sorunu vardı, yukarıdakilerin tümünü denedim, ancak başarılı olamadı. Hatanın oldukça komik nedeni, JVM'nin DB tablosu oluşturulmadan önce çok hızlı başlamasıydı (src.main.resources içinde bir data.sql dosyası kullanarak). Bu yüzden, "kişiden * seç" i çağırmadan önce sadece bir saniye beklemek için bir Thread.sleep (1000) zamanlayıcı koydum. Şimdi kusursuz çalışıyor.

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

ana sınıf:

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

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

Spring Data JPA'nın Spring boot sürüm 2.2.6'ya bağımlılığını ekledikten sonra çalıştığını gördüm.

    <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 dosyasına H2 DB yapılandırması ekleyin -

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

Bu yapılandırmayı ekleyerek çözümü buldum:

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

Tam yapılandırma (basit yaylı veri.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

Ben ile çalışıyorum h2 sürümü 1.4.200 ve İlkbahar-Boot 2.2.6.RELEASE


-2

Time.sleep (1000);

Bu benim için bir iş. Sadece denemek için, PC'niz yavaşsa, DB'nin iyi çalışabilmesi ve JVM'nin masamızı alabilmesi için iş parçacığının süresini uzatabilirsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.