Hibernate中不支持復雜子查詢from (select ……)解決方案

問題分析

樓主之前在維護公司之前一個項目時遇到一個坑,就是涉及到一個復雜子查詢形如from(select......)形式的hql語句不支持枝恋,簡單說就是先要通過子查詢查詢出來一張新的虛擬表殖氏,然后和其他表做關聯(lián)才能得到業(yè)務所需要的最終數(shù)據(jù)。
原SQL語句如下:

SELECT k.term_id,
        sum(k.work_time) worktime
FROM 
    (SELECT o.term_id,
        o.report_date,
        o.work_time,
         o.term_brand,
        o.model_name
    FROM rep_hardware_fault_rate o
    GROUP BY  o.term_id,o.report_date,
    o.work_time, o.term_brand,o.model_name) k, view_device_dept_info v
WHERE k.term_id=v.term_id
GROUP BY  k.term_brand;

我在網(wǎng)上查了大量資料,發(fā)現(xiàn)有一些求助的帖子中有類似的問題描述茵乱,但是都沒有相應的解決方案。后面樓主想了下要不就簡化SQL語句然后再代碼中處理(這種效率很低搜变,最笨的方法)采缚,或者在數(shù)據(jù)庫中新建一個視圖,但這種處理方法也不是十分完美挠他,就這一塊業(yè)務用到了扳抽,會增加數(shù)據(jù)庫的開銷,而且假如說有很多類似的業(yè)務殖侵,那不是得建很多張視圖贸呢,這種辦法可持續(xù)性也不好。后面樓主還是沒放棄拢军,就覺得應該有其他人也遇到過類似的問題楞陷,肯定有比較完美的解決方案~終于功夫不有心人,樓主參考大量的博客和資料終于找到了一種比較完美的解決方案茉唉,即建立虛擬視圖法固蛾。

具體解決方案

簡單說就是將select子查詢到的虛擬表建立一個實體類映射成一個虛擬視圖,然后再進行關聯(lián)查詢操作度陆。這里要用到一個@Subselect注解艾凯,即
subselect (可選): 它將一個不可變(immutable)并且只讀的實體映射到一個數(shù)據(jù)庫的子查詢中。當你想用視圖代替一張基本表的時候坚芜,這是有用的览芳,但最好不要這樣做。
對Hibernate映射來說視圖和表是沒有區(qū)別的鸿竖,這是因為它們在數(shù)據(jù)層都是透明的( 注意:一些數(shù)據(jù)庫不支持視圖屬性沧竟,特別是更新的時候)。有時你想使用視圖缚忧,但卻不能在數(shù)據(jù)庫中創(chuàng)建它(例如:在遺留的schema中)悟泵。這樣的話,你可以映射一個不可變的(immutable)并且是只讀的實體到一個給定的SQL子查詢表達式:定義這個實體用到的表為同步(synchronize)闪水,確保自動刷新(auto-flush)正確執(zhí)行糕非, 并且依賴原實體的查詢不會返回過期數(shù)據(jù)。subselect在屬性元素和一個嵌套映射元素中都可見球榆。

核心代碼

好啦朽肥,廢話不多說,直接上核心代碼持钉,以供大家參考和借鑒衡招。

  1. 實體類
    注意,雖然我們查詢出來的視圖沒有id每强,但是這里必須加主鍵始腾,否則hql無法正常映射州刽,應該是必須遵從的規(guī)范。
    這里的@Subselect注解是查詢數(shù)據(jù)庫的表數(shù)據(jù)結果浪箭,將其映射為一個實體類穗椅;@Synchronize是定義這個實體用到的表為同步(synchronize),確保自動刷新(auto-flush)正確執(zhí)行奶栖。
@Entity
@Subselect(" select o.TERM_ID,o.REPORT_DATE,o.WORK_TIME,o.TERM_BRAND,o.MODEL_NAME " +
           " from REP_HARDWARE_FAULT_RATE o  " +
           " group by o.TERM_ID,o.REPORT_DATE,o.WORK_TIME,o.TERM_BRAND,o.MODEL_NAME ")
/**
 *如果子查詢涉及2個表匹表,則這樣寫
 *@Synchronize( { "test_item", "test_bid" })
 */
@Synchronize({"REP_HARDWARE_FAULT_RATE"})

public class ViewDeviceForWorkTime {
    
    /**
     * 主鍵Id
     * 這里必須寫,不寫會報錯驼抹,hql映射必須要加
     */
    @Id
    @GeneratedValue(generator = "system-uuid")
    @GenericGenerator(name = "system-uuid", strategy = "uuid")
    private String id;

    /**
     * 設備Id
     * 可以加Column桑孩,也可以不加,后臺配置了駝峰映射法
     */
    @Column(name = "TERM_ID")
    private String termId;

    /**
     * 記錄日期
     */
    private String reportDate;

    /**
     * 應工作時間
     */
    private String workTime;

    /**
     * 設備品牌
     */
    private String termBrand;

    /**
     * 設備型號
     */
    private String modelName;



    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getTermId() {
        return termId;
    }

    public void setTermId(String termId) {
        this.termId = termId;
    }

    public String getReportDate() {
        return reportDate;
    }

    public void setReportDate(String reportDate) {
        this.reportDate = reportDate;
    }

    public String getWorkTime() {
        return workTime;
    }

    public void setWorkTime(String workTime) {
        this.workTime = workTime;
    }

    public String getTermBrand() {
        return termBrand;
    }

    public void setTermBrand(String termBrand) {
        this.termBrand = termBrand;
    }

    public String getModelName() {
        return modelName;
    }

    public void setModelName(String modelName) {
        this.modelName = modelName;
    }
}

映射數(shù)據(jù)庫中的表view_device_dept_info框冀。

@Entity
@Table(name = "VIEW_DEVICE_DEPT_INFO")
public class ViewDeviceDeptInfoForOpenRate {
    @Id
    private String deviceId;
    private String termId;
    private String termSeq;
    private String counterCode;
    private String termAddr;
    private String typeId;
    private String brandId;
    private String modelId;
    private String termIp;
    private String areaAddr;
    private String status;
    private String companyId;
    private String companyName;
    private String deptId;
    private String deptCode;
    private String deptName;
    private Integer deptLevel;
    private String deptAddr;
    private String deptId1;
    private String deptName1;
    private String deptId2;
    private String deptName2;
    private String deptId3;
    private String deptName3;
    private String deptId4;
    private String deptName4;
    private String deptId5;
    private String deptName5;
    private String deptId6;
    private String deptName6;

    public String getDeviceId() {
        return deviceId;
    }

    public void setDeviceId(String deviceId) {
        this.deviceId = deviceId;
    }

   ......這里省略后面的get,set方法

}

2.業(yè)務處理
這里和大家的寫法可能有所差別,這里只貼出樓主實際的業(yè)務邏輯敏簿,供大家參考明也,只要大家理解這個思路就好了。

//查詢應工作時間
 StringBuffer wql = new StringBuffer();
            wql.append(" select o.termBrand,sum(o.workTime) as workTime ");
            wql.append(" from ViewDeviceForWorkTime o,ViewDeviceDeptInfoForOpenRate v ");
            wql.append(" where o.termId = v.termId ");
            //這里是設置查詢的參數(shù)惯裕,省略
            wql.append(paramsSql);
            wql.append(" group by o.termBrand ");
  // 設置查詢的參數(shù)
  Query queryWorkTime = createQuery(wql.toString());
            for (int i = 0; i < queryObj.length; i++) {
                if (!"".equals(queryObj[i])) {
                    queryWorkTime.setParameter(i, queryObj[i]);   
                }
            }
 Object[] list = queryWorkTime .list().toArray();

小結

這里我們就很好的解決了hql的這類子查詢問題温数,總的來說就是hql不直接支持類似from(select ......)這類單獨成一個虛擬表的子查詢,所以我們就把這個子查詢查詢出來的虛擬表給它建立一個虛擬視圖的實體映射類蜻势,而且不會影響數(shù)據(jù)庫的真實操作撑刺,再讓它隨著數(shù)據(jù)庫對應的表同步刷新即可。

參考博客

Hibernate中子查詢(subselect)的使用

hibernate使用from (select ……)子查詢的方法

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末握玛,一起剝皮案震驚了整個濱河市够傍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌挠铲,老刑警劉巖冕屯,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異拂苹,居然都是意外死亡安聘,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進店門瓢棒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來浴韭,“玉大人,你說我怎么就攤上這事脯宿∧罹保” “怎么了?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵嗅绰,是天一觀的道長舍肠。 經(jīng)常有香客問我搀继,道長,這世上最難降的妖魔是什么翠语? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任叽躯,我火速辦了婚禮,結果婚禮上肌括,老公的妹妹穿的比我還像新娘点骑。我一直安慰自己,他們只是感情好谍夭,可當我...
    茶點故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布黑滴。 她就那樣靜靜地躺著,像睡著了一般紧索。 火紅的嫁衣襯著肌膚如雪袁辈。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天珠漂,我揣著相機與錄音晚缩,去河邊找鬼。 笑死媳危,一個胖子當著我的面吹牛荞彼,可吹牛的內容都是我干的。 我是一名探鬼主播待笑,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼鸣皂,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了暮蹂?” 一聲冷哼從身側響起寞缝,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎椎侠,沒想到半個月后第租,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡我纪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年慎宾,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片浅悉。...
    茶點故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡趟据,死狀恐怖,靈堂內的尸體忽然破棺而出术健,到底是詐尸還是另有隱情汹碱,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布荞估,位于F島的核電站咳促,受9級特大地震影響稚新,放射性物質發(fā)生泄漏。R本人自食惡果不足惜跪腹,卻給世界環(huán)境...
    茶點故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一褂删、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧冲茸,春花似錦屯阀、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至逗栽,卻和暖如春盖袭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背彼宠。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工苍凛, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人兵志。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像宣肚,于是被迫代替她去往敵國和親想罕。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,472評論 2 348

推薦閱讀更多精彩內容

  • ORA-00001: 違反唯一約束條件 (.) 錯誤說明:當在唯一索引所對應的列上鍵入重復值時霉涨,會觸發(fā)此異常按价。 O...
    我想起個好名字閱讀 5,249評論 0 9
  • Arleta Pech是公認的優(yōu)秀靜物畫家,且是享有國際聲譽的頂級水彩藝術家笙瑟。她出版過很多藝術作品和教程楼镐。Arle...
    AmeliaL閱讀 632評論 2 10
  • 遮風擋雨框产、無所不能,這棵大樹便是我們的父親错洁。 “總是向你索取秉宿,卻不曾說謝謝你。直到長大以后屯碴,才懂得你不容易....
    螢火蟲菇娘閱讀 460評論 0 2
  • 10年前的一天早晨导而,母親告訴我隔壁的王大叔娶回來了一個啞巴忱叭,啞巴的爸媽送啞巴過來隔崎,看到光棍多年的王大叔家里一貧如洗...
    夕雁無邊閱讀 427評論 0 1