1、概览 {#1概览}
H2 是一个开源的 SQL 数据库,在 Java 中通常用于测试。它是一个内存数据库,不会将任何数据持久化到磁盘,因此速度非常快。
在与 Spring Boot 整合时,我们可能会遇到 "Schema not found" 异常,本文将带你了解出现此异常的原因,以及如何解决该异常。
2、理解异常的原因 {#2理解异常的原因}
H2 的默认 Schema 是 PUBLIC 。如果我们映射的 JPA 实体类不使用 PUBLIC Schema,则必须确保在 H2 上创建了 Schema。当目标 Schema 不存在时,Spring Boot 会抛出异常 "Schema not found"。
模拟一下这个场景。在 Spring Boot 应用中创建以下实体类和 Repository。
@Entity
@Table(name = "student", schema = "test")
public class Student {
@Id
@Column(name = "student_id", length = 10)
private String studentId;
@Column(name = "name", length = 100)
private String name;
// 构造函数、Getter\Setter 方法
}
如上 @Table
注解指定了实体映射到 test Schema 下的 student 表的映射细节。
public interface StudentRepository extends JpaRepository<Student, String> {
}
接下来,启动 Spring Boot 应用并访问 Repository。此时,会抛出异常,表示 Schema 不存在。
通过集成测试来验证这一点:
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = SampleSchemaApplication.class)
class SampleSchemaApplicationIntegrationTest {
@Autowired
private StudentRepository studentRepository;
@Test
void whenSaveStudent_thenThrowsException() {
Student student = Student.builder()
.studentId("24567433")
.name("David Lloyds")
.build();
assertThatThrownBy(() -> studentRepository.save(student))
.isInstanceOf(InvalidDataAccessResourceUsageException.class);
}
}
执行测试时,我们可以在控制台中看到以下异常信息:
org.hibernate.exception.SQLGrammarException: could not prepare statement [Schema "TEST" not found; SQL statement:
select s1_0.student_id,s1_0.name from test.student s1_0 where s1_0.student_id=? [90079-214]] [select s1_0.student_id,s1_0.name from test.student s1_0 where s1_0.student_id=?]
3、通过数据库 URL 创建 Schema {#3通过数据库-url-创建-schema}
要解决这个问题,必须在 Spring Boot 应用启动时创建相应的 Schema。有两种不同的方法。
第一种方法是在建立数据库连接时创建数据库 Schema。H2 数据库 URL 允许我们在客户端通过 INIT 属性指定在连接数据库时执行的 DDL 或 DML 命令。在 Spring Boot 应用中,我们可以在 application.yaml
中定义 spring.datasource.url
属性:
spring:
jpa:
hibernate:
ddl-auto: create
datasource:
driverClassName: org.h2.Driver
url: jdbc:h2:mem:test;INIT=CREATE SCHEMA IF NOT EXISTS test
如果 Schema 不存在,初始化 DDL 会创建 Schema。需要注意的是,这种方法是 H2 数据库的专用方法,可能不适用于其他数据库。
这种方法通过数据库 URL 创建 Schema,而无需显式创建数据表。
我们依靠 Hibernate 的 ddl-auto
自动创建 Schema,通过在 YAML 文件中设置 spring.jpa.hibernate.ddl-auto
属性为 create
。
4、通过初始化脚本创建 Schema {#4通过初始化脚本创建-schema}
第二种方法是通用的,也适用于其他数据库。通过初始化脚本创建所有数据库组件,包括 Schema 和数据表。
Spring Boot 会在执行初始化脚本之前初始化 JPA Persistence Unit(持久化单元)。因此,需要在 application.yaml
中显式禁用 Hibernate 的 ddl-auto
功能,因为初始化脚本会负责这个任务:
spring:
jpa:
hibernate:
ddl-auto: none
如果不禁用它,就会在应用启动时遇到 "Schema TEST not found" 的异常,因为在 JPA Persistence Unit 初始化过程中,Schema 尚未创建。
现在,将创建 test Schema 和 student 表的 schema.sql
文件放到 resources
文件夹中:
CREATE SCHEMA IF NOT EXISTS test;
CREATE TABLE test.student (
student_id VARCHAR(10) PRIMARY KEY,
name VARCHAR(100)
);
默认情况下,Spring Boot 会在应用启动时查找 resources
文件夹中的 schema.sql
DDL 脚本,以初始化数据库。
5、总结 {#5总结}
"Schema not found" 异常是 Spring Boot 整合 H2 中常见的一个异常,我们可以通过数据库 URL 配置或初始化脚本来避免该异常。
Ref:https://www.baeldung.com/spring-boot-h2-exception-schema-not-found