51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

Spring Data JPA Repository 返回 Map

1、概览 {#1概览}

Spring JPA 为与数据库交互提供了非常灵活方便的 API。而且,还可以对其进行定制,以返回其他数据结构类型的返回值。

使用 Map 作为 JPA Repository 方法的返回类型有助于在服务和数据库之间创建更直接的交互。本文将带你了解如何在 Spring Data JPA Repository 接口的方法中返回 Map

2、手动实现 {#2手动实现}

当框架不提供某些功能时,最明显的解决方法就是自己实现。

2.1、List {#21list}

可以把返回的 List 映射为 Map。通过 Stream API 只用一行代码就能实现:

default Map<Long, User> findAllAsMapUsingCollection() {
    return findAll().stream()
      .collect(Collectors.toMap(User::getId, Function.identity()));
}

2.2、Stream {#22stream}

Repository 接口方法可以直接返回 Stream

@Query("select u from User u")
Stream<User> findAllAsStream();

之后,实现一个自定义方法,将结果映射到需要的数据结构中:

@Transactional
default Map<Long, User> findAllAsMapUsingStream() {
    return findAllAsStream()
      .collect(Collectors.toMap(User::getId, Function.identity()));
}

返回 Stream 的 Repository 方法应在事务中调用。所以,直接在 default 方法中添加 @Transactional 注解。

2.3、Streamable {#23streamable}

这与之前的方法类似。唯一的变化是返回 Streamable。先定义一个返回 Streamable 的方法:

@Query("select u from User u")
Streamable<User> findAllAsStreamable();

然后,在 default 方法中对结果进行映射:

default Map<Long, User> findAllAsMapUsingStreamable() {
    return findAllAsStreamable().stream()
      .collect(Collectors.toMap(User::getId, Function.identity()));
}

3、自定义 Streamable 封装 {#3自定义-streamable-封装}

前面的例子展示了非常简单的解决方案。但是,假设我们有多个不同的操作或数据结构,需要将结果映射到这些操作或数据结构上。在这种情况下,代码中可能到处都是重复的 Mapper 映射代码。

更好的方法可能是创建一个代表实体集合的专用类,并将与集合上的操作相关的所有方法都放在该类中。为此,可以使用 Streamable。如前所述,Spring JPA Repository 方法可以返回 Streamable

创建一个 Users 类来表示 User 对象的集合,继承 Streamable 并为其提供方便的方法:

public class Users implements Streamable<User> {

    private final Streamable<User> userStreamable;

    public Users(Streamable<User> userStreamable) {
        this.userStreamable = userStreamable;
    }

    @Override
    public Iterator<User> iterator() {
        return userStreamable.iterator();
    }

    // 自定义方法
}

为了让它与 JPA 配合使用,应该遵循一个简单的约定。首先,应该实现 Streamable,其次,提供 Spring 能够初始化 Streamable 的方法。初始化部分可以通过使用 Streamablepublic 构造函数或名称为 of(Streamable<T>)valueOf(Streamable<T>) 的静态工厂来实现。

然后,就可以使用 Users 作为 JPA Repository 方法的返回类型:

@Query("select u from User u")
Users findAllUsers();

现在,可以将 Repository 中保留的方法直接放在 Users 类中:

public Map<Long, User> getUserIdToUserMap() {
    return stream().collect(Collectors.toMap(User::getId, Function.identity()));
}

还可以使用与 User 实体的处理或映射相关的所有方法。比方说,想根据某些条件筛选出用户:

@Test
void fetchUsersInMapUsingStreamableWrapperWithFilterThenAllOfThemPresent() {
    Users users = repository.findAllUsers();
    int maxNameLength = 4;
    List<User> actual = users.getAllUsersWithShortNames(maxNameLength);
    User[] expected = {
        new User(9L, "Moe", "Oddy"),
        new User(25L, "Lane", "Endricci"),
        new User(26L, "Doro", "Kinforth"),
        new User(34L, "Otho", "Rowan"),
        new User(39L, "Mel", "Moffet")
    };
    assertThat(actual).containsExactly(expected);
}

此外,还可以对它们进行分组:

@Test
void fetchUsersInMapUsingStreamableWrapperAndGroupingThenAllOfThemPresent() {
    Users users = repository.findAllUsers();
    Map<Character, List<User>> alphabeticalGrouping = users.groupUsersAlphabetically();
    List<User> actual = alphabeticalGrouping.get('A');
    User[] expected = {
        new User(2L, "Auroora", "Oats"),
        new User(4L, "Alika", "Capin"),
        new User(20L, "Artus", "Rickards"),
        new User(27L, "Antonina", "Vivian")};
    assertThat(actual).containsExactly(expected);
}

通过这种方式,可以隐藏这些方法的实现细节,减少服务层的混乱,并减轻 Repository 的负担。

4、总结 {#4总结}

本文介绍了在 Spring Data JPA Repository 接口的方法中返回 Map 的几种方式。


Ref:https://www.baeldung.com/spring-data-return-map-instead-of-list

赞(2)
未经允许不得转载:工具盒子 » Spring Data JPA Repository 返回 Map