Hibernate的存储过程

2023/05/18

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 query = session.createSQLQuery("CALL GetAllFoos()").addEntity(Foo.class);
List<Foo> allFoos = query.list();

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

我们使用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查询和引用Foo映射实体的resultClass

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

resultClass属性与我们前面示例中的addEntity()方法起着相同的作用。

这两种方法可以互换使用,因为在性能或生产力方面,两者之间没有真正的区别。

3.4 使用@NamedStoredProcedureQuery调用存储过程

如果你使用的是JPA 2.1以及EntityManagerFactoryEntityManagerHibernate实现。

@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()存储过程。

此过程返回name属性与fooName参数匹配的Foo对象列表:

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数据库中的存储过程。

值得一提的是,并不是所有的RDBMS都支持存储过程。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章