2012-02-23 26 views
13

所以即時得到一些特定實體的情況下,通過ID加載由ID多個實體有效地在Hibernate 4

for(Integer songId:songGroup.getSongIds()) 
{ 
    session = HibernateUtil.getSession(); 
    Song song = (Song) session.get(Song.class,id); 
    processSong(song); 
} 

這會爲每個ID的SQL查詢,所以內容時發生,我認爲我應該做的這在一個,但我找不到一種方法來獲得多個實體在一個調用中除了通過運行查詢。所以我寫了一個查詢

return (List) session.createCriteria(Song.class) 
     .add(Restrictions.in("id",ids)).list(); 

,但如果我啓用2級緩存並不意味着我的老方法將能夠從二級高速緩存返回的對象(如果他們已經被請求過),但我查詢將始終轉到數據庫。

這樣做的正確方法是什麼?

+0

你有沒有想過辦法做到這一點? – FGreg 2012-10-24 19:36:56

回答

8

你在這裏要求做的是讓Hibernate爲你的Criteria做特殊情況處理,這是很多要問的。

你必須自己做,但並不難。使用SessionFactory.getCache(),可以獲取對緩存對象的實際存儲的引用。這樣做如下:

for (Long id : allRequiredIds) { 
    if (!sessionFactory.getCache().containsEntity(Song.class, id)) { 
    idsToQueryDatabaseFor.add(id) 
    } else { 
    songs.add(session.get(Song.class, id)); 
    } 
} 

List<Song> fetchedSongs = session.createCriteria(Song.class).add(Restrictions.in("id",idsToQueryDatabaseFor).list(); 
songs.addAll(fetchedSongs); 

然後從緩存中的歌曲也會從那裏獲取的,而不是得到一個單一select拉的人。

+0

謝謝,這是有道理的 – 2013-12-10 19:17:57

0

hibernate二級緩存與hibernate查詢緩存之間存在差異。 下面的鏈接解釋它真的很好:如果您使用的是相同的查詢多次使用相同的參數,那麼你可以減少數據庫http://www.javalobby.org/java/forums/t48846.html

簡而言之, 命中使用兩者的結合。

0

您可以做的另一件事是對id列表進行排序,並確定連續id的子序列,然後在單個查詢中查詢每個子序列。例如,給定List<Long> ids,請執行以下操作(假設你在Java中的Pair類):

List<Pair> pairs=new LinkedList<Pair>(); 
List<Object> results=new LinkedList<Object>(); 
Collections.sort(ids); 
Iterator<Long> it=ids.iterator(); 

Long previous=-1L; 
Long sequence_start=-1L; 
while (it.hasNext()){ 
    Long next=it.next(); 

    if (next>previous+1) { 
     pairs.add(new Pair(sequence_start, previous)); 
     sequence_start=next; 
    } 
    previous=next; 
} 
pairs.add(new Pair(sequence_start, previous)); 

for (Pair pair : pairs){ 
    Query query=session.createQuery("from Person p where p.id>=:start_id and p.id<=:end_id"); 
    query.setLong("start_id", pair.getStart()); 
    query.setLong("end_id", pair.getEnd()); 

    results.addAll((List<Object>)query.list()); 

} 
1

如果您知道的ID存在,你可以使用load(..)沒有實際擊中DB創建一個代理:

返回具有給定標識符的給定實體類的持久化實例,獲取指定的鎖定模式(假定實例存在)。

List<Song> list = new ArrayList<>(ids.size()); 
for (Integer id : ids) 
    list.add(session.load(Song.class, id, LockOptions.NONE)); 

當你訪問一個非標識符訪問,Hibernate將檢查緩存,如果需要退回到DB,使用批量抓取,如果配置。

如果ID不存在,一旦加載對象,就會發生ObjectNotFoundException。這可能是你的代碼中的某個地方,你不會真正期望出現異常 - 最後你使用了一個簡單的訪問器。因此,要麼100%確定ID存在,要麼至少在你期望的地方強迫ObjectNotFoundException,例如填充列表後立即。