首页 文章

避免使用Hibernate Criteria或HQL查询进行二次选择或连接

提问于
浏览
5

我在优化Hibernate查询时遇到问题,以避免执行连接或二次选择 .

执行Hibernate查询(条件或hql)时,如下所示:

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

...和where子句检查不需要与其他表连接的属性...但是Hibernate仍然与其他表执行完全连接(或者根据我设置fetchMode的方式进行二次选择) .

有问题的对象(GiftCard)有一些ManyToOne关联,在这种情况下我宁愿懒洋洋地加载(但不一定是所有情况) . 我想要一个解决方案,我可以控制在执行查询时延迟加载的内容 .

这就是GiftCard实体的样子:

@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 回答

  • 3

    如上所述

    我想要一个解决方案,我可以控制执行查询时延迟加载的内容

    如果您有这样的映射

    @Entity
    public class GiftCard implements Serializable {
    
        private User buyer;
    
        @ManyToOne
        @JoinColumn(name="buyerUserId")
        public User getBuyer() {
            return this.buyer;
        }
    }
    

    任何* ToOne关系,例如@OneToOne和@ManyToOne,默认情况下都是FetchType.EAGER,这意味着它将始终被提取 . 但是,它可能不是你想要的 . 你说什么,我可以控制什么是懒惰加载可以翻译为 Fetching Strategy . POJO in Action book支持这样的模式(通知方法签名)

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

    因此,根据您的用例,您可以创建自己的查找...使用...和...方法 . 它会照顾 just what you want

    但它有一个问题:它不支持通用方法签名 . 对于每个@Entity存储库,您必须定义自定义查找...使用...和方法 . 因此,我向您展示了如何定义通用存储库

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

    但是, sometimes ,您不希望通用Repository接口定义的所有方法 . 解决方案:创建一个将实现虚拟存储库的AbstractRepository类 . 例如,Spring Framework, heavily use this kind of pattern Interface >> 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可以重写为(参见extends而不是implements)和 just overrides what you really want

    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>
    

    因此,如果您想通过买家取回GiftCard,请致电

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

    要在没有任何关系的情况下检索我们的GiftCard,只需致电

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

    或使用import static

    import static packageTo.GiftCardRepository.*;
    

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

    我希望它对你有用!

  • 2

    在JPA中, ManyToOne 关联的默认提取类型是急切的(即非惰性),因此您可以尝试:

    @ManyToOne(fetch=FetchType.LAZY)
    

    然后,在任何JPA查询中,可以使用 left join fetch 急切地获取关联 .

相关问题