如何通过Criteria API将关联实体属性集合转为DTO的List<String>
问题解答
是否需要关联查询ServicePrefixEntity?
必须关联查询。因为ServiceMobileData的prefixes字段依赖ServicePrefixEntity的validationPrefix属性,该属性属于关联实体,无法直接从ServiceEntity中提取,所以必须通过join关联ServicePrefixEntity才能获取目标数据。
如何在multiselect中处理并存入集合?
有两种实用实现方式,可根据场景选择:
方式1:数据库端聚合(直接通过multiselect构造DTO)
借助数据库聚合函数将validationPrefix聚合为集合,再通过multiselect传入DTO构造函数。注意不同数据库的聚合函数语法有差异:
- PostgreSQL 使用
array_agg - MySQL 使用
group_concat(需在DTO构造函数中分割为List)
示例代码(PostgreSQL环境):
CriteriaBuilder cb = entityManager.getCriteriaBuilder(); CriteriaQuery<ServiceMobileData> query = cb.createQuery(ServiceMobileData.class); Root<ServiceEntity> serviceRoot = query.from(ServiceEntity.class); // 左连接关联,避免过滤掉无prefix的ServiceEntity Join<ServiceEntity, ServicePrefixEntity> prefixJoin = serviceRoot.join("servicePrefixes", JoinType.LEFT); // 按ServiceEntity主键分组,消除一对多关联产生的笛卡尔积重复数据 query.groupBy(serviceRoot.get("id"), serviceRoot.get("name")); // 构造DTO,将聚合后的validationPrefix集合传入 query.multiselect( cb.construct( ServiceMobileData.class, serviceRoot.get("id"), serviceRoot.get("name"), cb.function("array_agg", List.class, prefixJoin.get("validationPrefix")) ) ); List<ServiceMobileData> result = entityManager.createQuery(query).getResultList();
方式2:查询实体后内存转换(兼容性更强)
先查询ServiceEntity并抓取关联的ServicePrefixEntity集合,再在内存中转换为DTO。这种方式不依赖数据库特性,跨数据库兼容性更好:
CriteriaBuilder cb = entityManager.getCriteriaBuilder(); CriteriaQuery<ServiceEntity> query = cb.createQuery(ServiceEntity.class); Root<ServiceEntity> serviceRoot = query.from(ServiceEntity.class); // 抓取关联集合,避免N+1查询 serviceRoot.fetch("servicePrefixes", JoinType.LEFT); // 去重,消除一对多关联产生的重复实体 query.distinct(true); List<ServiceEntity> entities = entityManager.createQuery(query).getResultList(); // 转换为DTO List<ServiceMobileData> dtos = entities.stream() .map(entity -> new ServiceMobileData( entity.getId(), entity.getName(), entity.getServicePrefixes().stream() .map(ServicePrefixEntity::getValidationPrefix) .collect(Collectors.toList()) )) .collect(Collectors.toList());
是否可行?
两种方式均完全可行:
- 方式1适合需要直接通过数据库查询构造DTO、减少内存处理的场景,但需适配不同数据库的聚合函数;
- 方式2兼容性更强,无需依赖数据库特性,适合跨数据库部署的场景,仅需在内存中做简单转换。
内容的提问来源于stack exchange,提问作者Eljah
相关产品推荐
相关产品推荐

