JPA比较新,所以我有一种架构问题。 假设我有表EMPLOYEE和DEPARTMENT有多对一关系(即许多员工为一个部门工作):
EMPLOYEE
EMPLOYEE_ID
EMPLOYEE_NAME
DEPARTMENT_ID
DEPARTMENT
DEPARTMENT_ID
DEPARTMENT_NAME
所以我可以为Employee和Department定义合适的实体,没有问题。但是,在一个视图中,我想显示那些为该部门工作的员工人数的部门列表,如下所示:
SELECT D.DEPARTMENT_NAME,
(SELECT COUNT(*) FROM EMPLOYEE E WHERE E.DEPARTMENT_ID = D.DEPARTMENT_ID) NUMBER_OF_EMPLOYEES
FROM DEPARTMENT D
我只是不确定使用JPA实现此目的的正确策略是什么... 我不想总是为Department实体获取员工数量,因为在需要时只有一个视图。
看起来Hibernate的@Formula是一种可能的方法,但是它不符合JPA标准。
答案 0 :(得分:4)
您可以使用“新”语法在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
或者如果您只是选择计数(*),您只需将查询结果转换为数字。
或者,要在没有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
为您提供带有DEPARTMENT_ID的List<Object[]>
并计入其中。
然后使用第二个列表更新第一个列表中的瞬态计数属性。 (您可以尝试选择Map以使查找更容易,但我认为这是一个Hibernate功能。)
答案 1 :(得分:3)
选项1:我建议这样做,因为你不喜欢MattR建议的构造函数路径。您多次提到“视图”一词,我知道您正在讨论用户的视图,但为什么不在数据库中设置包含计算列的视图,然后创建一个映射到计算机的只读实体列?
选项2:回复您关于不想创建视图的评论。您可以创建一个容纳实体和计算列的容器对象,就像MattR建议的那样,您在select中使用new。类似的东西:
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来获取您感兴趣的属性。
答案 2 :(得分:1)
您可以在实体中创建一个成员作为附加列,然后在查询中使用别名引用它。 @Column
注释中的列名称必须与别名匹配。
说,对于原始查询,您可以添加countEmployees
成员,如下所示。还要添加insertable=false
和updatable=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存储库时也适用。