Hibernate执行太多查询

时间:2013-06-13 18:50:33

标签: java database hibernate orm

我相信我误解了如何选择和渴望工作;我的目标是在遇到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 condition INNER JOIN Area ON condition
  2. SELECT * FROM Employee WHERE employeeId IN(子查询1)
  3. SELECT * FROM Area WHERE areaId IN(子查询2)
  4. 事实上,对于前面提到的测试数据的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
                    )
                )
    

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

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

2 个答案:

答案 0 :(得分:5)

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

@Id
@ManyToOne
public Employee employee;

@Id
@ManyToOne
public Area area;

我使用了@Id导致了抛出的StackOverflowError异常。使用以下查询,在Employee上获取EAGER和@Fetch JOIN的OneToMany,以及在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。

答案 1 :(得分:3)

使用JOIN策略并将Area懒惰地链接到EmployeeArea,而Employee则急切地加载EmployeeAreas。当Employee加载EmployeeArea时,hibernate会话将填充EmployeeArea个对象。然后,如果您浏览Employee.EmployeeArea.Area.EmloyeeArea,将无法从数据库中获取任何内容,因为我们在会话缓存中已经有EmployeeArea