編輯器載入中...在 Hibernate 架構中提供直接操作 JDBC 介面的原因
Hibernate 架構在處理複雜查詢方面的問題
Hibernate 是一個開放原始碼的對象關係映射架構,它對 JDBC 進行了非常輕量級的對象封裝,使得 Java 程式員可以隨心所欲的使用物件導向編程思維來操縱資料庫。Hibernate 的優勢在於屏蔽了資料庫細節,對於新增修改刪除的資料層操作,不再需要跟具體的 SQL 陳述式打交道,簡單的對對象執行個體進行增刪改操作即可。
但是,對於多表關聯、分組統計、排序等複雜的查詢功能時,由於 Hibernate 自身的 O-R 映射機制,父子表之間關聯取資料會產生大量冗餘的查詢操作,效能低下。此類情況下,直接使用 JDBC 的 SQL 陳述式反而更加靈活和高效。
Hibernate 架構處理複雜查詢問題執行個體分析
考慮如下資料庫實體樣本,表 A 為主表,表 B 和表 C 為子表,A 與 B、A 與 C 表均為 1 對多關係,在 B 表和 C 表中以 A_ID 外鍵欄位關聯 A 表父記錄。
圖 1. 資料庫實體樣本圖
在 Hibernate 架構中,通常採用以下配置方式完成 A 表與 B,C 表父子實體之間的級聯查詢操作,Hibernate 實體配置 xml 如下:
清單 1. hibernate 實體配置 xml
A.hbm.xml:
B.hbm.xml:
C.hbm.xml
對應的 Hibernate 領域實體類程式碼範例如下:
清單 2. hibernate 實體類樣本
A.java:
public class A implements java.io.Serializable,Comparable {
private long id;
private Set children_b = new HashSet();
private Set children_c = new HashSet();
public A(long id) {
this.id = id;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public Set getChildern_b() {
return children_b;
}
public void setChildren_b (Set children_b) {
this.children_b = children_b;
}
public Set getChildern_c() {
return children_c;
}
public void setChildren_c (Set children_c) {
this.children_c = children_c;
}
public int compareTo(Object other) {
A otherSubject = (A)other;
long curAmount=this.getChildren_b().size()+this.getChildren_c().size();
long otherAmount =otherSubject.getChildren_b().size()
+ otherSubject.getChildren_c().size();
if(curAmountotherAmount)
{
return 1;
}
else
{
return 0;
}
}
}
B.java:
public class B implements java.io.Serializable,Comparable {
private long id;
private long a_id;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public long getA_id() {
return a_id;
}
public void setA_id(long a_id) {
this.a_id = a_id;
}
public B(long id) {
this.id=id;
}
}
C.java:
public class C implements java.io.Serializable,Comparable {
private long id;
private long a_id;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public long getA_id() {
return a_id;
}
public void setA_id(long a_id) {
this.a_id = a_id;
}
public C(long id) {
this.id=id;
}
}
假設現在要統計 A 表中從屬的 B 表和 C 表記錄之和最高的 top10 的 A 表記錄,在 Hibernate 架構下,由於取 A 表對應的資料庫記錄時,已關聯取出了對應的 B、C 表子記錄存放於 A 實體類的 children_a,children_c 的屬性中,因此 top10 的功能可以通過比較每個 A 表實體類中 children_a、children_c 的 Set 的 size 大小並進行排序得到,其程式碼範例如下:
清單 3. 排序程式碼範例
private ArrayList sortAByAmount(ArrayList all)
{
for(int i=0;i fetchObjects(ResultSet rs)
{
ArrayList ret = new ArrayList();
//example:
//while(rs.next())
//{
//Object object = new Object();
//rs.getString(1);
//rs.getString(2);
//ret.add(object);
//}
return ret;
}
public ArrayList getObjectsBySql(String pureSql)
{
Connection con = curSeesion.connection();
ps = con.prepareStatement(sqlbuf.toString());
rs = ps.executeQuery();
try
{
return this.fetchObjects(rs);
}
catch(Exception es)
{
System.out.println(es.getMessage());
return null;
}
finally
{
try
{
ps.close();
rs.close();
con.close();
}
catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
使用該解決方案時,只需要將程式碼封裝解壓至項目原始碼目錄,在想要直接操作 JDBC 介面的 DAO 模組繼承 BaseHibernateDao 類,然後重寫 fetchObjects 方法填入從自身資料庫表欄位填充到領域對象的操作 , 即可輕鬆調用 getObjectsBySql 傳入原生 SQL 陳述式,返回具體的領域對象實體集合,當然使用者也可以通過 getConnection 獲得 JDBC 的 Connection 執行個體來進行自己需要的特定的 JDBC 底層操作。
仍然以上文中的 A、B、C 表為例,採用該解決方案完成 top10 取數的程式碼範例如下:
清單 7. 使用解決方案樣本
public class testDAO extends BaseHibernateDao{
private String sqlQuery = " select tab1.ID,tab1.sumCol+tab2.sumCol"+
" from(select a.ID, count(b.ID) sumCol"+
" from A a left join B b on a.ID=b.ID"+
" GROUP BY a.ID)tab1, "+
" (select a.ID,count(c.ID) sumCol"+
" from A a left join C c on a.ID=c.ID"+
" GROUP BY a.ID)tab2"+
" where tab1.ID=tab2.ID"+
" order by tab1.sumCol+tab2.sumCol desc";
@override
public ArrayList fetchObjects(ResultSet rs)
{
ArrayList ret = new ArrayList();
int count=1;
while(rs.next())
{
A a = new A();
a.setId(rs.getLong(1));
System.out.println("top"+(count++)+" amount:"+rs.getLong(2));
ret.add(object);
}
return ret;
}
}
解決方案驗證
在實際 mySql 資料庫環境中,以 A 表資料量 1000 條,B 表資料量 3W 多條,C 表資料量 2000 條情況下進行上文中提到的 top10 的操作,採用 Hibernate 的耗時和用 JDBC 介面解決方案的效率比較如下:
表 1. Hibernate 架構方式與 JDBC 介面方式效率比較 1:
Hibernate 架構方式採用 JDBC 介面解決方案
查詢耗時 1475ms 1096ms
排序耗時 1035ms 0ms
總計: 2110ms 1096ms
A 表資料量 2000 條,B 表資料量 6W,C 表資料量 4000 條情況下,採用 Hibernate 的耗時和用 JDBC 介面解決方案的效率比較:
表 2. Hibernate 架構方式與 JDBC 介面方式效率比較 2:
Hibernate 架構方式採用 JDBC 介面解決方案
查詢耗時 2836ms 1657ms
排序耗時 1568ms 0ms
總計: 4404ms 1657ms
由以上結果可以看出:在資料量遞增的情況下,採用 Hibernate 方式下效率與庫表資料呈線性增長,且排序的操作的效率也是一樣,而直接採用 JDBC 介面解決方案下效率遠遠高於 Hibernate 方式,且在資料量增長的情況下耗時的增長速度處於合理的區間內。
回頁首
總結
本文分析了 Hibernate 架構在處理複雜查詢功能上的效率問題,提出並實現了一個在 Hibernate 架構內提供直接 JDBC 操作介面的解決方案,並實際驗證了該解決方案的有效性,文中的原始碼可以直接運用於選擇 Hibenrate 架構作為資料持久層實現的 J2EE 項目,使之具備操作底層 JDBC 的功能。