【发布时间】:2017-02-03 08:06:40
【问题描述】:
目前我们有一个看起来像这样的类(删除了去个性化和不相关的部分):
@Entity
@Table(name = "MAIN_TABLE")
public class MainTable extends AbstractTable {
@OneToMany(fetch = FetchType.LAZY, mappedBy = "mainTable")
@OrderBy("CREATED_ON DESC")
private Set<MainTableState> states;
...
public MainTableState getActiveState(){
if(this.states == null || this.states.isEmpty()){
return null;
}
MainTableState latest = states.iterator().next();
// The reason we use this for-loop, even though we have the @OrderBy annotation,
// Is because we can later add states to this list, which aren't automatically ordered
for(MainTableState state : states){
if(state.getCreatedOn() != null && latest.getCreatedOn() != null &&
state.getCreatedOn().after(latest.getCreatedOn()){
latest = state;
}
}
return latest;
}
...
}
所以目前默认情况下它会从数据库中检索所有 MainTableStates,如果我们需要 activeState,我们使用 for-loop 方法。显然,这对性能非常不利。目前我们根本不使用这个列表(目的是有一个状态的历史,但这已经被推迟到未来),但我们确实使用了getActiveState()方法相当多,主要是为了显示一个字符串里面UI 中的MainTableState-class。
此外,即使我们总是使用TreeSet 并保持排序,这样我们就不需要循环而只需要states.iterator().next(),它仍然会初始化状态列表。通过一些繁重的性能测试,当java.lang.OutOfMemoryError: GC overhead limit exceeded 崩溃时,我们有超过 100 万个 MainTableState-instances。
因此,我们想将其更改为以下内容:
@Entity
@Table(name = "MAIN_TABLE")
public class MainTable extends AbstractEntity {
@???
private MainTableState activeState;
...
public MainTableStates getActiveState(){
return activeState;
}
...
}
那么,我的问题是,我应该在@??? 中添加什么来完成此操作?我假设我需要 @Formula 或类似的东西,但我怎么能说休眠它应该返回一个 MainTableState 对象?我已经看到 @Formula 与 MAX 一起用于日期,但那是为了获取该日期属性,而不是根据该最大日期获取整个对象。
在 @user2447161 的建议之后,我使用了 @Where-annotation,这确实有助于将 Collection 大小减少到 1(有时),但我还有两个相关的问题:
如何使用
@OnToMany和@Where但获得单个对象,而不是大小为1 的对象列表?这甚至可能吗? Here in a answer from December 2010 it is stated it isn't.这个问题在过去六年的某个地方修复了吗?-
如何处理where子句中的随机别名?我可以这样做:
@OneToMany(fetch = FetchType.LAZY, mappedBy = "mainTable") @Where(clause = "CREATED_ON = (SELECT MAX(mts.CREATED_ON) FROM MAIN_TABLE_STATES mts WHERE mts.FK_MAIN_ID = ???.MAIN_ID)") 私有集合状态; // TODO 获取单个对象而不是大小为 1 的集合
问题在于??? 是hibernate 生成的随机别名(有时是this_,有时类似于mainTable_1_,等等)。如何为数据库的整个查询设置此别名以在此处使用它?我还尝试了MAIN_TABLE.MAIN_ID,但它不起作用,并且没有别名它也不起作用,因为它使用MainTableState-alias 而不是MainTable-alias(如下所示)。
from
MAIN_TABLE this_
left outer join
MAIN_TABLE_STATUSES mainstat2_
on this_.main_id=mainstat2_.fk_main_id
and (
mainstat2_.created_on = (
SELECT
MAX(mts.created_on)
FROM
MAIN_TABLE_STATUSES mts
WHERE
-- mainstat2_.main_id should be this_.main_id instead here:
mts.fk_main_id = mainstat2_.main_id
)
)
【问题讨论】:
-
除非你有大量的行,没有索引和/或在性能方面有非常严格的要求,否则我真的看不出这将是一个性能问题。对于现代计算机,for 循环通常只需纳秒,加载一组而不是单行应该可以忽略不计。此外,请确保列表在使用时是预先获取的,惰性会带来相当大的开销- 加载(可能比实际循环大一些数量级..)
-
@Tobb 我在问题中添加了另一行。 for 循环不是主要问题,而是
java.lang.OutOfMemoryError: GC overhead limit exceeded有超过 100 万个MainTableState-instances。这就是为什么我们只想保存单个活动状态而不是整个集合(当前未使用)的主要原因。 -
也许检查@where 和过滤器...stackoverflow.com/questions/12365285/…。或者从自定义查询中创建自己的 DTO...
标签: java oracle hibernate max hibernate-annotations