【问题标题】:Hibernate n+1 select with multiple table hierarchy具有多个表层次结构的休眠 n+1 选择
【发布时间】:2016-10-26 01:42:04
【问题描述】:

我有下一个班级结构

@Entity
@Table(name = "Company")
public class Company {
   @Id
   @GeneratedValue
   private Long id;

   @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
   @JoinColumn(name = "CompanyId")
   @Fetch(FetchMode.SUBSELECT)
   private Set<Departement> departements;
}

@Entity
@Table(name = "Departement")
public class Departement {
   @Id
   @GeneratedValue
   private Long id;

   @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
   @JoinColumn(name = "DepartementId")
   @Fetch(FetchMode.SUBSELECT)
   private Set<Employee> employees;
}

@Entity
@Table(name = "Employee")
public class Employee {
   @Id
   @GeneratedValue
   private Long id;

  // other fields and methods
}

在应用程序启动时,我需要获取所有具有初始化内部集合的公司。我的数据库足够大(Company 表中有 1,5 M 行)。我需要解决 n+1 选择问题以加快数据检索。提取连接的解决方案在我的情况下不起作用,因为生成的 sql 查询返回大量数据集,即使我使用这样的滚动

Query query = session.createQuery(query);
query.setReadOnly(true);
// MIN_VALUE gives hint to JDBC driver to stream results
query.setFetchSize(Integer.MIN_VALUE);
ScrollableResults results = query.scroll(ScrollMode.FORWARD_ONLY);

它仍然会消耗我所有的 RAM,因为我无法刷新会话或驱逐检索到的实体。

另一种方法是使用子选择,但当我这样做时

@SuppressWarnings("unchecked")
List<Company> companies = session.createQuery("from Company").list();

for (Company c : companies) {
    for (Departement d : c.getDepartements()) {
        d.getEmployees();
    }
}

hibernate 只生成 2 个查询:一个用于 Company 表

select ... from Company company

另一个用于部门表

select ... from Departement departemen0_ 
where departemen0_.CompanyId in (select company0_.id from Company company0_)

我仍然需要分别从部门类初始化员工集合。

有没有办法用子选择检索所有 3 个表?或者可能有另一种方法来检索具有给定结构的大量数据?

【问题讨论】:

  • 通常你在类的顶部添加@NamedEntityGraph(name = "Company.withDepartment", attributeNodes = @NamedAttributeNode("department")) Company@NamedEntityGraph(name = "Department.withEmployee", attributeNodes = @NamedAttributeNode("employee")) 在类的顶部下方Department
  • 然后您将以下代码添加到您的命名查询(更好地使用命名查询):{yourEntityManager}.{yourNamedQuery}.setHint("javax.persistence.loadgraph", getEntityManager().createEntityGraph("Company.withDepartment")) .getResultList();{yourEntityManager}.{yourNamedQuery}.setHint("javax.persistence.loadgraph", getEntityManager().createEntityGraph("Department.withEmployee")) .getResultList();
  • - 对于id,我会使用简单的long 而不是Long。
  • - 对于departments,我会写:@OneToMany @JoinColumn(name = "companyId") private Set&lt;Departement&gt; departements;
  • - 对于employees,我会写@OneToMany @JoinColumn(name = "departementId") private Set&lt;Employee&gt; employees;

标签: java hibernate jpa fetch jpql


【解决方案1】:

您的代码仅生成两个查询(针对 Company 和 Department),因为您的 Employee 查询代码(第三个也是所需的)没有被延迟获取。所以,只需改变:

d.getEmployees();

收件人:

d.getEmployees().size();

如果您正在处理如此大量的数据,SUBSELECT 对您来说似乎是 best strategy

  • JOIN:避免了 N+1 查询的主要问题,但它可能会检索到重复的数据。
  • SUBSELECT:也避免了 N+1 并且不复制数据,但它将关联类型的所有实体加载到内存中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多