1. 概述

在这个简短的教程中,我们将深入探讨 org.h2.jdbc.JdbcSQLSyntaxErrorException: SQL语句中的语法错误,预期的是“标识符” 这个异常。

首先,我们将解释异常的根本原因。接着,通过一个实际的例子,展示如何重现它,最后讲解如何解决这个问题。

2. 异常原因

在寻求解决方案之前,我们先理解这个异常。

通常情况下,H2 抛出 JdbcSQLSyntaxErrorException 是为了指出SQL语句中的语法错误。因此,“expected identifier”信息表明SQL期望一个合适的标识符,但我们未能提供。

这种异常最常见的原因是使用了保留关键字作为标识符

例如,将关键字 table 用于命名特定的SQL表会导致 JdbcSQLSyntaxErrorException

另一个可能的原因是SQL语句中缺少或放置了错误的关键字。

3. 重现异常

作为开发者,我们经常使用“user” 来表示处理用户的表。不幸的是,这在H2中是一个保留关键字

要重现这个异常,我们可以假设使用关键字“user”

首先,我们添加一个基本的SQL脚本来初始化并为H2数据库填充数据:

INSERT INTO user VALUES (1, 'admin', 'p@ssw@rd'); 
INSERT INTO user VALUES (2, 'user', 'userpasswd');

接下来,我们创建一个映射 user 表的实体类

@Entity
public class User {

    @Id
    private int id;
    private String login;
    private String password;

    // standard getters and setters
}

请注意,*@Entity* 是一个JPA注解,它标识一个类为实体类。

而*@Id* 表示与数据库主键对应的字段。

现在,如果我们运行主应用,Spring Boot会因为以下错误失败:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSourceScriptDatabaseInitializer' 
...
nested exception is org.h2.jdbc.JdbcSQLSyntaxErrorException: Syntax error in SQL statement "INSERT INTO [*]user VALUES (1, 'admin', 'p@ssw@rd')"; expected "identifier"; SQL statement:
INSERT INTO user VALUES (1, 'admin', 'p@ssw@rd') [42001-214]
...

如日志所示,H2抱怨插入查询,因为关键字 user 是保留的,不能用作标识符。

4. 解决方案

要修复异常,我们需要确保不在SQL中使用SQL保留关键字作为标识符。

或者,我们可以使用分隔符进行转义。H2支持双引号作为标准标识符分隔符。

首先,让我们将关键字 user 加上双引号:

INSERT INTO "user" VALUES (1, 'admin', 'p@ssw@rd');
INSERT INTO "user" VALUES (2, 'user', 'userpasswd');

接下来,我们为我们的实体 User 创建一个JPA仓库

@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
}

现在,让我们添加一个测试用例以确认一切按预期工作:

@Test
public void givenValidInitData_whenCallingFindAll_thenReturnData() {
    List<User> users = userRepository.findAll();

    assertThat(users).hasSize(2);
}

如上所示,findAll() 做好了工作,并没有因为 JdbcSQLSyntaxErrorException 而失败。

另一种避免异常的方法是将 NON_KEYWORDS=user 添加到JDBC URL:

spring.datasource.url=jdbc:h2:mem:mydb;NON_KEYWORDS=user

这样,我们告诉H2将 user 关键字从保留关键字列表中排除。

如果我们在使用Hibernate,可以设置 hibernate.globally_quoted_identifiers 属性为 true

spring.jpa.properties.hibernate.globally_quoted_identifiers=true

如属性名所示,Hibernate会自动对所有数据库标识符进行转义。

总之,以下是需要考虑的一些关键点:

  • 确保使用正确的SQL关键字,并正确放置它们
  • 避免使用保留关键字作为标识符
  • 检查SQL中不允许的特殊字符
  • 确保适当地转义或引用任何保留关键字

5. 总结

在这篇短文中,我们详细解释了 org.h2.jdbc.JdbcSQLSyntaxErrorException: SQL语句中的语法错误,预期的是“标识符” 异常背后的原因。然后,我们展示了如何产生这个异常以及如何解决它。

如往常一样,示例的完整源代码可以在GitHub上找到。