日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發布時間:2023/12/3 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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連接泄露

總結

以上是生活随笔為你收集整理的hibernate连接泄露_泄漏抽象,或如何正确地与Hibernate绑定Oracle DATE的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。