4

我相信我误解了 subselect 和 eager 的工作原理;我的目标是在遇到 N+1 问题时提高性能

编辑我想知道只使用 create SQL 查询方法并自己创建对象是否会更快,尽管我希望 hibernate 能与性能相提并论。在下面的示例中,我可以在单个查询中提取所有所需的数据,那么为什么 hibernate 对每个数据都进行单独的查询呢?

我创建了以下测试用例来突出我的问题,请原谅这个模型的粗鲁。

@Entity
@Table(name = "Area")
public class Area implements Serializable
{
    @Id
    @GeneratedValue(generator = "areaId" )
    @GenericGenerator(name = "areaId", strategy = "uuid2")
    public String areaId;

    @OneToMany(mappedBy = "area", fetch=FetchType.EAGER)
    @Fetch(FetchMode.SUBSELECT)
    public Set<EmployeeArea> employeeAreas = new HashSet<EmployeeArea>();
}

@Entity
@Table(name = "Employee")
public class Employee implements Serializable
{
    @Id
    @GeneratedValue(generator = "employeeId" )
    @GenericGenerator(name = "employeeId", strategy = "uuid2")
    public String employeeId;

    @OneToMany(mappedBy = "employee", fetch=FetchType.EAGER)
    @Fetch(FetchMode.SUBSELECT)
    public Set<EmployeeArea> employeeAreas = new HashSet<EmployeeArea>();
}

@Entity
@Table(name = "EmployeeArea")
public class EmployeeArea implements Serializable
{
    @Id
    @GeneratedValue(generator = "employeeAreaId" )
    @GenericGenerator(name = "employeeAreaId", strategy = "uuid2")
    public String employeeAreaId;

    @Id
    @ManyToOne
    public Employee employee;

    @Id
    @ManyToOne
    public Area area;
}

然后我填充了一些示例测试数据:-

Employee employee = new Employee();
Area area = new Area();

EmployeeArea employeeArea = new EmployeeArea();
employeeArea.area = area;
employeeArea.employee = employee;

session.save(employee);
session.save(area);
session.save(employeeArea);

这可以运行几次以提供一些数据。

然后我执行以下操作: -

session.createQuery("FROM Employee e INNER JOIN e.employeeAreas ea INNER JOIN ea.area").list();

我进行 JOIN 的原因是我可以执行专家搜索。我正在查看标准,但似乎它不允许我用 WHERE 做所有我能做的事情

我希望它最多会执行 3 个查询和 2 个子查询。

  1. SELECT * FROM Employee INNER JOIN EmployeeArea ON 条件 INNER JOIN Area ON 条件
  2. SELECT * FROM Employee WHERE employeeId IN(子查询 1)
  3. SELECT * FROM Area WHERE areaId IN(子查询 2)

事实上,对于上述测试数据的 6 个输入,我似乎得到了 6 个员工的选择,6 个区域的选择,这看起来像是我对“1”的假设查询。然后是两个看起来完全错误的更大查询:-

select
    employeear0_.employee_employeeId as employee2_3_2_,
    employeear0_.employeeAreaId as employee1_4_2_,
    employeear0_.employee_employeeId as employee2_4_2_,
    employeear0_.area_areaId as area3_4_2_,
    employeear0_.employeeAreaId as employee1_4_1_,
    employeear0_.employee_employeeId as employee2_4_1_,
    employeear0_.area_areaId as area3_4_1_,
    area1_.areaId as areaId1_0_0_ 
from
    EmployeeArea employeear0_ 
inner join
    Area area1_ 
        on employeear0_.area_areaId=area1_.areaId 
where
    employeear0_.employee_employeeId in (
        select
            employee1_.employeeId 
        from
            EmployeeArea employeear0_ 
        inner join
            Employee employee1_ 
                on employeear0_.employee_employeeId=employee1_.employeeId 
        where
            employeear0_.area_areaId in (
                select
                    area2_.areaId 
                from
                    Employee employee0_ 
                inner join
                    EmployeeArea employeear1_ 
                        on employee0_.employeeId=employeear1_.employee_employeeId 
                inner join
                    Area area2_ 
                        on employeear1_.area_areaId=area2_.areaId
                )
            )

然后是一个非常相似的区域。

我的目标是能够使用返回列表中的每个员工对象来识别工作的区域。每个实体中会有更多的字段,但是这个测试用例已经被简化了。

4

2 回答 2

5

我解决了这个问题;这是我的联接表的问题。请参阅以下内容: -

@Id
@ManyToOne
public Employee employee;

@Id
@ManyToOne
public Area area;

我使用了@Id,这导致了 StackOverflowError 异常被抛出。使用以下查询,在 Employee 上使用 OneToMany 获取 EAGER 和 @Fetch JOIN 以及在 Area 上使用 LAZY 和 @Fetch SELECT 的 OneToMany 获取,然后我可以执行以下查询:-

List<Employee> employees = session.createQuery("FROM Employee e INNER JOIN FETCH e.employeeAreas ea INNER JOIN FETCH ea.area").list();

同时能够在连接表列之一上使用 WHERE。

于 2013-06-14T22:04:11.553 回答
3

懒惰地使用JOIN策略和链接,而Area急切地加载. 加载时,休眠会话将填充对象。然后,如果您浏览什么都不会从数据库中获取,因为我们已经在会话缓存中。EmployeeAreaEmployeeEmployeeAreasEmployeeEmployeeAreaEmployeeAreaEmployee.EmployeeArea.Area.EmloyeeAreaEmployeeArea

于 2013-06-13T19:26:23.933 回答