【问题标题】:Hibernate Select Queries OptimizationHibernate 选择查询优化
【发布时间】:2016-01-23 02:17:25
【问题描述】:

我正在寻找优化 Hibernate select 查询的最佳方法。

这是一个基本的例子:

BDD 模型 1 个客户 -> n 个合同 -> n 个选项

请求客户端“xxxx”的所有数据的最简单方式是这样的:

final Query hqlQuery = jdbcTemplate.createHQLQuery("from Client cli left join fetch cli.contracts con left join fetch con.options where cli.id=:idClient");
hqlQuery .setString("idClient", "xxxx");

Client client = (Client) hqlQuery.uniqueResult();

有时这是不可能的,因为要返回两个大量数据。

所以,我拆分了请求,有点像:

// Query 1
final Query hqlQueryClient = jdbcTemplate.createHQLQuery("from Client cli left join fetch cli.contracts where cli.id=:clientId");
hqlQueryClient.setString("clientId", "xxxx");

Client client = (Client) hqlQueryClient.uniqueResult();

List<String> listContractIds = new ArrayList<String>();

for (Contract contract : client.getContracts()) {
  listContractIds.add(contract.getId());
}

// Query 2
final Query hqlQueryOptions = jdbcTemplate.createHQLQuery("from Option opt where opt.contract.id in(:contractIds)");
hqlQueryOptions.setParameterList("contractIds", listContractIds);

List<Option> options = hqlQueryClient.list();

但是,用第二种方式,我不能在client对象中“注入”options,所以我必须在我的代码中处理clientoptions对象,并在options中搜索列出与我正在使用的合同相对应的那些。

有没有办法用第二次请求的值来完成 Hibernate 对象(在我的示例中为客户端)?

感谢您的帮助。

PS:问不清楚,我是法国人:)

【问题讨论】:

    标签: java hibernate select hql


    【解决方案1】:

    &lt;rant&gt; 我通常讨厌hibernate,因为它非常浪费时间,而且如果手动编写SQL,你只运行少数几个查询,它似乎也运行了数百个查询 &lt;\rant&gt;

    如果强制使用休眠,我可能会使用 3 个类似的查询

    • from Options as o join fetch o.contract as co join fetch co.client as cl where cl = :client
    • from Contracts as co join fetch co.client as cl where cl = :client
    • from Client where clientId = :clientId

    然后我会将它们全部放入适当的 Map&lt;Long, List&gt; 映射中并在 java 中进行连接。

    【讨论】:

    • 没有办法将3次查询的结果直接注入到主对象中(而不是处理Map)?
    • 如果您只从选项 -> 合同 -> 客户端导航,您可能对第一个查询没问题。如果您选择其他方式(客户端 -> 合同 -> 选项),您将遇到 n+1 selects 问题
    • 你是对的,但我无法在我的代码中从选项 -> 合同 -> 客户端导航,我正在做另一种方式(客户端 -> 合同 -> 选项)。为了避免 n+1 选择问题,我从不打电话给contrat.getOptions()。相反,我以编程方式过滤 Options 查询的结果(通过 contrat id),但它不像调用 contrat.getOptions() 那样干净,这是这篇文章的主题:我怎样才能做得更好:)
    • 我回答了帖子的主题:3 个查询 + 地图 :)
    • 不完全是。问题是:是否可以请求您建议的方式,然后将结果注入顶级对象(例如 Client )以便能够直观地迭代 Client children ?所以我猜你的回答是否定的,这是不可能的:(
    【解决方案2】:

    使用 Hibernate 的好处是 ORM。您可以将您的类设置为实体。因此,您不再需要担心简单的查询。只需使用JPA 完成该任务。实体可能看起来像这样:

    @Entity
    public class Client implements Serializable {
        private Long id;
        private ArrayList<Contract> contracts;
        // more attributes here
        @Id @GeneratedValue(strategy = GenerationType.AUTO)
        public Long getId() {
            return id;
        }
    
        @OneToMany
        public ArrayList<Contract> getContracts() {
            return contracts;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public void setContracts(ArrayList<Contract> contracts) {
            this.contracts = contracts;
        }
    }
    
    @Entity
    public class Contract implements Serializable {
        private Long id;
        private List<Option> options;
        // more attributes here
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        public Long getId() {
            return id;
        }
    
        @OneToMany
        public List<Option> getOptions() {
            return options;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public void setOptions(List<Option> options) {
            this.options = options;
        }
    }
    

    等等……

    【讨论】:

    • 您好,您的回答断章取义,我的问题不是关于映射,而是如何有效地查询
    • 来自您想要强制使用 Hibernate 编写 SQLQ 的 POV。这完全脱离了上下文,因为您甚至不了解 Hibernate 的用途。所以玩得开心
    【解决方案3】:

    首先:您有多少数据导致第一个查询不起作用? 如果您的结果有这么多行并且您想优化此查询,请检查您是否真的需要从 db 获得的所有数据。也许您应该对其他更扁平的对象进行投影。

    如果您不使用 Java 处理数据并仅将其传递给前端,请考虑对结果进行分页。

    【讨论】:

    • 潜在 1 个客户端 -> 50 个合约 -> 50 个选项 假设每个客户端,每个合约和每个选项都有 20 列,这意味着 Hibernate 必须处理 50*20*50*20 = 1 000 000 列。
    • 你真的需要所有这些列吗?
    • 不是全部,但至少 50%。
    • 所以首先你应该只选择你需要的列。其次是你如何处理数据——你用 Java 处理它吗?
    • 您对列的看法是对的,但我如何从 3 个对象级别(客户、合同、选项)中选择我想要的这些,并将结果映射到我的客户对象上?关于处理,是的,我用 Java 处理这些结果。
    猜你喜欢
    • 1970-01-01
    • 2011-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-20
    • 1970-01-01
    相关资源
    最近更新 更多