Avoiding secondary selects or joins with Hibernate Criteria or HQL query

前端 未结 2 1297
粉色の甜心
粉色の甜心 2020-12-06 03:30

I am having trouble optimizing Hibernate queries to avoid performing joins or secondary selects.

When a Hibernate query is performed (criteria or hql), such as the f

相关标签:
2条回答
  • 2020-12-06 03:48

    As said

    I want a solution that I can control what is lazily loaded when I perform the query

    If you have a mapping like this one

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

    Any *ToOne relationship, such as @OneToOne and @ManyToOne, is, by default, FetchType.EAGER which means it will be always fetched. But, it could not be what you want. What you say as I can control what is lazily loaded can be translated as Fetching Strategy. POJO in Action book supports a pattern like this one (Notice method signature)

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

    So based on your use case, you can create your own find...With...And... method. It will take care of fetching just what you want

    But it has a problem: It does not support a generic method signature. For each @Entity repository, you have to define your custom find...With...And method. Because of that, i show you how i define a generic 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);
    
    }
    

    But, sometimes, you do not want all of methods defined by generic Repository interface. Solution: create an AbstractRepository class which will implement a dummy repository. Spring Framework, for instance, 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.");
        }
    
    }
    

    So your GiftCardRepository can be re-written as (See extends instead of implements) and 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 {}
    }
    

    Now we externalize our named query in a multiline - and readable and maintainable - xml file

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

    So if you want to retrieve you GiftCard with Buyer, just call

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

    And to retrieve our GiftCard without no relationship, just call

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

    or use import static

    import static packageTo.GiftCardRepository.*;
    

    And

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

    I hope it can be useful to you!

    0 讨论(0)
  • 2020-12-06 03:48

    In JPA the default fetch type for ManyToOne associations is eager (i.e. non-lazy) so could you try with:

    @ManyToOne(fetch=FetchType.LAZY)
    

    Then, in any JPA query the association can be eagerly fetched using left join fetch.

    0 讨论(0)
提交回复
热议问题