2011-08-31 68 views
6

一個真實的生產場景。背景:6個表格:基金,賬戶,期間,期貨權重,持倉,頭寸。休眠,如何在真實世界中加載複雜對象

Fund: The fund information, for example: fund name, total Asset, start time.. etc. 
Account: each fund has an account (one to one) to fund. 
period: time period (for example: 2000-01-01 to 2000-12-31) 
periodweight: at a certain period, the target holding weight. 
holding: IBM, Oracle, GE are stock holdings. 
position: IBM($3000), oracle($2000), GE($5000) 

如果我有一個基金名稱:假基金,其中有一個目標保持爲IBM(30%)中,Oracle(20%),GE(50%)的期間(2000-01-01至2000-12-31),2000-01-01的實際狀況爲10%,10%,80%,2000-01-02爲20%,20%,60%表

Account: id account_Number Fund_id 
     * 1   0001  10 
      2   0002  11 

Fund:  id  name     other properties... 
     * 10   fake fund      xxx 
      11   another fake one    xxx 

period: id  start_time  end_time fund_id 
     * 3  2000-01-01  2000-12-31  10 
      4  2001-01-01  2001-12-31  10 

periodWeight: id  target_weight  holding_id period_id 
       *11  30%     21   3 
       *12  20%     22   3 
       *13  50%     23   3 

holding:  id    name   order  other properties... 
      *21    IBM    1    xxx 
      *22    Oracle   2    xxx 
      *23    GE    3    xxx 

position:  id  Account_id holding_id date    actual_position 
       1   1   11   2000-01-01   10% 
       2   1   12   2000-01-01   10% 
       3   1   13   2000-01-01   80% 
       4   1   11   2000-01-02   20% 
       5   1   12   2000-01-02   20% 
       6   1   13   2000-01-02   60% 

的Java類是

Account{ 
    @onetoOne(mappedby="account") 
    Fund f; 

    @oneToMany 
    Set<Position> positions; 
} 

Fund{ 
    @manyToOne 
    Account account; 

    @oneToMany(mappedby="fund") 
    Set<Period> periods; 
} 

Period{ 
    @manyToOne 
    Fund fund; 

    @oneToMany(mappedby="period") 
    Set<PeriodWeight> periodWeights; 
} 

PeriodWeight{ 
    @manyToOne 
    Period period; 

    @ManyToOne 
    Holding holding 
} 

Holding{ 
    @OneToMany(mappedby="holding") 
    Set<PeriodWeight> periodWeights; 

    @OneToMany 
    Set<Position> positions; 
} 

Position{ 
    @manyToOne 
    Account account; 

    @manyToOne 
    Holding holding; 
} 

我想有一個查詢: 基地d日期(2000-01-01)和基金名稱(假基金)。我想創建一個Fund對象,其中包含賬戶和期間(2000-01-01到2000-12-31),期間包含periodWeight,periodWeight包含持有,並且持有包含期權(2000-01-01 )。當沒有這樣的位置時,例如,我查詢2000-01-03和假基金,我想要有結構,只是位置是持有的空集。

如果有數據,hql可以正確加載結構。

select f from Fund f 
inner join fetch f.account a 
inner join fetch f.period p 
inner join fetch p.periodWeight w 
inner join fetch w.holding h 
inner join fetch h.positions po 
where f.name=:name and :date between p.start_date and p.end_date and :date=po.date and po.account= a 

問題是當位置表沒有數據時,它返回null。當沒有數據時,我需要一個sql來給我這個結構,它可以加載除了位置之外的所有東西,只需將位置設置爲空即可。

另一個問題是加載這種複雜結構的更好方法是什麼?一個像這樣的hql,或者通過一個hql加載部分結構,然後通過另一個hql加載其他部分?因爲在sql中,你總是一個接一個地加載它們,首先是基金,然後是期限,然後是權重,然後是持有,然後是持倉等。權重需要根據持有訂單進行排序。

select f from Fund f 
inner join fetch f.account a 
inner join fetch f.period p 
inner join fetch p.periodWeight w 
inner join fetch w.holding h 
left join fetch h.positions po with po.account= a and :date=po.date 
where f.name=:name and :date between p.start_date and p.end_date 

東西很接近,但是這給我的錯誤,

org.hibernate.hql.ast.QuerySyntaxException: with-clause not allowed on fetched associations; use filters 
+0

「問題是什麼時候沒有數據,它會返回null」 - 沒有數據在哪裏?在職位協會? –

+0

是的,當日期的位置表上沒有數據時,我只有2000-01-01和2000-01-02的數據。當我查詢2000-01-03和「假基金」時,它返回資金清單。 – Nan

+0

我想我的第一個問題是,如果我想加載這樣一個對象,我應該把它分成幾個小步驟,而不是一次性加載所有內容?如果我使用太多迷你步驟,我就無法感受到休眠的好處。 – Nan

回答

0

您應該能夠通過改變來實現:

inner join fetch h.positions p 

到:

left join fetch h.positions p 

並致電

query.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY); 

對您的查詢對象執行查詢之前。

+0

我不太清楚query.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY)的用途。左連接不成功。仍爲null – Nan

+0

您的查詢在兩處使用p別名:內部聯合提取f.period p和內部聯合提取h.positions p,您需要更改其中一個,然後更新WHERE子句。 –

+0

對不起,這是一個錯字,我已經改變了它的問題,並把正確的。我還提出了一個非常接近但尚未解決的問題。真的想知道這個值得這麼複雜的hql嗎?我完全同意你使用左連接獲取,問題是有條件的。它問我使用過濾器,我不知道如何使用它。順便說一下,位置表是相當大的,不適用於首先加載大量過濾器,必須適合數據庫查詢 – Nan

2

這是HQL中令人討厭的限制,我曾經幾次遇到基於時間的關聯。

我發現,這是含有fetchwith子句一個HQL查詢的解決方案:

select f from Fund f 
inner join fetch f.account a 
inner join fetch f.period p 
inner join fetch p.periodWeight w 
inner join fetch w.holding h 
left join fetch h.positions po 
where f.name=:name and :date between p.start_date and p.end_date 
and (po is null or (po.account= a and :date=po.date)) 

訣竅是所述with子句移動到where子句和添加一個選項爲對象爲空允許它返回一個位置的行。

+3

不幸的是它不等同。你放棄所有成功加入的行,但不符合WHERE中的條件。請參閱示例http://pastebin.com/eY8Vw0xG – okocian