2011-08-31 66 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