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

标签 hibernate hql criteria lazy-evaluation

我在优化 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;
 }
}

最佳答案

如前所述

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



如果你有这样的映射
@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 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();
     }

}

因此,根据您的用例,您可以创建自己的 find...With...And... 方法。它将负责获取 正是你想要的

但它有一个问题:它不支持泛型方法签名。对于每个 @Entity 存储库,您必须定义您的自定义 find...With...And 方法。因此,我将向您展示如何定义通用存储库
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);

}

但是,有时 ,您不希望由通用 Repository 接口(interface)定义的所有方法。解决方案:创建一个 AbstractRepository 类,该类将实现一个虚拟存储库。例如,Spring 框架 大量使用这种模式接口(interface) >> 抽象接口(interface)
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.*;


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

我希望它对你有用!

关于hibernate - 避免使用 Hibernate Criteria 或 HQL 查询进行二次选择或连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2934753/

相关文章:

java - postgres id 生成和 hibernate 的问题

hibernate - 在 JBoss AS 7 中捕获由 hibernate JPA 生成的最终 SQL 查询

java - 如何为每个别名分配条件限制

java - 标准:搜索两个不同的实体类

带投影的 hibernate where 子句

java - jpa 2.1、Hibernate 4.3.7 和 C3P0 的管道损坏

hibernate - 使用 MetadataBuilderContributor 添加对 "match against" hibernate 的支持

sql - SQL-选择在其他表格中至少出现一次的ID

hibernate - 如何使用实体查询框架对以下查询进行建模

java.lang.ClassCastException : org. hibernate.hql.ast.tree.SqlNode 无法转换为 org.hibernate.hql.ast.tree.FromReferenceNode