内容简介:在本文中,您将学习使用JPA和Hibernate时调用存储过程的最佳方法,以便尽快释放底层JDBC资源。我决定写这篇文章,因为Hibernate处理存储过程的方式会导致
在本文中,您将学习使用JPA和Hibernate时调用存储过程的最佳方法,以便尽快释放底层JDBC资源。
我决定写这篇文章,因为Hibernate处理存储过程的方式会导致 ORA-01000: maximum open cursors exceeded Oracle 上出现问题,如本 Hibernate论坛帖子 或 StackOverflow问题所述 。
存储过程调用如何与JPA和Hibernate一起使用
要使用JPA调用存储过程或数据库函数,可以使用 StoredProcedureQuery 如以下示例所示:
StoredProcedureQuery query = entityManager .createStoredProcedureQuery(<font>"count_comments"</font><font>) .registerStoredProcedureParameter( </font><font>"postId"</font><font>, Long.<b>class</b>, ParameterMode.IN ) .registerStoredProcedureParameter( </font><font>"commentCount"</font><font>, Long.<b>class</b>, ParameterMode.OUT ) .setParameter(</font><font>"postId"</font><font>, 1L); query.execute(); Long commentCount = (Long) query .getOutputParameterValue(</font><font>"commentCount"</font><font>); </font>
在幕后,StoredProcedureQuery接口通过特定于Hibernate 的接口进行扩展ProcedureCall,因此我们可以像这样重写前面的示例:
ProcedureCall query = session .createStoredProcedureCall(<font>"count_comments"</font><font>); query.registerParameter( </font><font>"postId"</font><font>, Long.<b>class</b>, ParameterMode.IN ) .bindValue(1L); query.registerParameter( </font><font>"commentCount"</font><font>, Long.<b>class</b>, ParameterMode.OUT ); Long commentCount = (Long) call .getOutputs() .getOutputParameterValue(</font><font>"commentCount"</font><font>); </font>
在执行Hibernate的ProcedureCall的JPA的StoredProcedureQuery或outputs().getCurrent()时,Hibernate执行以下操作:
请注意,JDBC CallableStatement已准备好并存储在关联的ProcedureOutputsImpl对象中。在调用getOutputParameterValue方法时,Hibernate将使用底层CallableStatement来获取OUT参数。
因此,CallableStatement即使在执行存储过程并获取OUT或REF_CURSOR参数之后,底层JDBC 仍保持打开状态。
现在,默认情况下,CallableStatement在当前正在运行的数据库事务结束时将关闭,这是通过调用commit或rollback实现。
测试时间
要验证此行为,请考虑以下测试用例:
StoredProcedureQuery query = entityManager .createStoredProcedureQuery(<font>"count_comments"</font><font>) .registerStoredProcedureParameter( </font><font>"postId"</font><font>, Long.<b>class</b>, ParameterMode.IN ) .registerStoredProcedureParameter( </font><font>"commentCount"</font><font>, Long.<b>class</b>, ParameterMode.OUT ) .setParameter(</font><font>"postId"</font><font>, 1L); query.execute(); Long commentCount = (Long) query .getOutputParameterValue(</font><font>"commentCount"</font><font>); assertEquals(Long.valueOf(2), commentCount); ProcedureOutputs procedureOutputs = query .unwrap(ProcedureOutputs.<b>class</b>); CallableStatement callableStatement = ReflectionUtils .getFieldValue( procedureOutputs, </font><font>"callableStatement"</font><font> ); assertFalse(callableStatement.isClosed()); procedureOutputs.release(); assertTrue(callableStatement.isClosed()); </font>
请注意,CallableStatement即使在调用execute或获取commentCount OUT参数后仍处于打开状态。只有调用完成后才释放ProcedureOutputs的对象时,CallableStatement也会封闭。
尽快关闭JDBC语句
因此,要CallableStatement尽快关闭JDBC ,在存储过程中获取所需的所有数据后调用release:
StoredProcedureQuery query = entityManager .createStoredProcedureQuery(<font>"count_comments"</font><font>) .registerStoredProcedureParameter( </font><font>"postId"</font><font>, Long.<b>class</b>, ParameterMode.IN ) .registerStoredProcedureParameter( </font><font>"commentCount"</font><font>, Long.<b>class</b>, ParameterMode.OUT ) .setParameter(</font><font>"postId"</font><font>, 1L); <b>try</b> { query.execute(); Long commentCount = (Long) query .getOutputParameterValue(</font><font>"commentCount"</font><font>); assertEquals(Long.valueOf(2), commentCount); } <b>finally</b> { query.unwrap(ProcedureOutputs.<b>class</b>).release(); } CallableStatement callableStatement = ReflectionUtils .getFieldValue( query.unwrap(ProcedureOutputs.<b>class</b>), </font><font>"callableStatement"</font><font> ); assertTrue(callableStatement.isClosed()); </font>
在finally块中release的关联ProcedureOutputs对象上调用方法可确保CallableStatement无论存储过程调用的结果如何都关闭JDBC 。
现在,release手动调用有点乏味,所以我决定创建 HHH-13215 Jira问题,我将其集成到Hibernate ORM 6分支中。
因此,从Hibernate 6开始,您可以像这样重写前面的示例:
Long commentCount = doInJPA(entityManager -> { <b>try</b>(ProcedureCall query = entityManager .createStoredProcedureQuery(<font>"count_comments"</font><font>) .unwrap(ProcedureCall.<b>class</b>)) { <b>return</b> (Long) query .registerStoredProcedureParameter( </font><font>"postId"</font><font>, Long.<b>class</b>, ParameterMode.IN ) .registerStoredProcedureParameter( </font><font>"commentCount"</font><font>, Long.<b>class</b>, ParameterMode.OUT ) .setParameter(</font><font>"postId"</font><font>, 1L) .getOutputParameterValue(</font><font>"commentCount"</font><font>); } }); </font>
好多了,对吧?
通过ProcedureCall扩展接口AutoClosable,我们可以使用try-with-resource Java语句,因此在解除分配JDBC资源时,调用数据库存储过程会更简洁,更直观。
结论
在CallableStatement使用JPA和Hibernate调用存储过程时,尽快释放底层JDBC 非常重要,否则,数据库游标将一直打开,直到提交或回滚当前事务为止。
因此,从Hibernate ORM 6开始,您应该使用try-finally块。同时,对于Hibernate 5和4,CallableStatement在完成获取所需的所有数据后,应该使用try-finally块关闭右侧。
以上所述就是小编给大家介绍的《使用JPA和Hibernate调用存储过程的最佳方法 - Vlad Mihalcea》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- MySQL存储过程语句及调用
- 使用MyBatis轻松实现递归查询与存储过程调用
- asp.net中如何调用sql存储过程实现分页
- Oracle报存储过程中调用DBLink同义词出现错误:PLS-00201: 必须声明标识符
- Kubernetes 1.14.1 使用 NFS-Client-Provisioner 调用 NFS Server 静态和动态配置 Mysql 持久化存储
- 直观讲解-RPC调用和HTTP调用的区别
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
离心力:互联网历史与数字化未来
[英] 乔尼·赖安(Johnny Ryan) / 段铁铮 / 译言·东西文库/电子工业出版社 / 2018-2-1 / 68.00元
★一部详实、严谨的互联网史著作; ★哈佛、斯坦福等高校学生必读书目; ★《互联网的未来》作者乔纳森·L. 齐特雷恩,《独立报》《爱尔兰时报》等知名作者和国外媒体联合推荐。 【内容简介】 虽然互联网从诞生至今,不过是五六十年,但我们已然有必要整理其丰富的历史。未来的数字世界不仅取决于我 们的设想,也取决于它的发展历程,以及互联网伟大先驱们的理想和信念。 本书作者乔尼· ......一起来看看 《离心力:互联网历史与数字化未来》 这本书的介绍吧!