Cơ sở dữ liệu trong bộ nhớ H2. Không tìm thấy bảng


183

Tôi đã có một cơ sở dữ liệu H2 với URL "jdbc:h2:test". Tôi tạo một bảng bằng cách sử dụng CREATE TABLE PERSON (ID INT PRIMARY KEY, FIRSTNAME VARCHAR(64), LASTNAME VARCHAR(64));. Sau đó tôi chọn mọi thứ từ bảng này (trống) bằng cách sử dụng SELECT * FROM PERSON. Càng xa càng tốt.

Tuy nhiên, nếu tôi thay đổi URL thành "jdbc:h2:mem:test", sự khác biệt duy nhất là cơ sở dữ liệu hiện chỉ có trong bộ nhớ, điều này mang lại cho tôi một org.h2.jdbc.JdbcSQLException: Table "PERSON" not found; SQL statement: SELECT * FROM PERSON [42102-154]. Tôi có thể thiếu một cái gì đó đơn giản ở đây, nhưng bất kỳ trợ giúp sẽ được đánh giá cao.


2
Sau khi chuyển sang chế độ trong bộ nhớ, bạn phải tạo lại bảng Person. H2 không biết gì về cơ sở dữ liệu bạn đã tạo trên đĩa trước đó.
Benjamin Muschko

Phần còn lại của chương trình không thay đổi - tôi đã tạo lại bảng.
Jorn

Câu trả lời:


331

DB_CLOSE_DELAY=-1

hbm2ddl đóng kết nối sau khi tạo bảng, vì vậy h2 loại bỏ nó.

Nếu bạn có cấu hình url kết nối như thế này

jdbc:h2:mem:test

nội dung của cơ sở dữ liệu bị mất tại thời điểm kết nối cuối cùng được đóng lại.

Nếu bạn muốn giữ nội dung của mình, bạn phải định cấu hình url như thế này

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

Nếu làm như vậy, h2 sẽ giữ nội dung của nó miễn là vm còn sống.

Lưu ý dấu chấm phẩy ( ;) chứ không phải dấu hai chấm ( :).

Xem phần Cơ sở dữ liệu trong bộ nhớ của trang Tính năng . Để trích:

Theo mặc định, đóng kết nối cuối cùng với cơ sở dữ liệu sẽ đóng cơ sở dữ liệu. Đối với cơ sở dữ liệu trong bộ nhớ, điều này có nghĩa là nội dung bị mất. Để giữ cơ sở dữ liệu mở, thêm ;DB_CLOSE_DELAY=-1vào URL cơ sở dữ liệu. Để giữ nội dung của cơ sở dữ liệu trong bộ nhớ miễn là máy ảo còn sống, hãy sử dụng jdbc:h2:mem:test;DB_CLOSE_DELAY=-1.


Tôi đã tìm thấy vấn đề bản thân trong thời gian đó, nhưng vâng, điều này là hoàn toàn chính xác. Cảm ơn!
Jorn

3
Và nó phải là một cơ sở dữ liệu trong bộ nhớ, có nghĩa là jdbc:h2:mem:;DB_CLOSE_DELAY=-1không hoạt động.
Peter Becker

Làm thế nào chúng ta có thể lưu trữ dữ liệu trong tập tin thay vì bộ nhớ?
Suleman khan

9
nếu bạn sử dụng chữ thường để đặt tên cho các bảng của mình theo mã, bạn nên biết rằng H2 viết hoa mọi thứ theo mặc định, sử dụng DATABASE_TO_UPPER = false để tránh nó, ví dụ: jdbc: h2: mem: test; DB_CLOSE_DELAY = -1; DATABASE_TO_UPPER = false;
Oleksandr Petrenko

@OleksandrPetrenko - dấu vết đó ';' dường như gây ra vấn đề Tôi nghĩ bạn cần phải bỏ nó ra.
Volksman

104

Tôi biết đây không phải là trường hợp của bạn nhưng tôi gặp vấn đề tương tự vì H2 đang tạo các bảng có tên UPPERCASE sau đó xử lý phân biệt chữ hoa chữ thường, mặc dù trong tất cả các tập lệnh (kể cả trong các tập lệnh) tôi đã sử dụng chữ thường.

Giải quyết bằng cách thêm ;DATABASE_TO_UPPER=falsevào URL kết nối.


7
Wow - Tôi rất vui vì bạn đã chia sẻ cái này! không bao giờ có thể nghĩ về điều đó.
ms-tg

1
Không phải là giải pháp cho câu hỏi đã được hỏi, mà là giải pháp cho vấn đề tôi gặp phải khi tìm kiếm với cùng một câu hỏi!
Yaytay

Có thể đặt điều này DATABASE_TO_UPPER=falsenhư một câu lệnh SQL trong tập lệnh init không? (Tương tự như một tuyên bố như SET MODE PostgreSQL;) Nếu vậy, cú pháp chính xác là gì?
Jonik

3
Làm thế nào tôi có thể upvote này nhiều lần? Cảm ơn rất nhiều! Đây phải là một phần của câu trả lời đầu tiên.
Ribesg

11

Khó nói. Tôi đã tạo một chương trình để kiểm tra điều này:

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

Thử nghiệm đã chạy đến khi hoàn thành, không có lỗi và không có đầu ra bất ngờ. Phiên bản nào của h2 bạn đang chạy?


Tôi sẽ thử vào ngày mai, cảm ơn. Phiên bản H2 là phiên bản tôi đã rời khỏi trang web ngày hôm nay: 1.3.154
Jorn

1
Tôi nghĩ rằng tôi đã tìm thấy vấn đề. Khi tôi đóng kết nối, bảng đã được tạo, sau đó mở một kết nối mới, db sẽ biến mất. Khi tôi mở một kết nối mới trước khi tôi đóng kết nối trước đó, dữ liệu vẫn còn. Khi tôi sử dụng một tập tin, dữ liệu (rõ ràng) luôn luôn còn.
Jorn

7

Cơ sở dữ liệu trong bộ nhớ H2 lưu trữ dữ liệu trong bộ nhớ bên trong JVM. Khi JVM thoát, dữ liệu này sẽ bị mất.

Tôi nghi ngờ rằng những gì bạn đang làm tương tự như hai lớp Java bên dưới. Một trong các lớp này tạo một bảng và lớp kia cố gắng chèn vào nó:

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

Khi tôi chạy các lớp này lần lượt, tôi đã nhận được kết quả sau:

C: \ Users \ Luke \ Stuff> java CreatTable

C: \ Users \ Luke \ Stuff> java ChènIntoTable
Ngoại lệ trong luồng "chính" org.h2.jdbc.JdbcSQLException: Không tìm thấy bảng "PERSON"; Câu lệnh sql:
XÁC NHẬN VÀO CÁ NHÂN (ID, FIRSTNAME, LASTNAME) GIÁ TRỊ (1, 'John', 'Doe') [42102-154]
        tại org.h2.message.DbException.getJdbcSQLException (DbException.java:327)
        tại org.h2.message.DbException.get (DbException.java:167)
        tại org.h2.message.DbException.get (DbException.java:144)
        ...

Ngay khi javaquá trình đầu tiên thoát ra, bảng được tạo bởi CreateTablekhông còn tồn tại. Vì vậy, khi lớp insertIntoTable xuất hiện, không có bảng nào để chèn vào.

Khi tôi thay đổi chuỗi kết nối thành jdbc:h2:test, tôi thấy rằng không có lỗi như vậy. Tôi cũng thấy rằng một tập tin test.h2.dbđã xuất hiện. Đây là nơi H2 đã đặt bảng và vì nó đã được lưu trên đĩa, nên bảng vẫn ở đó để tìm lớp ChènIntoTable.


1
Xin lưu ý rằng registerDriver()cuộc gọi là không cần thiết: Đầu tiên: Class.forName () đơn giản thực hiện tương tự đối với hầu hết các trình điều khiển JDBC (quan trọng hơn) nó hoàn toàn không cần thiết đối với Java 6, nó tự động phát hiện các trình điều khiển JDBC (tương thích) trên lớp học
Joachim Sauer

Một db trong bộ nhớ chỉ tồn tại chừng nào chương trình sở hữu bộ nhớ đang chạy? Wow, tôi không có ý tưởng> _ <Nhưng thực sự, tôi biết những gì tôi đang cố gắng làm. Đọc câu trả lời của bạn, tôi không chắc bạn làm được.
Jorn

2
@Jorn: Tôi có thể không biết bạn đang cố gắng làm gì, tôi đoán dựa trên thông tin bạn cung cấp. Có thể hữu ích hơn khi cung cấp SSCCE ( sscce.org ) thể hiện vấn đề của bạn - Tôi sẽ không gọi câu hỏi của bạn là 'hoàn thành' trong khía cạnh đó. Tôi đã cung cấp câu trả lời ở trên bởi vì có những người trên SO (người mới tham gia lập trình, chủ yếu) có thể nghĩ rằng cơ sở dữ liệu 'trong bộ nhớ' lưu trữ dữ liệu trong bộ nhớ của máy tính ở đâu đó có thể tồn tại giữa các lần gọi chương trình. Câu hỏi của bạn chưa đủ để thuyết phục tôi rằng bạn không phải là một trong những người này.
Luke Woodward

5

Tôi đã cố gắng thêm

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

Tuy nhiên, điều đó không giúp được gì. Trên trang H2 , tôi đã tìm thấy sau đây, điều này thực sự có thể giúp ích trong một số trường hợp.

Theo mặc định, đóng kết nối cuối cùng với cơ sở dữ liệu sẽ đóng cơ sở dữ liệu. Đối với cơ sở dữ liệu trong bộ nhớ, điều này có nghĩa là nội dung bị mất. Để giữ cơ sở dữ liệu mở, hãy thêm; DB_CLOSE_DELAY = -1 vào URL cơ sở dữ liệu. Để giữ nội dung của cơ sở dữ liệu trong bộ nhớ miễn là máy ảo còn sống, hãy sử dụng jdbc: h2: mem: test; DB_CLOSE_DELAY = -1.

Tuy nhiên , vấn đề của tôi là chỉ lược đồ được cho là khác với lược đồ mặc định. Vì vậy, sử dụng

JDBC URL: jdbc:h2:mem:test

Tôi đã phải sử dụng:

JDBC URL: jdbc:h2:mem:testdb

Sau đó, các bảng được nhìn thấy


cảm ơn, "testdb" cũng là một bản sửa lỗi cho tôi (ngoài "DB_CLOSE_DELAY = -1")!
boly38

4

Tôi đã có cùng một vấn đề và thay đổi cấu hình của tôi trong application-test.properations thành điều này:

#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

Và phụ thuộc của tôi:

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

Và các chú thích được sử dụng trên lớp kiểm tra:

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

3

Tôi đã cố gắng tìm nạp dữ liệu meta bảng, nhưng đã gặp lỗi sau:

Sử dụng:

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

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

trả về một Kết quả trống.

Nhưng sử dụng URL sau thay vì nó hoạt động đúng:

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

Cần phải chỉ định: DATABASE_TO_UPPER = false


Điều này không thêm bất cứ điều gì không được bao phủ bởi câu trả lời này . Từ đánh giá .
Wai Ha Lee

3

Khi mở bảng điều khiển h2, URL JDBC phải khớp với URL được chỉ định trong các thuộc tính:

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

nhập mô tả hình ảnh ở đây

Điều này có vẻ hiển nhiên, nhưng tôi đã dành hàng giờ để tìm ra điều này ..


2

Được giải quyết bằng cách tạo thư mục src / test / resource mới + chèn tệp application.properIES, chỉ định rõ ràng để tạo dbase thử nghiệm:

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

1

Tôi đến bài viết này vì tôi có cùng một lỗi.

Trong trường hợp của tôi, các phát triển cơ sở dữ liệu không được thực thi, do đó bảng không có ở đó.

Vấn đề của tôi là cấu trúc thư mục cho các kịch bản tiến hóa đã sai.

từ: https://www.playframework.com/documentation/2.0/Evolutions

Chơi theo dõi diễn biến cơ sở dữ liệu của bạn bằng cách sử dụng một số tập lệnh tiến hóa. Các tập lệnh này được viết bằng SQL cũ đơn giản và nên được đặt trong thư mục conf / evolutions / {tên cơ sở dữ liệu} của ứng dụng của bạn. Nếu các tiến hóa áp dụng cho cơ sở dữ liệu mặc định của bạn, đường dẫn này là conf / evolutions / default.

Tôi đã có một thư mục gọi là conf / evolutions.default được tạo bởi nhật thực. Sự cố đã biến mất sau khi tôi sửa cấu trúc thư mục thành 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

Có cùng một vấn đề, đã thử tất cả các cách trên, nhưng không thành công. Nguyên nhân khá buồn cười của lỗi là JVM khởi động quá nhanh, trước khi bảng DB được tạo (sử dụng tệp data.sql trong src.main.resource). Vì vậy, tôi đã đặt bộ hẹn giờ Thread.s ngủ (1000) để chờ trong giây lát trước khi gọi "select * from person". Làm việc hoàn hảo bây giờ.

ứng dụng. sản phẩm:

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=

dữ liệu

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

lớp chính:

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

0

Tôi thấy nó hoạt động sau khi thêm sự phụ thuộc của Spring Data JPA vào phiên bản khởi động 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>

Thêm cấu hình H2 DB trong 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

Tôi tìm thấy giải pháp bằng cách thêm cấu hình này:

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

Cấu hình đầy đủ (với spring.datasource.url đơn giản):

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

Tôi đang làm việc với phiên bản h2 1.4.200Spring-Boot 2.2.6.RELEASE


-2

Thời gian ngủ (1000);

Đây là công việc cho tôi. Chỉ cần thử, nếu PC của bạn chậm, bạn có thể tăng thời lượng của luồng để DB hoạt động tốt và JVM có thể lấy bảng của chúng tôi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.