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
实体中的order
和where
值。
3. 实际的SQL错误
我们可能已经能推断出异常的根本原因。即使不能,我们也选择最喜欢的SQL编辑器,粘贴捕获的SQL语句并使用示例值:
INSERT INTO broken_phone_order (order, where) VALUES ('some-1', 'somewhere');
大多数情况下,我们会看到where
和order
已经被标记为红色。如果没有,一旦执行,数据库引擎(我们的例子中是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标准,ORDER
和WHERE
都是保留字,它们在语言语法中有特殊含义。每个数据库引擎都有一份基础列表,但也可以扩展。
我们可以通过在其前后添加单引号“
来简单地消除它们的特殊意义,例如\`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
注解中正确转义保留关键字。通过在order
和where
术语周围添加额外的转义字符,我们影响了Hibernate生成的查询,并解决了异常。
如往常一样,本文的代码可以在GitHub上找到。