2010-05-29 12 views
5

Ich habe Probleme beim Optimieren von Hibernate-Abfragen, um das Ausführen von Joins oder sekundären Auswahls zu vermeiden.Vermeiden von sekundären Auswählen oder Verknüpfen mit Hibernate-Kriterien oder HQL-Abfrage

Wenn eine Hibernate-Abfrage durchgeführt wird (Kriterien oder HQL), wie die folgenden:

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

... und die where-Klausel untersucht Eigenschaften, die benötigen keine mit anderen Tabellen verknüpft ... aber Hibernate führt immer noch eine vollständige Verknüpfung mit anderen Tabellen aus (oder sekundäre Auswahl, je nachdem, wie ich den fetchMode einstelle).

Das fragliche Objekt (GiftCard) hat ein paar ManyToOne Assoziationen, die ich in diesem Fall bevorzugen würde (aber nicht unbedingt alle Fälle). Ich möchte eine Lösung, die ich kontrollieren kann, was geladen wird, wenn ich die Abfrage ausführe.

Hier ist, was die Giftcard Entity wie folgt aussieht:

@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; 
} 
} 

Antwort

2

Wie gesagt

ich eine Lösung wollen, die ich kontrollieren kann, was faul geladen wird, wenn ich die Abfrage ausführen

Wenn Sie eine Abbildung wie diese haben ein

@Entity 
public class GiftCard implements Serializable { 

    private User buyer; 

    @ManyToOne 
    @JoinColumn(name="buyerUserId") 
    public User getBuyer() { 
     return this.buyer; 
    } 
} 

Jede * ToOne-Beziehung, wie z. B. @OneToOne und @ManyToOne, ist standardmäßig FetchType.EAGER, was bedeutet, dass es ein a Immer geholt. Aber es könnte nicht sein, was du willst. Was du sagst als Ich kann kontrollieren, was lazy geladen wird kann übersetzt werden als Abrufen der Strategie. POJO in Action Buch unterstützt ein Muster wie dieses (Hinweis Methodensignatur)

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(); 
    } 

} 

auf Ihren Anwendungsfall So basiert, können Sie Ihre eigenen finden ... Mit ... Und ... Methode erstellen. Es wird sich darum kümmern, zu holen, was Sie wollen

Aber es hat ein Problem: Es unterstützt keine generische Methode Signatur. Für jedes @Entity-Repository müssen Sie Ihre benutzerdefinierte Methode finden ... mit ... und definieren. Aus diesem Grund zeige ich Ihnen, wie ich eine generische Repository

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); 

} 

Aber definieren manchmal, müssen Sie nicht alle Methoden, die von generischen Repository-Schnittstelle definiert werden soll. Lösung: Erstellen Sie eine AbstractRepository-Klasse, die ein Dummy-Repository implementiert.Spring Framework zum Beispiel schwer, diese Art von Muster verwenden Schnittstelle >> 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."); 
    } 

} 

So Ihre GiftCardRepository werden kann neu geschrieben als (Siehe erstreckt statt Geräte) und überschreibt nur, was Sie wirklich wollen

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 {} 
} 

Jetzt externalisieren wir unsere benannte Abfrage in einem mehrzeiligen - und lesbar und wartbar - xML-Datei

// 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> 

Also, wenn Sie Sie Giftcard mit dem Käufer abrufen möchten, rufen Sie einfach

Repository<GiftCard, GiftCard, String> giftCardRepository; 

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

Und unsere Giftcard ohne keine Beziehung abzurufen, rufen Sie einfach

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

oder Verwendung import static

import static packageTo.GiftCardRepository.*; 

Und

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

Ich hoffe, es kann dir nützlich sein!

+0

Sehr interessant ... das ist gutes Denkanstoß. Und es wirft eine Nebenfrage auf. Wie würde ich eine Abfrage durchführen, die keine anderen Objekte als die Ziel-GiftCard abruft? –

+0

@Ben Benson Siehe GiftCardRepository (letzte Zeile), benannte Abfragedatei und zusätzlicher Code am unteren Rand. Wenn meine Antwort Ihre Anforderungen erfüllt, markieren Sie sie als akzeptiert. Vielen Dank –

2

In JPA der Standardtyp für ManyToOne Verbände ist bestrebt, (dh nicht faul) holen, so können Sie versuchen, mit:

@ManyToOne(fetch=FetchType.LAZY) 

Dann In jeder JPA-Abfrage kann die Assoziation unter Verwendung von left join fetch eifrig abgerufen werden.