6

JPA 相对较新,所以我有一种架构问题。假设我有具有多对一关系的表 EMPLOYEE 和 DEPARTMENT(即许多员工为一个部门工作):

EMPLOYEE
  EMPLOYEE_ID
  EMPLOYEE_NAME
  DEPARTMENT_ID 

DEPARTMENT
  DEPARTMENT_ID
  DEPARTMENT_NAME

所以我可以为员工和部门定义适当的实体,没有问题。但是,在一个视图中,我想显示为该部门工作的员工人数的部门列表,如下所示:

SELECT D.DEPARTMENT_NAME, 
       (SELECT COUNT(*) FROM EMPLOYEE E WHERE E.DEPARTMENT_ID = D.DEPARTMENT_ID) NUMBER_OF_EMPLOYEES
FROM DEPARTMENT D

我只是不确定使用 JPA 完成此任务的正确策略是什么……我不想总是为部门实体获取员工人数,因为在需要时只有一个视图。

看起来 Hibernate 的 @Formula 将是一种可能的方法,但是 afaik 它不符合 JPA 标准。

4

3 回答 3

4

您可以使用“new”语法在 QL 中创建任何对象——您的类只需要一个构造函数,该构造函数接受查询返回的值。

例如,对于像 DepartmentEmployeeCount 这样的类,具有构造函数:

public DepartmentEmployeeCount(String departmentName, Integer employeeCount)

你可以使用 QL 之类的东西:

SELECT NEW DepartmentEmployeeCount(D.DEPARTMENT_NAME, count(E.id)) from Department D left join D.employees E GROUP BY D.DEPARTMENT_NAME

或者,如果您只是选择 count(*),您可以简单地将查询结果转换为数字。

或者,要在没有 DepartmentEmployeeCount 类的情况下执行相同操作,您可以省略 NEW,因此:

SELECT D.DEPARTMENT_NAME, count(E.id)    

这将返回一个List<Object[]>其中每个列表项是 2 个元素的数组,departmentName 和 count。

要在评论中回答您后面的问题,要填充部门的所有字段以及临时的employeeCount 字段,一个建议是进行 2 次查询。这仍然比您的原始查询(每个员工计数的子选择)更有效。

所以一个查询来阅读部门

SELECT D from Department D

给你一个List<Department>

然后第二个查询返回一个临时数组:

SELECT D.DEPARTMENT_ID, count(E.id) from Department D left join D.employees E GROUP BY D.DEPARTMENT_ID

给你一个List<Object[]>DEPARTMENT_ID 并计算在内。

然后使用第二个列表更新第一个列表上的瞬态计数属性。(您可以尝试选择 Map 以使此查找更容易,但我认为这是 Hibernate 功能)。

于 2012-07-09T23:25:24.290 回答
4

选项 1:我建议这样做是因为您不喜欢 MattR 建议的构造函数路线。您多次提到“视图”这个词,我知道您在向用户谈论视图,但为什么不在您的数据库中设置一个包含计算列的视图,然后创建一个映射到计算列的只读实体列?

选项 2:回应您关于不想创建视图的评论。您可以创建一个包含实体和计算列的容器对象,然后就像 MattR 建议的那样,您在选择中使用新的。就像是:

public class DepartmentInfo {
    private Department department;

    // this might have to be long or something
    // just see what construct JPA tries to call
    private int employeeCount;

    public DepartmentInfo( Department d, int count ) { 
        department = d;
        employeeCount = count;
    }
    // getters and setters here
}

然后你的选择变成

SELECT new my.package.DepartmentInfo( D, 
       (SELECT COUNT(*) FROM EMPLOYEE E WHERE E.DEPARTMENT_ID = D.DEPARTMENT_ID))
FROM DEPARTMENT D

有了它,您可以使用 DepartmentInfo 来获取您感兴趣的属性。

于 2012-07-10T04:53:42.127 回答
1

您可以在实体中创建一个成员作为附加列,然后在查询中使用别名引用它。@Column注释中的列名必须与别名匹配。

说,对于您的原始查询,您可以添加一个countEmployees成员,如下所示。还要添加insertable=falseupdatable=false因此实体管理器不会尝试将其包含在插入或更新语句中:

public class Department {

    @Column(name="DEPARTMENT_ID")
    Long departmentId;

    @Column(name="DEPARTMENT_NAME")
    String departmentName;

    @Column(name="countEmployees", insertable=false, updatable=false)
    Long countEmployees;

    //accessors omitted

}

你的查询:

SELECT D.DEPARTMENT_NAME, 
(SELECT COUNT(*) FROM EMPLOYEE E WHERE E.DEPARTMENT_ID = D.DEPARTMENT_ID) AS countEmployees
FROM DEPARTMENT D

这也适用于使用 Spring Data Jpa Repositories 时。

于 2016-07-05T00:17:11.457 回答