2010-05-29 10 views
5

Tengo problemas para optimizar las consultas de Hibernate para evitar realizar uniones o selecciones secundarias.Evitar selecciones o uniones secundarias con Hibernate Criteria o consulta HQL

Cuando una consulta de Hibernate se lleva a cabo (HQL) o criterios, tales como las siguientes:

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

... y la cláusula where examina las propiedades que no requiere ningún une con otras mesas ... pero Hibernate aún realiza una unión completa con otras tablas (o selecciones secundarias dependiendo de cómo configuro fetchMode).

El objeto en cuestión (GiftCard) tiene un par de asociaciones de ManyToOne que preferiría cargar de forma perezosa en este caso (pero no necesariamente en todos los casos). Quiero una solución que pueda controlar lo que se carga de forma perezosa cuando realizo la consulta.

Esto es lo que la tarjeta de regalo Entidad se ve así:

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

Respuesta

2

Como se ha dicho

quiero una solución que puedo controlar lo que se carga con pereza cuando realizo la consulta

Si usted tiene un mapeo como éste

@Entity 
public class GiftCard implements Serializable { 

    private User buyer; 

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

Cualquier relación * ToOne, como @OneToOne y @ManyToOne, es, de forma predeterminada, FetchType.EAGER lo que significa que será una siempre fue a buscar. Pero, no podría ser lo que quieres. Lo que dices como Puedo controlar lo que está cargado perezosamente se puede traducir como Obteniendo Estrategia. POJO in Action libro es compatible con un patrón como este (firma del método Aviso)

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

} 

Así que basado en su caso de uso, puede crear su propio encontrar ... Con ... Y ... método. Se encargará de buscar exactamente lo que quiere

Pero tiene un problema: no es compatible con la firma de un método genérico. Para cada repositorio de @Entity, debe definir su método de búsqueda personalizado ... Con ... Y. Debido a eso, les muestro cómo definir un repositorio genérico

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

} 

Pero, a veces , que no desea que todos los métodos definidos por la interfaz de repositorio genérico. Solución: cree una clase de AbstractRepository que implementará un repositorio ficticio.Spring Framework, por ejemplo, en gran medida el uso de este tipo de patrón Interfaz >> 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."); 
    } 

} 

Así que su GiftCardRepository se puede volver a escribir como (Ver extiende en lugar de implementos) y simplemente anula lo que realmente quiere

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

Ahora exteriorizan nuestra consulta con nombre en una línea múltiple - y legible y mantenible - archivo 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> 

lo tanto, si desea recuperar usted tarjeta de regalo con el Comprador, simplemente llame

Repository<GiftCard, GiftCard, String> giftCardRepository; 

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

Y para recuperar nuestra tarjeta de regalo y sin ninguna relación, simplemente llame

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

o el uso de importación estática

import static packageTo.GiftCardRepository.*; 

Y

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

¡Espero que pueda ser útil para usted!

+0

Muy interesante ... esta es una gran fuente de reflexión. Y trae a colación una pregunta secundaria. ¿Cómo realizaría una consulta que no capta ningún objeto asociado que no sea la GiftCard de destino? –

+0

@ Ben Benson Ver GiftCardRepository (última línea), nombre de archivo de consulta y código adicional en la parte inferior. Si mi respuesta cumple con sus necesidades, márquela como aceptada. Gracias –

2

En JPA por defecto traiga tipo para ManyToOne asociaciones está ansioso (es decir, no perezoso) por lo que podría intentar con:

@ManyToOne(fetch=FetchType.LAZY) 

Entonces En cualquier consulta JPA, la asociación se puede obtener con entusiasmo utilizando left join fetch.

Cuestiones relacionadas