简体   繁体   English

在 JPA 查询中从 GROUP BY 创建 Map 对象

[英]Create Map object from GROUP BY in JPA Query

I have 3 tables for simplicity:为简单起见,我有 3 个表:

  • COMPANY公司
  • EMPLOYEE员工
  • DEVICES设备

COMPANY has EMPLOYEES and each EMPLOYEE has a number of devices assigned to them.公司有员工,每个员工都有许多分配给他们的设备。 I am interested in a list of companyIds connected to a list of device names they have assigned to employees.我对连接到他们分配给员工的设备名称列表的 companyId 列表感兴趣。

Example Java object:示例 Java 对象:

@Getter
@Setter
@AllArgsConstructor
public class CompanyDevices {
  private Long companyId;
  private List<String> deviceNames;
}

Or even better a Map would be great: Map<Long, List where Long would be companyId and List would be deviceNames;或者甚至更好的 Map 会很棒:Map<Long, List 其中 Long 将是 companyId,List 将是 deviceNames;

Is it even possible to create JPA Query, join all those tables and return list of custom objects or a map using @Query inside JPARepository?甚至可以在 JPARepository 中使用 @Query 创建 JPA 查询、连接所有这些表并返回自定义对象列表或映射吗?

What I have right now is slightly different class:我现在所拥有的课程略有不同:

@Getter
@Setter
@AllArgsConstructor
public class CompanyDevice {
  private Long companyId;
  private String deviceName;
}
public interface SomeRepository extends JpaRepository<SomeEntity, Long> {
    
    @Query("SELECT " +
            "new path.to.CompanyDevice(C.id, D.deviceName) " +
            "FROM Company C " +
            "JOIN C.employees E " +
            "JOIN E.devices D " +
            "WHERE C.id IN :companyIds"
    )
    List<CompanyDevice> findDevicesForCompanies(@Param("companyIds") List<Long> companyIds);

}

Because of this I need to manualy later group by and create a map.因此,我需要稍后手动分组并创建地图。 So what I would like to do is something like this but I just can't find enough information or it may not even be possible(sorry for pseudo code):所以我想做的是这样的事情,但我找不到足够的信息,或者甚至可能不可能(对不起,伪代码):

public interface SomeRepository extends JpaRepository<SomeEntity, Long> {
    
    @Query("SELECT " +
            "<create map where key is C.id and value is <list of D.deviceName from grouping>>" +
            "FROM Company C " +
            "JOIN C.employees E " +
            "JOIN E.devices D " +
            "WHERE C.id IN :companyIds" +
            "GROUP BY C.id"
    )
    Map<Long, List<String>> findDevicesForCompanies(@Param("companyIds") List<Long> companyIds);

}

What you need is an intermediate function like this:您需要的是这样的中间函数:

public interface SomeRepository extends JpaRepository<SomeEntity, Long> {
    
    @Query("SELECT " +
            "c.id, d.deviceName " +
            "FROM Company c " +
            "JOIN c.employees e " +
            "JOIN e.devices d " +
            "WHERE c.id IN :companyIds"
    )
    List<Object[]> findDevicesForCompanies0(@Param("companyIds") List<Long> companyIds);

    default Map<Long, List<String>> findDevicesForCompanies(List<Long> companyIds) {
        return findDevicesForCompanies(companyIds).stream()
            .collect(
                Collectors.groupingBy(
                    o -> (Long) o[0],
                    Collectors.mapping( o -> (String) o[1], Collectors.toList() )
                )
            );
    }

}

Another nice way to solve this would be to useBlaze-Persistence Entity Views which I think this is a perfect use case for.解决这个问题的另一个好方法是使用Blaze-Persistence Entity Views ,我认为这是一个完美的用例。

I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids.我创建了该库以允许在 JPA 模型和自定义接口或抽象类定义的模型之间轻松映射,例如类固醇上的 Spring Data Projections。 The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.这个想法是您按照自己喜欢的方式定义目标结构(域模型),并通过 JPQL 表达式将属性(getter)映射到实体模型。

A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:使用 Blaze-Persistence Entity-Views 的用例的 DTO 模型可能如下所示:

@EntityView(Company.class)
public interface CompanyDevices {
    @IdMapping
    Long getId();
    @Mapping("employees.devices.deviceName")
    Set<String> getDeviceNames();
}

Querying is a matter of applying the entity view to a query, the simplest being just a query by id.查询是将实体视图应用于查询的问题,最简单的只是按 id 查询。

CompanyDevices a = entityViewManager.find(entityManager, CompanyDevices.class, id);

The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features Spring Data 集成允许您几乎像 Spring Data Projections 一样使用它: https : //persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

Page<CompanyDevices> findAll(Pageable pageable);

Or in your particular case或者在您的特定情况下

List<CompanyDevices> findByIdIn(List<Long> companyIds);

The best part is, it will only fetch the state that is actually necessary!最好的部分是,它只会获取实际需要的状态!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM