1. 概述

在这个教程中,我们将学习如何通过正确转义数据库列名中的保留关键字,避免遇到晦涩难懂的Hibernate异常。

2. 准备工作

在深入之前,我们先定义一个简单的带有保留关键字的BrokenPhoneOrder实体:

@Entity
public class BrokenPhoneOrder implements Serializable {
    @Id
    @Column(name = "order")
    String order;
    @Column(name = "where")
    String where;

    // getters and setters
}

理解这个实体导致语法错误的原因后,我们将将其转换为没有使用不当保留关键字的PhoneOrder

2. Hibernate异常 - 隐藏的根本原因

假设我们从基于ORM的应用程序中收到以下异常:

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax

当我们尝试将BrokenPhoneOrder持久化到数据库时,总会抛出这个异常:

@Test
void givenBrokenPhoneOrderWithReservedKeywords_whenNewObjectIsPersisted_thenItFails() {
    BrokenPhoneOrder order = new BrokenPhoneOrder(randomUUID().toString(), "My House");

    assertThatExceptionOfType(PersistenceException.class).isThrownBy(() -> {
        session.persist(order);
        session.flush();
    });
}

异常本身没有帮助,让我们开启额外的SQL语句日志来查看实际的查询。

我们需要修改hibernate.cfg.xml文件,包含show_sql属性:

<property name="show_sql">true</property>

hibernate.properties中,语法稍有不同:

show_sql = true

现在我们可以看到异常发生前的日志中记录了以下SQL语句:

Hibernate: insert into broken_phone_order (where, order) values (?, ?)

问号只是我们试图创建的BrokenPhoneOrder实体中的orderwhere值。

3. 实际的SQL错误

我们可能已经能推断出异常的根本原因。即使不能,我们也选择最喜欢的SQL编辑器,粘贴捕获的SQL语句并使用示例值:

INSERT INTO broken_phone_order (order, where) VALUES ('some-1', 'somewhere');

大多数情况下,我们会看到whereorder已经被标记为红色。如果没有,一旦执行,数据库引擎(我们的例子中是MySQL)会返回错误:

ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'order, where) values ('a', 'b')' at line 1

4. 数据库关键字

根据SQL标准,ORDERWHERE都是保留字,它们在语言语法中有特殊含义。每个数据库引擎都有一份基础列表,但也可以扩展。

我们可以通过在其前后添加单引号来简单地消除它们的特殊意义,例如\`where\

让我们应用到INSERT语句中,确认这次它能够通过:

INSERT INTO broken_phone_order (`order`, `where`) VALUES ('some-1', 'somewhere');

5. Hibernate中的保留关键字

问题是:如果查询是自动生成的,我们如何更改Hibernate查询?

在Hibernate中,就像在SQL中一样,我们可以在@Column注解中使用单引号进行转义。

让我们尝试用PhoneOrder实体试一下:

@Entity
@Table(name = "phone_order")
public class PhoneOrder implements Serializable {
    @Id
    @Column(name = "`order`")
    String order;
    @Column(name = "`where`")
    String where;

    // getters and setters
}

通过正确的关键字转义,MySQL不再抱怨语法问题,实体成功被持久化:

@Test
void givenPhoneOrderWithEscapedKeywords_whenNewObjectIsPersisted_thenItSucceeds() {
    PhoneOrder order = new PhoneOrder(randomUUID().toString(), "here");

    session.persist(order);
    session.flush();
}

6. 总结

在这篇文章中,我们学习了如何在@Column注解中正确转义保留关键字。通过在orderwhere术语周围添加额外的转义字符,我们影响了Hibernate生成的查询,并解决了异常。

如往常一样,本文的代码可以在GitHub上找到。


« 上一篇: Java Weekly, 第478期
» 下一篇: Roaring Bitmap介绍