1. 概述

存储过程是一组驻留在数据库中的编译后的 SQL 语句。它们被用来封装和共享逻辑,并利用数据库特有的功能,如索引提示或特定的关键字。

本文将展示如何使用 Hibernate 调用 MySQL 数据库 中的 存储过程

2. MySQL 中的存储过程

在讨论如何从 Hibernate 中调用存储过程之前,我们需要先创建它。

为了快速演示 MySQL 示例,我们将创建一个用于从 foo 表获取所有记录的存储过程。

要创建存储过程,我们可以使用 CREATE PROCEDURE 语句:

DELIMITER //
    CREATE PROCEDURE GetAllFoos()
        LANGUAGE SQL
        DETERMINISTIC
        SQL SECURITY DEFINER
        BEGIN
            SELECT * FROM foo;
        END //
DELIMITER;

BEGIN 语句之前,可以定义可选的语句。要深入了解这些语句的细节,请参考官方 MySQL 文档

我们可以使用 CALL 语句确保我们的存储过程按预期执行:

CALL GetAllFoos();

现在,存储过程已经运行起来,让我们直接进入如何从 Hibernate 中调用它的内容。

3. 使用 Hibernate 调用存储过程

从 Hibernate 3 开始,我们有机会使用原始 SQL 语句,包括存储过程来查询数据库。

本节将通过看似基础的示例,说明如何使用 Hibernate 调用名为 GetAllFoos() 的存储过程。

3.1. 配置

在开始编写可运行的代码之前,我们需要在项目中配置 Hibernate。

关于 Maven 依赖、MySQL 配置、Hibernate 配置以及 SessionFactory 实例化,您可以查看 Hibernate 文章

3.2. 使用 CreateNativeSQL 方法调用存储过程

Hibernate 允许直接以 原生 SQL 格式表达查询。因此,我们可以直接创建一个原生 SQL 查询,并使用 CALL 语句调用 getAllFoos() 存储过程:

Query<Foo> query = session.createNativeQuery("CALL GetAllFoos()").addEntity(Foo.class);
List<Foo> allFoos = query.list();

上述查询返回一个列表,其中每个元素都是一个 Foo o 对象。

我们使用 addEntity() 方法从原生 SQL 查询中获取实体对象,否则,如果存储过程返回非原始值时,会抛出 ClassCastException

3.3. 使用 @NamedNativeQueries 调用存储过程

另一种调用存储过程的方法是使用 @NamedNativeQueries 注解。

@NamedNativeQueries 用于指定一个与持久化单元关联的原生 SQL 命名查询数组:

@NamedNativeQueries({ 
  @NamedNativeQuery(
    name = "callGetAllFoos", 
    query = "CALL GetAllFoos()", 
    resultClass = Foo.class) 
})
@Entity
public class Foo implements Serializable {
    // Model definition
}

每个命名查询显然都有一个 name 属性,实际的 SQL 查询,以及指向已映射实体的 resultClass

Query<Foo> query = session.getNamedQuery("callGetAllFoos", Foo.class);
List<Foo> allFoos = query.list();

resultClass 属性的作用与我们在前一个示例中使用的 addEntity() 方法相同。

这两种方法都可以互换使用,因为它们在性能和生产力方面没有真正的区别。

3.4. 使用 @NamedStoredProcedureQuery 调用存储过程

如果您正在使用 JPA 2.1 和 Hibernate 实现的 EntityManagerFactoryEntityManager

可以使用 @NamedStoredProcedureQuery 注解来声明存储过程:

@NamedStoredProcedureQuery(
  name="GetAllFoos",
  procedureName="GetAllFoos",
  resultClasses = { Foo.class }
)
@Entity
public class Foo implements Serializable {
    // Model Definition 
}

要调用命名的存储过程查询,我们需要实例化一个 EntityManager,然后调用 createNamedStoredProcedureQuery() 方法来创建存储过程:

StoredProcedureQuery spQuery = 
  entityManager.createNamedStoredProcedureQuery("getAllFoos");

我们可以通过直接调用 StoredProcedureQuery 对象上的 execute() 方法来获取 Foo 实体的列表。

4. 带参数的存储过程

几乎所有的存储过程都需要参数。本节将展示如何使用 Hibernate 调用带有参数的存储过程。

让我们在 MySQL 中创建一个名为 getFoosByName() 的存储过程。

这个存储过程返回一个 Foo 对象列表,其中 name 属性匹配参数 fooName

DELIMITER //
    CREATE PROCEDURE GetFoosByName(IN fooName VARCHAR(255))
        LANGUAGE SQL
        DETERMINISTIC
        SQL SECURITY DEFINER
        BEGIN
            SELECT * FROM foo WHERE name = fooName;
        END //
DELIMITER;

要调用 GetFoosByName() 存储过程,我们将使用命名参数:

Query query = session.createSQLQuery("CALL GetFoosByName(:fooName)")
  .addEntity(Foo.class)
  .setParameter("fooName","New Foo");

同样,命名参数 :fooName 可以与 @NamedNativeQuery 注解一起使用:

@NamedNativeQuery(
  name = "callGetFoosByName", 
  query = "CALL GetFoosByName(:fooName)", 
  resultClass = Foo.class
)

命名查询将以以下方式调用:

Query query = session.getNamedQuery("callGetFoosByName")
  .setParameter("fooName","New Foo");

当使用 @NamedStoredProcedureQuery 注解时,我们可以使用 @StoredProcedureParameter 注解指定参数:

@NamedStoredProcedureQuery(
  name="GetFoosByName",
  procedureName="GetFoosByName",
  resultClasses = { Foo.class },
  parameters={
    @StoredProcedureParameter(name="fooName", type=String.class, mode=ParameterMode.IN)
  }
)

我们可以使用 setParameter() 方法调用带有 fooName 参数的存储过程:

StoredProcedureQuery spQuery = entityManager.createNamedStoredProcedureQuery("GetFoosByName")
  .setParameter("fooName", "NewFooName");

5. 结论

本文展示了如何使用 Hibernate 在 MySQL 数据库中通过不同方法调用存储过程。

值得一提的是,并非所有关系型数据库管理系统都支持存储过程。

您可以在链接的 GitHub 项目 中查看本文提供的示例。


» 下一篇: JSF EL入门介绍