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以及EntityManagerFactory和EntityManager的Hibernate实现。
@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上获得。