2010-05-29 75 views
5

我無法優化Hibernate查詢以避免執行連接或次級選擇。避免使用Hibernate Criteria或HQL查詢進行二級選擇或連接

當執行(標準或HQL),如下面的一個Hibernate查詢:

return getSession().createQuery(("from GiftCard as card where card.recipientNotificationRequested=1").list(); 

...和where子句檢查不需要任何與其他表中的連接屬性...但Hibernate仍然執行與其他表的完全連接(或根據我如何設置fetchMode進行二級選擇)。

有問題的對象(GiftCard)有一對ManyToOne關聯,我寧願在這種情況下(但不一定是所有情況下)都懶惰地加載。我想要一個解決方案,可以控制執行查詢時延遲加載的內容。

這裏的禮金券實體是什麼樣子:

@Entity 
@Table(name = "giftCards") 
public class GiftCard implements Serializable 
{ 
private static final long serialVersionUID = 1L; 

private String id_; 
private User buyer_; 
private boolean isRecipientNotificationRequested_; 


@Id 
public String getId() 
{ 
    return this.id_; 
} 

public void setId(String id) 
{ 
    this.id_ = id; 
} 

@ManyToOne 
@JoinColumn(name = "buyerUserId") 
@NotFound(action = NotFoundAction.IGNORE) 
public User getBuyer() 
{ 
    return this.buyer_; 
} 
public void setBuyer(User buyer) 
{ 
    this.buyer_ = buyer; 
} 

@Column(name="isRecipientNotificationRequested", nullable=false, columnDefinition="tinyint") 
public boolean isRecipientNotificationRequested() 
{ 
    return this.isRecipientNotificationRequested_; 
} 

public void setRecipientNotificationRequested(boolean isRecipientNotificationRequested) 
{ 
    this.isRecipientNotificationRequested_ = isRecipientNotificationRequested; 
} 
} 

回答

2

至於說

我想要我可以控制什麼是延遲加載當我執行查詢

如果你有這樣一個

@Entity 
public class GiftCard implements Serializable { 

    private User buyer; 

    @ManyToOne 
    @JoinColumn(name="buyerUserId") 
    public User getBuyer() { 
     return this.buyer; 
    } 
} 
的映射解決方案

任何* ToOne關係,例如@OneToOne和@ManyToOne,默認情況下是FetchType.EAGER,這意味着它將是一個抓住了。但是,這不可能是你想要的。你說什麼我可以控制什麼是懶惰加載可以翻譯爲提取策略POJO in Action書支持所以根據您的使用情況下,像這樣的(注意方法簽名)的模式

public class GiftCardRepositoryImpl implements GiftCardRepository { 

    public List<GiftCard> findGiftCardWithBuyer() { 
     return sessionFactory.getCurrentSession().createQuery("from GiftCard c inner join fetch c.buyer where c.recipientNotificationRequested = 1").list(); 
    } 

} 

,您可以創建自己的發現...與...和...方法。它會照顧取正是你想要的

但它有一個問題:它不支持通用的方法簽名。對於每個@Entity存儲庫,您必須定義您的自定義找到... With ...和方法。正因爲如此,我告訴你我是如何定義一個通用存儲庫

public interface Repository<INSTANCE_CLASS, UPDATABLE_INSTANCE_CLASS, PRIMARY_KEY_CLASS> { 

    void add(INSTANCE_CLASS instance); 
    void remove(PRIMARY_KEY_CLASS id); 
    void update(PRIMARY_KEY_CLASS id, UPDATABLE_INSTANCE_CLASS updatableInstance); 
    INSTANCE_CLASS findById(PRIMARY_KEY_CLASS id); 
    INSTANCE_CLASS findById(PRIMARY_KEY_CLASS id, FetchingStrategy fetchingStrategy); 
    List<INSTANCE_CLASS> findAll(); 
    List<INSTANCE_CLASS> findAll(FetchingStrategy fetchingStrategy); 
    List<INSTANCE_CLASS> findAll(int pageNumber, int pageSize); 
    List<INSTANCE_CLASS> findAll(int pageNumber, int pageSize, FetchingStrategy fetchingStrategy); 
    List<INSTANCE_CLASS> findAllByCriteria(Criteria criteria); 
    List<INSTANCE_CLASS> findAllByCriteria(Criteria criteria, FetchingStrategy fetchingStrategy); 
    List<INSTANCE_CLASS> findAllByCriteria(int pageNumber, int pageSize, Criteria criteria); 
    List<INSTANCE_CLASS> findAllByCriteria(int pageNumber, int pageSize, Criteria criteria, FetchingStrategy fetchingStrategy); 

} 

但是,有時,你不希望所有的由通用庫接口中定義的方法。解決方案:創建一個AbstractRepository類,它將實現一個虛擬存儲庫。Spring框架,例如大量使用這種模式的接口>> AbstractInterface

public abstract class AbstractRepository<INSTANCE_CLASS, UPDATABLE_INSTANCE_CLASS, PRIMARY_KEY_CLASS> implements Repository<INSTANCE_CLASS, UPDATABLE_INSTANCE_CLASS, PRIMARY_KEY_CLASS> { 

    public void add(INSTANCE_CLASS instance) { 
     throw new UnsupportedOperationException("Not supported yet."); 
    } 

    public void remove(PRIMARY_KEY_CLASS id) { 
     throw new UnsupportedOperationException("Not supported yet."); 
    } 

    public void update(PRIMARY_KEY_CLASS id, UPDATABLE_INSTANCE_CLASS updatableInstance) { 
     throw new UnsupportedOperationException("Not supported yet."); 
    } 

    public INSTANCE_CLASS findById(PRIMARY_KEY_CLASS id) { 
     throw new UnsupportedOperationException("Not supported yet."); 
    } 

    public INSTANCE_CLASS findById(PRIMARY_KEY_CLASS id, FetchingStrategy fetchingStrategy) { 
     throw new UnsupportedOperationException("Not supported yet."); 
    } 

    public List<INSTANCE_CLASS> findAll() { 
     throw new UnsupportedOperationException("Not supported yet."); 
    } 

    public List<INSTANCE_CLASS> findAll(FetchingStrategy fetchingStrategy) { 
     throw new UnsupportedOperationException("Not supported yet."); 
    } 

    public List<INSTANCE_CLASS> findAll(int pageNumber, int pageSize) { 
     throw new UnsupportedOperationException("Not supported yet."); 
    } 

    public List<INSTANCE_CLASS> findAll(int pageNumber, int pageSize, FetchingStrategy fetchingStrategy) { 
     throw new UnsupportedOperationException("Not supported yet."); 
    } 

    public List<INSTANCE_CLASS> findAllByCriteria(Criteria criteria) { 
     throw new UnsupportedOperationException("Not supported yet."); 
    } 

    public List<INSTANCE_CLASS> findAllByCriteria(Criteria criteria, FetchingStrategy fetchingStrategy) { 
     throw new UnsupportedOperationException("Not supported yet."); 
    } 

    public List<INSTANCE_CLASS> findAllByCriteria(int pageNumber, int pageSize, Criteria criteria) { 
     throw new UnsupportedOperationException("Not supported yet."); 
    } 

    public List<INSTANCE_CLASS> findAllByCriteria(int pageNumber, int pageSize, Criteria criteria, FetchingStrategy fetchingStrategy) { 
     throw new UnsupportedOperationException("Not supported yet."); 
    } 

} 

所以你GiftCardRepository可以重新寫爲(見延伸,而不是工具)和只覆蓋你真正想要的

public class GiftCardRepository extends AbstractRepository<GiftCard, GiftCard, String> { 

    public static final GIFT_CARDS_WITH_BUYER GIFT_CARDS_WITH_BUYER = new GIFT_CARDS_WITH_WITH_BUYER(); 
    public static final GIFT_CARDS_WITHOUT_NO_RELATIONSHIP GIFT_CARDS_WITHOUT_NO_RELATIONSHIP = new GIFT_CARDS_WITHOUT_NO_RELATIONSHIP(); 

    public List<GiftCard> findAll(FetchingStrategy fetchingStrategy) { 
     sessionFactory.getCurrentSession().getNamedQuery(fetchingStrategy.toString()).list(); 
    } 


    /** 
     * FetchingStrategy is just a marker interface 
     * public interface FetchingStrategy {} 
     * 
     * And AbstractFetchingStrategy allows you to retrieve the name of the Fetching Strategy you want, by overriding toString method 
     * public class AbstractFetchingStrategy implements FetchingStrategy { 
     * 
     *  @Override 
     *  public String toString() { 
     *   return getClass().getSimpleName(); 
     *  } 
     * 
     * } 
     * 
     * Because there is no need to create an instance outside our repository, we mark it as private 
     * Notive each FetchingStrategy must match a named query 
     */ 
    private static class GIFT_CARDS_WITH_BUYER extends AbstractFetchingStrategy {}  
    private static class GIFT_CARDS_WITHOUT_NO_RELATIONSHIP extends AbstractFetchingStrategy {} 
} 

現在我們外部化的多我們的命名查詢 - 和可讀性和可維護性 - xml文件

// app.hbl.xml 
<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 
<hibernate-mapping> 
    <query name="GIFT_CARDS_WITH_BUYER"> 
     <![CDATA[ 
      from 
       GiftCard c 
      left join fetch 
       c.buyer 
      where 
       c.recipientNotificationRequested = 1 
     ]]> 
    </query> 
    <query name="GIFT_CARDS_WITHOUT_NO_RELATIONSHIP"> 
     <![CDATA[ 
      from 
       GiftCard 
     ]]> 
    </query> 
</hibernate-mapping> 

所以,如果你想找回你禮金券與買家,只需撥打

Repository<GiftCard, GiftCard, String> giftCardRepository; 

List<GiftCard> giftCardList = giftCardRepository.findAll(GiftCardRepository.GIFT_CARDS_WITH_WITH_BUYER); 

而且沒有任何關係,獲取我們的禮金券,只需撥打

List<GiftCard> giftCardList = giftCardRepository.findAll(GiftCardRepository.GIFT_CARDS_WITHOUT_NO_RELATIONSHIP); 

或使用進口靜態

import static packageTo.GiftCardRepository.*; 

And

List<GiftCard> giftCardList = giftCardRepository.findAll(GIFT_CARDS_WITHOUT_NO_RELATIONSHIP); 

我希望它對你有用!

+0

非常有趣...這是偉大的思想食物。它提出了一個側面的問題。我如何執行一個查詢,不會獲取目標GiftCard以外的任何關聯對象? – 2010-05-30 06:36:41

+0

@Ben Benson請參閱GiftCardRepository(最後一行),在底部指定查詢文件和附加代碼。如果我的回答滿足您的需求,請將其標記爲已接受。謝謝 – 2010-05-31 12:45:53

2

在JPA默認爲ManyToOne協會提取類型是渴望(即非懶惰),所以你可以嘗試用:

@ManyToOne(fetch=FetchType.LAZY) 

然後在任何JPA查詢中,可以使用left join fetch熱切地獲取關聯。