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

問題分析

樓主之前在維護(hù)公司之前一個(gè)項(xiàng)目時(shí)遇到一個(gè)坑,就是涉及到一個(gè)復(fù)雜子查詢形如from(select......)形式的hql語句不支持,簡(jiǎn)單說就是先要通過子查詢查詢出來一張新的虛擬表,然后和其他表做關(guān)聯(lián)才能得到業(yè)務(wù)所需要的最終數(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)有一些求助的帖子中有類似的問題描述,但是都沒有相應(yīng)的解決方案。后面樓主想了下要不就簡(jiǎn)化SQL語句然后再代碼中處理(這種效率很低,最笨的方法),或者在數(shù)據(jù)庫中新建一個(gè)視圖,但這種處理方法也不是十分完美,就這一塊業(yè)務(wù)用到了,會(huì)增加數(shù)據(jù)庫的開銷,而且假如說有很多類似的業(yè)務(wù),那不是得建很多張視圖,這種辦法可持續(xù)性也不好。后面樓主還是沒放棄,就覺得應(yīng)該有其他人也遇到過類似的問題,肯定有比較完美的解決方案~終于功夫不有心人,樓主參考大量的博客和資料終于找到了一種比較完美的解決方案,即建立虛擬視圖法。

具體解決方案

簡(jiǎn)單說就是將select子查詢到的虛擬表建立一個(gè)實(shí)體類映射成一個(gè)虛擬視圖,然后再進(jìn)行關(guān)聯(lián)查詢操作。這里要用到一個(gè)@Subselect注解,即
subselect (可選): 它將一個(gè)不可變(immutable)并且只讀的實(shí)體映射到一個(gè)數(shù)據(jù)庫的子查詢中。當(dāng)你想用視圖代替一張基本表的時(shí)候,這是有用的,但最好不要這樣做。
對(duì)Hibernate映射來說視圖和表是沒有區(qū)別的,這是因?yàn)樗鼈冊(cè)跀?shù)據(jù)層都是透明的( 注意:一些數(shù)據(jù)庫不支持視圖屬性,特別是更新的時(shí)候)。有時(shí)你想使用視圖,但卻不能在數(shù)據(jù)庫中創(chuàng)建它(例如:在遺留的schema中)。這樣的話,你可以映射一個(gè)不可變的(immutable)并且是只讀的實(shí)體到一個(gè)給定的SQL子查詢表達(dá)式:定義這個(gè)實(shí)體用到的表為同步(synchronize),確保自動(dòng)刷新(auto-flush)正確執(zhí)行, 并且依賴原實(shí)體的查詢不會(huì)返回過期數(shù)據(jù)。subselect在屬性元素和一個(gè)嵌套映射元素中都可見。

核心代碼

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

  1. 實(shí)體類
    注意,雖然我們查詢出來的視圖沒有id,但是這里必須加主鍵,否則hql無法正常映射,應(yīng)該是必須遵從的規(guī)范。
    這里的@Subselect注解是查詢數(shù)據(jù)庫的表數(shù)據(jù)結(jié)果,將其映射為一個(gè)實(shí)體類;@Synchronize是定義這個(gè)實(shí)體用到的表為同步(synchronize),確保自動(dòng)刷新(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個(gè)表,則這樣寫
 *@Synchronize( { "test_item", "test_bid" })
 */
@Synchronize({"REP_HARDWARE_FAULT_RATE"})

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

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

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

    /**
     * 應(yīng)工作時(shí)間
     */
    private String workTime;

    /**
     * 設(shè)備品牌
     */
    private String termBrand;

    /**
     * 設(shè)備型號(hào)
     */
    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è)務(wù)處理
這里和大家的寫法可能有所差別,這里只貼出樓主實(shí)際的業(yè)務(wù)邏輯,供大家參考,只要大家理解這個(gè)思路就好了。

//查詢應(yīng)工作時(shí)間
 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è)置查詢的參數(shù),省略
            wql.append(paramsSql);
            wql.append(" group by o.termBrand ");
  // 設(shè)置查詢的參數(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();

小結(jié)

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

參考博客

Hibernate中子查詢(subselect)的使用

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • ORA-00001: 違反唯一約束條件 (.) 錯(cuò)誤說明:當(dāng)在唯一索引所對(duì)應(yīng)的列上鍵入重復(fù)值時(shí),會(huì)觸發(fā)此異常。 O...
    我想起個(gè)好名字閱讀 5,986評(píng)論 0 9
  • Arleta Pech是公認(rèn)的優(yōu)秀靜物畫家,且是享有國(guó)際聲譽(yù)的頂級(jí)水彩藝術(shù)家。她出版過很多藝術(shù)作品和教程。Arle...
    AmeliaL閱讀 699評(píng)論 2 10
  • 遮風(fēng)擋雨、無所不能,這棵大樹便是我們的父親。 “總是向你索取,卻不曾說謝謝你。直到長(zhǎng)大以后,才懂得你不容易....
    螢火蟲菇娘閱讀 537評(píng)論 0 2
  • 10年前的一天早晨,母親告訴我隔壁的王大叔娶回來了一個(gè)啞巴,啞巴的爸媽送啞巴過來,看到光棍多年的王大叔家里一貧如洗...
    夕雁無邊閱讀 524評(píng)論 0 1

友情鏈接更多精彩內(nèi)容