1. 概述

在这个简短的教程中,我们将探讨如何解决Hibernate的QueryException: "named parameter not bound"问题。

首先,我们会解释异常背后的主要原因,然后演示如何重现它,最后学习如何修复。

2. 理解异常

在寻求解决方案之前,让我们先理解这个异常及其堆栈跟踪的含义。

简单来说,当Hibernate在将Hibernate查询转换为SQL时遇到语法错误,它会抛出QueryException来表示错误。因此,堆栈跟踪中的"named parameter not bound"意味着Hibernate无法绑定查询中的一个指定的命名参数。

通常,命名参数前缀为冒号(:),后面跟着一个实际值的占位符,需要在执行查询前设置:

SELECT p FROM Person p WHERE p.firstName = :firstName;

我们异常的一个常见原因是,在Hibernate查询中忘记为命名参数分配值。

3. 复现异常

现在我们知道导致异常的原因,让我们通过一个实际例子深入了解一下如何复现它。

首先,考虑一下Person实体类:

@Entity
public class Person {

    @Id
    private int id;
    private String firstName;
    private String lastName;

   // standard getters and setters
}

在这里,@Entity注解表示我们的类映射数据库中的一个表,而@Id表示id属性代表主键。

现在,我们创建一个带有命名参数的Hibernate查询,并假装忘记为参数设置值:

@Test
void whenNotSettingValueToNamedParameter_thenThrowQueryException() {
    Exception exception = assertThrows(QueryException.class, () -> {
        Query<Person> query = session.createQuery("FROM Person p WHERE p.firstName = :firstName", Person.class);
        query.list();
    });

    String expectedMessage = "Named parameter not bound";
    String actualMessage = exception.getMessage();

    assertTrue(actualMessage.contains(expectedMessage));
}

如我们所见,测试用例由于QueryException: "named parameter not bound" 失败。原因在于Hibernate对命名参数firstName一无所知,期望在执行查询前设置这个参数。

4. 修复异常

避免QueryException的方法是在执行查询前为命名参数分配一个值。可以使用setParameter()方法:

@Test
void whenSettingValueToNamedParameter_thenDoNotThrowQueryException() {
    Query<Person> query = session.createQuery("FROM Person p WHERE p.firstName = :firstName", Person.class);
    query.setParameter("firstName", "Azhrioun");

    assertNotNull(query.list());
}

如上所示,测试用例成功通过。**setParameter()调用告诉Hibernate在执行查询时使用哪个值作为我们的命名参数。**

5. 总结

在这篇短文中,我们解释了Hibernate抛出QueryException: "named parameter not bound"的原因。接着,我们通过实际示例展示了如何重现异常以及如何修复它。

如往常一样,所有示例的完整源代码可在GitHub上找到:https://github.com/eugenp/tutorials/tree/master/persistence-modules/hibernate-exceptions