Spring Boot+QueryDSL如何一次性获取含列表字段的DTO数据?
解决方案:使用数据库视图实现一次性关联查询
要一次性获取包含CarDTO列表的WorkerDTO列表、避免反复查询数据库,数据库视图方案完全可行,具体实现步骤如下:
1. 创建数据库视图
首先在数据库中创建视图,将person、car、motor三张表的关联数据整合为扁平结构,确保仅需一次查询就能获取所有需要的字段:
CREATE VIEW worker_car_view AS SELECT p.id AS person_id, p.name AS person_name, c.model AS car_model, m.type AS motor_type FROM person p LEFT JOIN car c ON p.id = c.person_id LEFT JOIN motor m ON c.motor_id = m.id;
使用LEFT JOIN可以保证没有车辆的Worker也能被查询到。
2. 创建视图对应的实体类
创建与视图结构对应的JPA实体类,用于映射视图查询结果:
@Entity @Table(name = "worker_car_view") public class WorkerCarView { @Id @Column(name = "person_id") private Long personId; @Column(name = "person_name") private String personName; @Column(name = "car_model") private String carModel; @Column(name = "motor_type") private String motorType; // 生成无参、全参构造函数,以及getter、setter方法 }
3. 编写Repository接口
创建JPA Repository接口,用于查询视图数据:
public interface WorkerCarViewRepository extends JpaRepository<WorkerCarView, Long> { List<WorkerCarView> findAll(); }
4. 转换为目标DTO结构
在服务层中,将视图返回的扁平数据分组转换为WorkerDTO列表,把同一Worker的车辆数据收集到对应的CarDTO列表中:
@Service public class WorkerService { private final WorkerCarViewRepository viewRepository; // 构造函数注入(推荐替代@Autowired) public WorkerService(WorkerCarViewRepository viewRepository) { this.viewRepository = viewRepository; } public List<WorkerDTO> getWorkerDTOList() { List<WorkerCarView> viewList = viewRepository.findAll(); return viewList.stream() // 按Worker的ID分组 .collect(Collectors.groupingBy(WorkerCarView::getPersonId)) .entrySet().stream() .map(entry -> { WorkerDTO workerDTO = new WorkerDTO(); WorkerCarView firstEntry = entry.getValue().get(0); workerDTO.setName(firstEntry.getPersonName()); // 转换为CarDTO列表 List<CarDTO> carDTOList = entry.getValue().stream() // 过滤无车辆的记录 .filter(view -> view.getCarModel() != null) .map(view -> { CarDTO carDTO = new CarDTO(); carDTO.setModel(view.getCarModel()); MotorDTO motorDTO = new MotorDTO(); motorDTO.setType(view.getMotorType()); carDTO.setMotor(motorDTO); return carDTO; }) .collect(Collectors.toList()); workerDTO.setCars(carDTOList); return workerDTO; }) .collect(Collectors.toList()); } }
补充说明
你之前尝试的@Query("select new WorkerDTO(...)")和QueryDSL Projections.list(...)方法,本质上都会返回扁平的查询结果,最终还是需要手动分组转换DTO结构。而数据库视图方案只是将表关联逻辑转移到数据库层面,同样需要后续的分组转换,但能保证仅执行一次数据库查询,彻底避免反复查询的问题。
内容的提问来源于stack exchange,提问作者Mehmet Fırat ÖZÜN
相关产品推荐
相关产品推荐

