如何用@Query注解编写查询填充EmployeeDetails实例
如何使用@Query注解将多表数据映射到自定义模型EmployeeDetails?
问题背景
我需要把my_employee和my_address表的数据映射到自定义的EmployeeDetails模型类中。现有实体类、自定义模型以及仓库接口如下:
Employee实体类
@Entity @Table(name = "my_employee") public class Employee { @Id @GeneratedValue(strategy = GenerationType.AUTO) private int id; private String name; @OneToMany(cascade = CascadeType.ALL) @JoinTable(name = "emp_address_mapping", joinColumns = @JoinColumn(name = "emp_id"), inverseJoinColumns = @JoinColumn(name = "address_id")) private List<Address> addresses = new ArrayList<Address>(); // 省略其他getter/setter及辅助方法 }
Address实体类
@Entity @Table(name = "my_address") public class Address { @Id @GeneratedValue(strategy = GenerationType.AUTO) private int id; private String country; // 省略其他getter/setter及辅助方法 }
自定义模型EmployeeDetails
public class EmployeeDetails { private int empId; private String name; private String country; // 省略getter/setter及辅助方法 }
仓库接口
public interface EmployeeRepository extends CrudRepository<Employee, Integer> { @Query("SELECT new com.sample.app.model.EmployeeDetails......") List<EmployeeDetails> getEmployeeDetails(); }
请问该如何编写@Query注解里的JPQL语句,来填充所有EmployeeDetails实例?
解决方案
要实现这个需求,核心是利用JPQL的构造函数表达式,同时正确关联两个实体类的数据,下面分步骤说明:
1. 先给EmployeeDetails添加对应构造函数
JPQL实例化自定义对象必须依赖显式的构造函数,所以你需要给EmployeeDetails添加一个参数顺序为empId、name、country的构造函数,示例如下:
public class EmployeeDetails { private int empId; private String name; private String country; // 必须添加这个构造函数,参数顺序要和JPQL查询的字段顺序一致 public EmployeeDetails(int empId, String name, String country) { this.empId = empId; this.name = name; this.country = country; } // 省略getter/setter及辅助方法 }
2. 编写完整的@Query语句
因为Employee和Address是一对多关联,我们需要通过关联属性addresses来关联查询,这里推荐用LEFT JOIN保证没有地址的员工也能被查询到(如果只需要有地址的员工,换成JOIN即可):
public interface EmployeeRepository extends CrudRepository<Employee, Integer> { @Query("SELECT new com.sample.app.model.EmployeeDetails(e.id, e.name, a.country) " + "FROM Employee e " + "LEFT JOIN e.addresses a") List<EmployeeDetails> getEmployeeDetails(); }
关键说明
LEFT JOIN e.addresses a:通过Employee实体的addresses集合属性关联Address实体,LEFT JOIN会保留所有员工记录,没有地址的员工对应的country会是null。new com.sample.app.model.EmployeeDetails(...):必须使用类的全限定名,传入的字段顺序要和构造函数的参数顺序完全匹配。- 注意:如果一个员工有多个地址,这条查询会返回多条该员工的记录(每个地址对应一条)。如果需要每个员工只返回一条记录,可以按需调整:
场景1:取员工的第一个地址(按地址ID排序)
@Query("SELECT new com.sample.app.model.EmployeeDetails(e.id, e.name, " + "(SELECT a.country FROM Address a WHERE a MEMBER OF e.addresses ORDER BY a.id ASC LIMIT 1)) " + "FROM Employee e") List<EmployeeDetails> getEmployeeDetails();
场景2:分组聚合(比如取员工的任意一个地址国家)
@Query("SELECT new com.sample.app.model.EmployeeDetails(e.id, e.name, MAX(a.country)) " + "FROM Employee e " + "LEFT JOIN e.addresses a " + "GROUP BY e.id, e.name") List<EmployeeDetails> getEmployeeDetails();
内容的提问来源于stack exchange,提问作者Hari Krishna
相关产品推荐
相关产品推荐

