hibernate连接泄露_泄漏抽象,或如何正确地与Hibernate绑定Oracle DATE

hibernate连接泄露

我们最近发布了一篇文章,介绍如何在SQL / JDBC和jOOQ中正确绑定Oracle DATE类型 。 这篇文章在Reddit上颇受关注, Vlad Mihalcea对此发表了有趣的评论,他经常在他的博客上撰写有关Hibernate,JPA,事务管理和连接池的博客 。 Vlad指出,使用Hibernate也可以解决此问题,我们很快将对此进行研究。

Oracle DATE有什么问题?

上一篇文章中提出的问题涉及以下事实:查询在Oracle DATE列上使用过滤器:

// execute_at is of type DATE and there's an index
PreparedStatement stmt = connection.prepareStatement("SELECT * " + "FROM rentals " +"WHERE rental_date > ? AND rental_date < ?");

…并且我们使用java.sql.Timestamp作为绑定值:

stmt.setTimestamp(1, start);
stmt.setTimestamp(2, end);

…那么,即使我们应该进行常规的INDEX RANGE SCAN,执行计划对FULL TABLE SCAN还是INDEX FULL SCAN都会变得非常糟糕。

-------------------------------------
| Id  | Operation          | Name   |
-------------------------------------
|   0 | SELECT STATEMENT   |        |
|*  1 |  FILTER            |        |
|*  2 |   TABLE ACCESS FULL| RENTAL |
-------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------1 - filter(:1<=:2)2 - filter((INTERNAL_FUNCTION("RENTAL_DATE")>=:1 AND INTERNAL_FUNCTION("RENTAL_DATE")<=:2))

这是因为通过此INTERNAL_FUNCTION()将数据库列从Oracle DATE扩展到Oracle TIMESTAMP ,而不是将java.sql.Timestamp值截断为Oracle DATE

有关问题本身的更多详细信息,请参见上一篇文章。

使用Hibernate防止此INTERNAL_FUNCTION()

可以使用org.hibernate.usertype.UserType通过Hibernate的专有API进行修复。

假设我们具有以下实体:

@Entity
public class Rental {@Id@Column(name = "rental_id")public Long rentalId;@Column(name = "rental_date")public Timestamp rentalDate;
}

现在,让我们在这里运行此查询(例如,我使用的是Hibernate API,而不是JPA):

List<Rental> rentals =
session.createQuery("from Rental r where r.rentalDate between :from and :to").setParameter("from", Timestamp.valueOf("2000-01-01 00:00:00.0")).setParameter("to", Timestamp.valueOf("2000-10-01 00:00:00.0")).list();

我们现在得到的执行计划再次效率低下:

-------------------------------------
| Id  | Operation          | Name   |
-------------------------------------
|   0 | SELECT STATEMENT   |        |
|*  1 |  FILTER            |        |
|*  2 |   TABLE ACCESS FULL| RENTAL |
-------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------1 - filter(:1<=:2)2 - filter((INTERNAL_FUNCTION("RENTAL0_"."RENTAL_DATE")>=:1 AND INTERNAL_FUNCTION("RENTAL0_"."RENTAL_DATE")<=:2))

解决方案是将此@Type批注添加到所有相关列中…

@Entity
@TypeDefs(value = @TypeDef(name = "oracle_date", typeClass = OracleDate.class)
)
public class Rental {@Id@Column(name = "rental_id")public Long rentalId;@Column(name = "rental_date")@Type(type = "oracle_date")public Timestamp rentalDate;
}

并注册以下简化的UserType

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.Objects;import oracle.sql.DATE;import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.usertype.UserType;public class OracleDate implements UserType {@Overridepublic int[] sqlTypes() {return new int[] { Types.TIMESTAMP };}@Overridepublic Class<?> returnedClass() {return Timestamp.class;}@Overridepublic Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)throws SQLException {return rs.getTimestamp(names[0]);}@Overridepublic void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session)throws SQLException {// The magic is here: oracle.sql.DATE!st.setObject(index, new DATE(value));}// The other method implementations are omitted
}

这将起作用,因为使用供应商特定的oracle.sql.DATE类型将对您的执行计划产生与在SQL语句中显式强制转换绑定变量相同的效果,如上一篇文章 CAST(? AS DATE) 。 现在,执行计划是所需的计划:

------------------------------------------------------
| Id  | Operation                    | Name          |
------------------------------------------------------
|   0 | SELECT STATEMENT             |               |
|*  1 |  FILTER                      |               |
|   2 |   TABLE ACCESS BY INDEX ROWID| RENTAL        |
|*  3 |    INDEX RANGE SCAN          | IDX_RENTAL_UQ |
------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------1 - filter(:1<=:2)3 - access("RENTAL0_"."RENTAL_DATE">=:1 AND "RENTAL0_"."RENTAL_DATE"<=:2)

如果要重现此问题,只需通过JPA / Hibernate用java.sql.Timestamp绑定值查询任何Oracle DATE列, 并按照此处所示获取执行计划 。

不要忘记刷新共享池和缓冲区高速缓存以在两次执行之间强制执行新计划的计算,因为每次生成SQL都是相同的。

我可以使用JPA 2.1吗?

乍一看,看起来JPA 2.1中的新转换器功能( 就像jOOQ的转换器功能一样 )应该可以解决问题。 我们应该能够写:

import java.sql.Timestamp;import javax.persistence.AttributeConverter;
import javax.persistence.Converter;import oracle.sql.DATE;@Converter
public class OracleDateConverter 
implements AttributeConverter<Timestamp, DATE>{@Overridepublic DATE convertToDatabaseColumn(Timestamp attribute) {return attribute == null ? null : new DATE(attribute);}@Overridepublic Timestamp convertToEntityAttribute(DATE dbData) {return dbData == null ? null : dbData.timestampValue();}
}

然后可以将此转换器与我们的实体一起使用:

import java.sql.Timestamp;import javax.persistence.Column;
import javax.persistence.Convert;
import javax.persistence.Entity;
import javax.persistence.Id;@Entity
public class Rental {@Id@Column(name = "rental_id")public Long rentalId;@Column(name = "rental_date")@Convert(converter = OracleDateConverter.class)public Timestamp rentalDate;
}

但是不幸的是,这不是开箱即用的,因为Hibernate 4.3.7会认为您将要绑定VARBINARY类型的变量:

// From org.hibernate.type.descriptor.sql.SqlTypeDescriptorRegistrypublic <X> ValueBinder<X> getBinder(JavaTypeDescriptor<X> javaTypeDescriptor) {if ( Serializable.class.isAssignableFrom( javaTypeDescriptor.getJavaTypeClass() ) ) {return VarbinaryTypeDescriptor.INSTANCE.getBinder( javaTypeDescriptor );}return new BasicBinder<X>( javaTypeDescriptor, this ) {@Overrideprotected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)throws SQLException {st.setObject( index, value, jdbcTypeCode );}};}

当然,我们可能可以通过某种方式调整此SqlTypeDescriptorRegistry来创建自己的“绑定程序”,但随后我们将返回特定于Hibernate的API。 这个特定的实现可能是Hibernate端的“错误”,已在此处注册以作记录:

https://hibernate.atlassian.net/browse/HHH-9553

结论

即使在JCP视为“标准”的情况下,抽象在各个级别上都是泄漏的。 标准通常是事后证明行业实际标准的一种手段(当然要涉及一些政治因素)。 我们不要忘记,Hibernate并不是从一个标准开始的,而是在14年前彻底改变了标准的J2EE人士对持久性的思考方式。

在这种情况下,我们有:

  • Oracle SQL的实际实现
  • SQL标准,它指定的DATE与Oracle完全不同
  • ojdbc,它扩展了JDBC以允许访问Oracle功能
  • JDBC,在时间类型方面遵循SQL标准
  • Hibernate,它提供专有的API,以便在绑定变量时访问Oracle SQL和ojdbc功能
  • JPA,它在时间类型方面再次遵循SQL标准和JDBC
  • 您的实体模型

如您所见,实际的实现(Oracle SQL)通过Hibernate的UserType或JPA的Converter泄漏到您自己的实体模型中。 从那时起,它将有望与您的应用程序隔离开(直到不会),使您无需理会这个讨厌的Oracle SQL详细信息。

无论如何,如果您想解决实际的客户问题(即即将出现的重大性能问题),那么您将需要使用Oracle SQL,ojdbc和Hibernate的特定于供应商的API-而不是假装该SQL ,JDBC和JPA标准是底线。

但这可能没关系。 对于大多数项目,最终的实现锁定是完全可以接受的。

翻译自: https://www.javacodegeeks.com/2015/01/leaky-abstractions-or-how-to-bind-oracle-date-correctly-with-hibernate.html

hibernate连接泄露

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/338800.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

信工干货||C语言中的运算符和表达式

C语言中的运算符和表达式1算术运算符及表达式&#xff08;1&#xff09;算术运算符包括&#xff1a; &#xff0c;-&#xff0c;*&#xff0c;/&#xff0c;%。&#xff08;2&#xff09;*&#xff0c;/&#xff0c;%同级&#xff0c;比 &#xff0c;-高。&#xff08;3&#x…

java程会释放锁join_关于join() 是否会释放锁的一些思考

# 首先从一个很有意思的问题开始&#xff1a;- 问 &#xff1a; Thread 的join() 方法是否会释放锁&#xff1f;- 答&#xff1a; 会&#xff01;# 如果一切到这里就结束了&#xff0c;那可能也就没有这篇小记了&#xff0c;但是我的脑子却冒出了一些奇怪的想法&#xff1a;- 释…

java lambda::_书评:精通Lambda:多核世界中的Java编程

java lambda::从版本8开始&#xff0c;λ编程&#xff08;lambda编程&#xff09;终于在Java世界中引入。此功能将在很大程度上改变Java开发人员的编程方式以及针对样板代码的新“武器”。 Java 8通过引入新的Stream API&#xff0c;大部分已将函数式编程应用在Collections API…

3皮卡丘眨眼代码_活见久,皮卡丘居然是一门编程语言

我很荣幸地向你介绍皮卡神教的编程语言&#xff0c;这门语言专为皮神设计(认真脸)。为什么一定要学习这门语言呢——谁不想要只皮卡丘我问你&#xff1f;在当今的宝可梦就业环境中&#xff0c;大多数皮卡丘们都在残忍的宝可梦训练师手下过着顺从和被奴役的生活。他们经常被迫与…

C语言关系运算符详解

关系运算符在使用时&#xff0c;它的的两边都会有一个表达式&#xff0c;比如变量、数值、加减乘除运算等&#xff0c;关系运算符的作用就是判明这两个表达式的大小关系。注意&#xff0c;是判明大小关系&#xff0c;不是其他关系。C语言提供了以下关系运算符&#xff1a;关系运…

sizeof不是java关键字是_下列哪项不是Java语言的关键字。

下列哪项不是Java语言的关键字。A:instanceofB:gotoC:volatileD:sizeof正确答案:sizeof解析&#xff1a;下列哪项不是Java语言的关键字。A:instanceofB:gotoC:volatileD:sizeof相关问题&#xff1a;汉代公主的丧葬不包括&#xff1a;A:柏椁B:没有墓道C:百官送葬D:樟棺大学生人际…

jboss fuse 教程_IDC关于使用JBoss Fuse的商业价值的报告(与Apache Camel一起使用)

jboss fuse 教程这只是一篇博客文章&#xff0c;具有更多的商业性质&#xff0c;但是您不能一无所有。 实际上&#xff0c;这也是使Apache Camel保持活力并保持良好状态的原因&#xff0c;这还归功于其商业上的成功。 希望从JBoss Fuse之类的产品中寻找有关在商业上使用Apache…

python list存储对象_python List 对象

从源码中可以看到&#xff0c;PyListObject中存储数据用的是PyObject **ob_item&#xff0c; 是当作Vector类似的方法。Items must normally not be NULL&#xff0c;这句是说list中的元素从list中删除&#xff0c;并不会立即free&#xff0c; 而是放到list的缓存池中。这个和整…

嵌入式软件架构设计分层思路

在正规的项目开发中&#xff0c;项目往往是并行开发的&#xff0c;也就是说硬件设计、底层软件设计、应用软件设计等是同步进行的。比如说在开发板上调试模块驱动&#xff0c;在其他平台上调试应用程序再移植到目前这个平台等。嵌入式专栏1为什么很少看见嵌入式软件架构师职位在…

python单元测试的应用_单元测试使用请求库的python应用程序

如果你使用具体请求尝试httmock.它的奇妙简单和优雅&#xff1a;from httmock import urlmatch, HTTMockimport requests# define matcher:urlmatch(netlocr(.*\.)?google\.com$)def google_mock(url, request):return Feeling lucky, punk?# open context to patchwith HTTM…

jconsole 使用_我的Wiki:使用JConsole对WildFly(或JBoss AS7)进行远程JMX访问

jconsole 使用与以前的版本相比&#xff0c;JBoss AS7的目标之一是使其在默认情况下更加安全。 受此目标直接影响的领域之一是&#xff0c;您不再期望服务器在端口上公开某些服务&#xff0c;而无需任何身份验证/授权即可访问它。 请记住&#xff0c;在以前的JBoss AS版本中&am…

python写入并获取剪切板内容_python写入并获取剪切板内容

{"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],"search_count":[{"count_phone":4,"count":4}]},"card":[{"des":"阿里技术人对外发布原创技术内容的最大平台&…

C语言函数返回1和返回0究竟哪个好?

基本上&#xff0c;没有人会将大段的C语言代码全部塞入 main() 函数。更好的做法是按照复用率高、耦合性低的原则&#xff0c;尽可能的将代码拆分不同的功能模块&#xff0c;并封装成函数。C语言代码的组合千变万化&#xff0c;因此函数的功能可能会比较复杂&#xff0c;不同的…

java向指定文件继续写内容_java 向指定文件写入内容(如文件存在,则先删除再创建;写入如目录不存在,则创建)...

/*** 向指定文件写入内容(如文件存在&#xff0c;则先删除再创建写入)** param content 保存内容* param path 文件完整路径* throws IOException*/public static void save(String content, String path) throws IOException {FileWriter writer null;try {File file new Fi…

java ee4.8下载_在WildFly 8.2中修补焊接3 – Java EE 8的第一个实验RI

java ee4.8下载Java EE 8一直在发展&#xff0c;并且已经提出了几个新的组件JSR。 JSR 365将定义CDI 2.0的规范。 红帽已经开始开发Weld 3的实现原型&#xff0c; Alpha3最近发布了 。 Red Hat的Java EE 8兼容应用服务器将是WildFly&#xff0c;将在其中实现所有不同的技术。 …

乐高ev3搭建图_乐高EV3第一讲,Hello Ev3,搭建机甲战神模型

课程主题认识编程及结构。课程目标1.简单认识 Ev3 的硬件及使用2.学习 Ev3主控自带的功能(最好拿控制器给孩子演示讲解)联系同学们好&#xff01;从今天开始我们就要进入乐高 EV3的世界&#xff0c;制作更多更酷、更好玩的机器人了&#xff01;首先老师先来问大家几个基本的问题…

对单片机C语言的一些误用和总结

我觉得语言之所以能称之为语言&#xff0c;它肯定是一种工具一种相互交流相互通信相互传达之间的意图的工具&#xff0c;作为语言那肯定得有自己的语法&#xff0c;要想相互交流肯定得先学好它的语法吧(比如像表达式&#xff0c;函数&#xff0c;循环&#xff0c;指针)我称之为…

JAVA中使用bos做视频上传_JAVA语言之搭建物流BOS项目骨架

本文主要向大家介绍了JAVA语言之搭建物流BOS项目骨架&#xff0c;通过具体的内容向大家展示&#xff0c;希望对大家学习JAVA语言有所帮助。提供pom.xml4.0.0com.itheimabos-parent0.0.1-SNAPSHOTpom父工程4.2.4.RELEASE5.0.7.Final2.3.241.6.61.2.121.2.3org.springframeworksp…

js 使用多态替换条件语句_用多态和组成替换多个条件

js 使用多态替换条件语句用多态替换条件语句是一种众所周知的重构模式。 如果您不熟悉该模式&#xff0c;可以在此处查看 。 但是&#xff0c;一旦类中有多个条件检查所基于的字段&#xff0c;该基本解决方案便会开始崩溃。 我们将研究一些有关如何使用这些可能性的想法。 有很…

python连不上树莓派_树莓派respberry中cmake编译链接python2.7库不成功,求教

本人在树莓派pi3平台上&#xff0c;下载respberry 5.4 lite 编译一个github上的项目&#xff0c;该项目用cmake管理&#xff0c;目前看到 链接库阶段不成功。请教如何调整cmake的链接库目录&#xff0c;或是怎样直接调整生成的makefile&#xff0c;用于连编是一个主动降噪的项目…