【问题标题】:Spring Data: JPA repository return Map instead of List?Spring Data:JPA 存储库返回 Map 而不是 List?
【发布时间】:2021-04-19 20:18:29
【问题描述】:

我有这样的查询,我想从哪里提取电影标题及其评分

   @Query(value ="SELECT m.title, avg(r.rating) " +
        "FROM movies m " +
        "INNER JOIN " +
        "ratings r " +
        "ON m.movieid = r.movieid " +
        "WHERE m.genres LIKE %:genre% " +
        "GROUP BY m.title " +
        "ORDER BY avg(r.rating) DESC " +
        "LIMIT 10",
        nativeQuery = true)
 List<Movies> topTenMoviesByGenreLikeIgnoreCase(@Param("genre") String genre);

但是这个列表显然不能存储2个值的resultSet

在了解它之后,我发现例如此链接Spring Data: JPA repository findAll() to return *Map instead of List? 但我似乎不清楚它们的实现。

我不明白他们从第一个链接中从哪里获得 Long 值

default Map<Long, TransactionModel> findByClientIdMap(Long id) {
    return findByClientId(id).stream().collect(Collectors.toMap(TransactionModel::getId, v -> v));
}

如何创建我的地图?

default Map<Movies, Float> topTenMoviesByGenreLikeIgnoreCaseMap(@Param("genre") String genre) {
    return topTenMoviesByGenreLikeIgnoreCase(genre).stream().collect(Collectors.toMap());
}

电影类:

@Entity
public class Movies {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer movieid;

private String title;
private String genres;

@OneToMany(mappedBy = "movieid")
private Set<Ratings> rates = new HashSet<>();

public Movies() {
}

public Movies(String title, String genres) {
    this.title = title;
    this.genres = genres;
}

/*Getters and Setters for variables*/

public Set<Ratings> getRates() {
    return rates;
}

public void setRates(Set<Ratings> rates) {
    this.rates = rates;
}
}

评分类:

@Entity
public class Ratings {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer rateid;

@ManyToOne
@JoinColumn(name = "movieid")
private Movies movieid;

@ManyToOne
@JoinColumn(name = "userid")
private Usrs userid;

private float rating;
private Integer timestamp;

public Ratings() {
}

public Ratings(Movies movieid, Usrs userid, float rating, Integer timestamp)
{
    this.movieid = movieid;
    this.userid = userid;
    this.rating = rating;
    this.timestamp = timestamp;
}
/*Getters and Setters for variables*/
}

【问题讨论】:

    标签: sql spring spring-boot jpa spring-data-jpa


    【解决方案1】:

    你可以这样使用Object[]作为查询结果

    @Query(value ="SELECT m.title, avg(r.rating) " +
            "FROM movies m " +
            "INNER JOIN " +
            "ratings r " +
            "ON m.movieid = r.movieid " +
            "WHERE m.genres LIKE %:genre% " +
            "GROUP BY m.title " +
            "ORDER BY avg(r.rating) DESC " +
            "LIMIT 10",
            nativeQuery = true)
     List<Object[]> topTenMoviesByGenreLikeIgnoreCase(@Param("genre") String genre);
    

    那么

    default Map<String, Float> topTenMoviesByGenreLikeIgnoreCaseMap(@Param("genre") String genre) {
        return topTenMoviesByGenreLikeIgnoreCase(genre)
                .stream()
                .collect(Collectors.toMap((arr->arr[0].toString()), (arr->Float.valueOf(arr[1].toString()))));
    }
    

    如您所见,topTenMoviesByGenreLikeIgnoreCaseMap 返回 Map,其中电影标题是关键。要使 Movie 实体成为键,您应该使用 JPQL 查询而不是本机查询。

    【讨论】:

    • 感谢您的建议。我为您提供有关查询的注释。写的真是莫名其妙。我已将 Ratings 类添加到问题中。您介意解释一下JPQL 查询是什么以及在我的情况下如何使用它吗?当我使用您的解决方案时,我得到Required type: Map&lt;String,Float&gt; Provided:Map&lt;Object,Object&gt;。是不是应该先改成Map&lt;Object, Object&gt;再转换成String, Float
    • 没错。这是我的错。你必须转换类型
    • JPQL 用于对实体而不是数据库表进行查询。 ORM可以根据JPQL查询生成SQL查询
    • 令我惊讶的是,如果我像 Collectors.toMap((arr-&gt; (String)arr[0]), (arr-&gt; (Float)arr[1]))) 那样投射它,我看到 将 'arr[0]' 投射到 'String' 是多余的投射 'arr[ 1]' 到 'Float' 是多余的。稍后我会尝试您的解决方案,如果可行,我会回信并接受您的回答
    • 我已经编辑了你的答案。它与这种铸造完美配合。感谢您的帮助并注意到奇怪的查询方面
    【解决方案2】:

    试试这个:

    @Query(value ="SELECT m.title as title, r.avg(rating) as rates " +
        "FROM movies m " +
        "INNER JOIN " +
        "ratings r " +
        "ON m.movieid = r.movieid " +
        "WHERE m.genre = LIKE %:genre% " +
        "GROUP BY r.avg(rating) " +
        "ORDER BY r.avg(rating) DESC " +
        "LIMIT 10",
        nativeQuery = true)
    
    List<Movies> topTenMoviesByGenreLikeIgnoreCase(@Param("genre") String genre);    
    
    
    
    default Map<Movies, Set<Ratings>> topTenMoviesByGenreLikeIgnoreCaseMap(@Param("genre") String genre) {
        return topTenMoviesByGenreLikeIgnoreCase(genre).stream().collect(
                                                                Collectors.toMap(
                                                                tuple -> (tuple.getTitle(),
                                                                tuple -> (tuple.getRates())
                                                             ));
    }
    

    【讨论】:

    • 我很赞同你的建议,但似乎有一些小错误。奇怪的点,如collect(.Collectors.toMap(.tuple。顺便说一句,tuple 是什么,你从哪里得到的?此外,在我的情况下,元组没有方法 get()
    • tuple 定义为 SELECT 查询生成的表中的一行。
    • 很抱歉,tuple 仍然没有 get() 方法。我想知道为什么。此外,如果应用toString()floatValue(),为什么要将所有内容都转换为String?我想将 Float 存储为一个值,我不需要将其转换为 String
    • 如果我们将其稍微更改为tuple -&gt; (tuple.getTitle()) mb 就可以了,我们不需要强制转换任何内容,但是tuple -&gt; (tuple.get("ratings")).floatValue() 呢?由于输出是 Movies 类,我无法获得电影的评分,因为它存储在其他类中
    • 糟糕,我也错过了。
    猜你喜欢
    • 2017-06-12
    • 2022-07-11
    • 2019-12-20
    • 2016-05-04
    • 1970-01-01
    • 2015-09-09
    • 2013-10-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多